Фабричных методов на самом деле много.
Даже базовые конструкции if else являются фабричным методом, так как и switch.
Но ведь это не удобно читать и расширять.
Представим простой пример. У нас есть игра slasher с монстрами. У нас есть несколько режимов: easy, medium, hard. Следовательно пусть у нас будут отличаться у монстров жизни и урон. Как организовать удобное создание монстров и в одном месте? Все в подcut`e.
1. Создадим примерное описание монстра в виде Description.
[Serializable]
public class MobDescription
{
public float MaxHealth;
public float Damage;
}
2. Заполним наших монстров по уровням
Способов заполнить такие Descriptions на самом деле много, будь то json, xml или же просто ScriptableObject. Для примера воспользуемся ScriptableObject и создадим класс списков описаний монстров.
[CreateAssetMenu(fileName = "MobDescriptions", menuName = "MobDescriptions", order = 51)]
public class MobDescriptions : ScriptableObject
{
[SerializeField] private List<MobDescription> _listOgre;
[SerializeField] private List<MobDescription> _listTroll;
public List<MobDescription> ListOgre => _listOgre;
public List<MobDescription> ListTroll => _listTroll;
}
И получим вот это.
Так как мы делаем 3 уровня сложности, то давайте заполним
Хорошо у нас теперь есть все описания по уровням сложности для наших монстров.
3. Создадим Model для mvc монстра.
Это лишь шаблон использования расширения модели, для того чтобы не хранить данные о монстре прямо в модели.
public class MobModel
{
private MobDescription _description;
private float _currentHealth;
public MobDescription Description => _description;
public MobModel(MobDescription description){
_description = description;
_currentHealth = _description.MaxHealth;
}
// Ваша логика монстра
}
4. Теперь создадим класс фабрики, которая будет создавать монстров в зависимости от уровня сложности. Такую фабрику легко расширить, а так же добавлять другие фабрики для генерацию дропа или спавна объектов.
public class Factory
{
private Dictionary<string, Func<int, MobModel>> mobFactory;
public void Init(MobDescriptions descriptions)
{
mobFactory = new Dictionary<string, Func<int, MobModel>>()
{
{"ogre", (level) => new MobModel(descriptions.ListOgre[level])},
{"troll", (level) => new MobModel(descriptions.ListTroll[level])}
};
}
public MobModel CreateMobModel(string nameMob, int level)
{
return mobFactory[nameMob](level);
}
}
5. Ну и конечно же пример вызова создания монстра.
Пока нет никакой логики используем хардкод. В итоговом варианты это может быть доп описание уровня, в котором будут параметры для спавна монстров с нормальными условиями. Например когда игрок прошел конкретную точку, спавнятся N enemy X уровня.
public class GameManager : MonoBehaviour
{
[SerializeField] private MobDescriptions _mobDescriptions;
private Factory _factory;
private void Start()
{
_factory = new Factory();
_factory.Init(_mobDescriptions);
_factory.CreateMobModel("ogre", 2);
}
}
lair
Вы не могли бы развернуть это утверждение?
vildafaust Автор
тоже самое на swich
Не стоит забывать, что pattern лишь нечто каноническое, они лишь помогают выполнять работу объектов наиболее удобным и оптимальным способом, но и способов этих много. Это просто порождение других классов через нужные условия, а метод может быть реализован хоть абстрактной стандартной фабрикой.
lair
Ну давайте, что ли, на примерах.
Является ли вот такой код фабричным методом?
А вот такой?
aikixd
Фабрика, в любых ее проявлениях, позволяет отложить указание типа создаваемого объекта до времени исполнения. Наличие или отсутствие логического ветвления в методе ни о чем не говорит.
Dark_Daiver
Наверное, все же, указание реализации.
Ну и про время исполнения тоже сложно — наверняка на шаблонах тоже можно запилить фабрику.
aikixd
Я несколько раз начинал и стирал свой ответ пока не понял почему слово "реализация" в этом контексте мне не нравится.
В большинстве (наверное во всех, но я всех не знаю) ООП языках конструкторы не поддерживают полиморфизм. Соответсвенно фабрики используются для обхода этого ограничения. Полиморфный метод статически связывается с конкретным конструктором. Конструктор это атрибут типов, а не реализаций.
Есть и другие способы выбора реализации (даже если относится к термину очень вольно), например использовать делегаты вместо методов или даже просто через ветвление.
Поэтому мне кажется что слово "тип" тут больше подходит.