Паттерны проектирования
12. Паттерн «Фабричный метод» (Factory Method)
Когда при обсуждении дизайна упоминается фабрика, то в подавляющем большинстве случаев имеется в виду одна из разновидностей паттерна «Фабричный метод».
Назначение: определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс создавать. Фабричный метод позволяет классу делегировать создание объекта подклассам.
Причины использования:
Иерархии классов обеспечивают гибкость за счет полиморфного использования, но привносят дополнительную сложность. Абстрактная фабрика решает задачу полиморфного создания семейства объектов, но очень часто возникает более простая задача — создание одного экземпляра иерархии наследования.
На практике встречаются три вида паттерна «Фабричный метод», которые приведены на рисунке ниже:
-
Creator — объявляет абстрактный или виртуальный метод создания продукта, использует фабричный метод в своей реализации;-
ConcreteCreator — реализует фабричный метод, который возвращает ConcreteProduct;-
Product — определяет интерфейс продуктов, создаваемых фабричным методом;-
ConcreteProduct — определяет конкретный вид продуктов.1. Классический фабричный метод является частным случаем паттерна «Шаблонный метод», переменный шаг которого отвечает за создание нужного типа объекта. Это значит, что фабричный метод привязан к текущей иерархии типов и не может быть использован повторно в другом контексте.
2. Статический фабричный метод является самой простой версией. Основное отличие его от классического в том, что тип создаваемого объекта определяется не типом наследника, а аргументами, переданными методу. Типичная реализация такого метода содержит оператор
switch, в котором происходит выбор создаваемого типа. Вместо этого можно воспользоваться словарём, ключом которого будет некоторый идентификатор (строка, перечисление и т. п.), а значением — делегат, который будет создавать объект нужного класса. Статический метод позволяет обойти ограничения конструкторов. Например, тип создаваемого объекта может зависеть от аргументов метода, экземпляр может возвращаться из кэша, а не создаваться заново или же фабричный метод может быть асинхронным.3. Полиморфный фабричный метод определяет интерфейс фабрики, а за создание конкретного экземпляра продукта отвечает конкретная фабрика. Является стратегией создания экземпляров некоторого семейства типов, что позволяет использовать одну фабрику в разных контекстах. Тип создаваемого объекта определяется типом фабрики и обычно не зависит от аргументов фабричного метода.
Конструктор vs. фабричный метод
В большинстве случаев конструкторы прекрасно справляются со своей задачей. Но есть две причины использовать фабричный метод вместо конструктора:
1. Именованные конструкторы. В языке C# невозможно использовать два конструктора с одним набором и типом параметров. Хорошим примером такого ограничения является структура
Timespan. Очень удобно создавать интервал времени по количеству секунд, минут, часов и дней, но сделать несколько конструкторов, каждый из которых принимает один параметр типа double, невозможно. Для этого структура Timespan содержит набор фабричных методов.public struct Timespan {
public Timespan(double ticks) {…}
public static Timespan FromMilliseconds(double value) {…}
public static Timespan FromSeconds(double value) {…}
public static Timespan FromMinutes(double value) {…}
…
}
2. Тяжеловесный процесс создания. Обычно логика конструктора относительно простая, но слишком тяжеловесную логику лучше вынести в статический фабричный метод. Если для конструирования объекта требуется обращение к внешним ресурсам, то лучше сразу же выделять эту логику в фабричный метод. Это позволяет сделать фабричный метод асинхронным, а также упростить эволюцию решения и разбиение данного класса на более мелкие составляющие в случае необходимости.Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 10.