Снова паттерны.
В прошлый раз мы рассмотрели, как с помощью фабричного метода можно вывести логику создания объекта в отдельный класс. Однако, в конце того поста всплыл вопрос - а что если нам надо создавать объекты разных семейств, объединяя их как-то? Если использовать одну и ту же фабрику, то система теряет гибкость, т.к. для добавления нового семейства необходимо переписывать уже существующий класс фабрики.Итак, возьмём пример из предыдущего поста - эльфийское воинство, состоящее из магов, воинов, лучников. Для эффективного создания этих юнитов мы используем "фабрику эльфов" с тремя фабричными методами для каждого типа юнита соответственно. Теперь мы хотим создать подобный механизм для, например, орков, но при этом создать унифицированный интерфейс доступа к ним для того, чтобы впоследствии иметь возможность добавления ещё игровых рас: людей, дроу, нежити и т.д.
Для начала вспомним про базовые классы (которые будут абстрактными):
Базовые классы
Базовый юнит////// Базовый класс для абстрагирования юнитов /// public abstract class UnitBase { ////// Базовая атака /// ///Содержание атаки public abstract String Attack(); }
Базовый маг
////// Базовый класс для мага /// public abstract class MageBase:UnitBase { ////// Атака мага /// ///Содержание атаки public override abstract String Attack(); }
Базовый лучник
////// Базовый класс для лучника /// public abstract class ArcherBase : UnitBase { ////// Атака лучника /// ///Содержание атаки public override abstract String Attack(); }
Базовый пехотинец
////// Базовый класс для пехотинца /// public abstract class GruntBase : UnitBase { ////// Атака пехотинца /// ///Содержание атаки public override abstract String Attack(); }
Теперь реализуем функционал для эльфов - создадим фабрику эльфов. Прошлый пример мы немного модернизируем. В прошлый раз у нас было три метода - по одному для каждого вида юнитов. Однако, если нам понадобится четвёртый - мы должны будем переписывать в том числе и запускающий модуль. Ведь, он должен будет как-то "узнать", что у эльфийской фабрики появился например метод создания летающих существ.
Для того, чтобы избежать этого, мы просто заменим три разных метода на три метода с одинаковым названием, но разными параметрами, т.е. используем полиморфизм. Чтобы разделить эти методы - определим для каждого из них параметр того типа, экземпляр которого будет возвращать метод. Например, для метода, возвращающего эльфийского лучника, мы будем передавать параметр типа "Базовый лучник". Т.е. вызов этого метода будет равносилен запросу: "Фабрика эльфов, дай мне лучника".
Эльфийские воины:
Эльфийский лучник:////// Эльфийский лучник /// public class ElfArcher : ArcherBase { ////// Атака эльфийского лучника /// ///Содержание атаки public override String Attack() { return "Эльфийский лучник отстрелялся!"; } }
Эльфийский воин:
////// Эльфийский пехотинец /// public class ElfGrunt : GruntBase { ////// Атака эльфийского пехотинца /// ///Содержание атаки эльфийского пехотинца public override String Attack() { return "Эльфийский пехотинец стукнул"; } }
Эльфийский маг:
////// Эльфийский маг /// public class ElfMage :MageBase { ////// Атака эльфийского мага /// ///Содержание атаки эльфийского мага public override String Attack() { return "Эльфийский маг колданул!"; } }
Эльфийская фабрика:
////// Эльфийская фабрика /// public class ElvenFactory { ////// Создание эльфийского лучника /// /// Маркер лучника ///Эльфийский лучник public UnitBase CreateUnit(ArcherBase unit) { return new ElfArcher(); } ////// Создание эльфийского мага /// /// Маркер мага ///Эльфийский маг public UnitBase CreateUnit(MageBase unit) { return new ElfMage(); } ////// Создание эльфийского пехотинца /// /// Маркер пехотинца ///Эльфийский пехотинец public UnitBase CreateUnit(GruntBase unit) { return new ElfGrunt(); } }
Основной модуль:
static void Main(string[] args)
{
//Общий список юнитов
List<UnitBase> units = new List<UnitBase>();
//Создаём две фабрики
ElvenFactory elfs = new ElvenFactory();
//Создаём три переменных базовых типов
//Они нужны лишь как маркеры для вызова определённых методов фабрик
MageBase mage=null;
ArcherBase archer = null;
GruntBase grunt = null;
//Добавляем некоторое количество юнитов
units.Add(elfs.CreateUnit(archer));
units.Add(elfs.CreateUnit(grunt));
//Заставляем юниты атаковать
foreach (UnitBase u in units)
{
Console.WriteLine(u.Attack());
}
//Тормозилка
Console.ReadLine();
}
Если мы бы вдруг захотели добавить новый тип юнитов (например, летучки или всадники), то нам достаточно было бы создать наследника от Эльфийской фабрики, добавить туда этот метод и добавить соответствующий базовый класс. После этого можно было бы спокойно использовать этот класс из основного модуля.
Ваше Величество, орки!
Но, перед нами встала задача добавить расу орков в нашу гипотетическую игру. При этом, интерфейсы создания юнитов для орков и эльфов надо унифицировать. Для этого мы делаем Эльфийскую фабрику наследником абстрактного класса Фабрика воинов, которая содержит все соответствующие методы. И от этой же абстрактной Фабрики воинов наследуем Фабрику орков по аналогии с Фабрикой эльфов.Абстрактная фабрика:
///Изменённая эльфийская фабрика:/// Абстрактная фабрика воинов /// public abstract class WarriorsFactory { ////// Создание лучника /// /// Маркер лучника ///Абстрактный лучник public abstract UnitBase CreateUnit(ArcherBase unit); ////// Создание мага /// /// Маркер мага ///Абстрактный лучник public abstract UnitBase CreateUnit(MageBase unit); ////// Создание пехотинца /// /// Маркер пехотинца ///Абстрактный пехотинец public abstract UnitBase CreateUnit(GruntBase unit); }
////// Эльфийская фабрика /// public class ElvenFactory: WarriorsFactory { ////// Создание эльфийского лучника /// /// Маркер лучника ///Эльфийский лучник public override UnitBase CreateUnit(ArcherBase unit) { return new ElfArcher(); } ////// Создание эльфийского мага /// /// Маркер мага ///Эльфийский маг public override UnitBase CreateUnit(MageBase unit) { return new ElfMage(); } ////// Создание эльфийского пехотинца /// /// Маркер пехотинца ///Эльфийский пехотинец public override UnitBase CreateUnit(GruntBase unit) { return new ElfGrunt(); } }
Орк-лучник:
////// Орочий лучник /// public class OrcArcher : ArcherBase { ////// Атака орочьего лучника /// ///Содержание атаки public override String Attack() { return "Орочий лучник отстрелялся!"; } }
Орк-воин:
////// Орочий пехотинец /// public class OrcGrunt : GruntBase { ////// Атака орочьего пехотинца /// ///Содержание атаки public override String Attack() { return "Орочий пехотинец cтукнул!"; } }
Орк-маг:
////// Орочий маг /// public class OrcMage : MageBase { ////// Атака орочьего мага /// ///Содержание атаки public override string Attack() { return "Орочий маг колданул!"; } }
Орочья фабрика:
////// Фабрика, создающая орков /// public class OrcFactory : WarriorsFactory { ////// Создание орочьего лучника /// /// Маркер лучника ///Орочий лучник public override UnitBase CreateUnit(ArcherBase unit) { return new OrcArcher(); } ////// Создание орочьего мага /// /// Маркер мага ///Орочий маг public override UnitBase CreateUnit(MageBase unit) { return new OrcMage(); } ////// Орочий пехотинец /// /// Маркер пехотинца ///Орочий пехотинец public override UnitBase CreateUnit(GruntBase unit) { return new OrcGrunt(); } }
И изменяем основной модуль, т.е. добавляем туда орочью фабрику и прописываем создание объектов-орков:
static void Main(string[] args)
{
//Общий список юнитов
List<UnitBase> units = new List<UnitBase>();
//Создаём две фабрики
ElvenFactory elfs = new ElvenFactory();
OrcFactory orcs = new OrcFactory();
//Создаём три переменных базовых типов
//Они нужны лишь как маркеры для вызова определённых методов фабрик
MageBase mage=null;
ArcherBase archer = null;
GruntBase grunt = null;
//Добавляем некоторое количество юнитов
units.Add(orcs.CreateUnit(mage));
units.Add(orcs.CreateUnit(archer));
units.Add(orcs.CreateUnit(mage));
units.Add(elfs.CreateUnit(archer));
units.Add(elfs.CreateUnit(grunt));
//Заставляем юниты атаковать
foreach (UnitBase u in units)
{
Console.WriteLine(u.Attack());
}
//Тормозилка
Console.ReadLine();
}
Комментариев нет:
Отправить комментарий