Фабричный метод - определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
-
Когда заранее неизвестны типы и зависимости объектов, с которыми должен работать ваш код.
-
Фабричный метод отделяет код производства продуктов от остального кода, который эти продукты использует.
Благодаря этому, код производства можно расширять, не трогая основной код. Так, чтобы добавить поддержку нового продукта, вам нужно создать новый подкласс и определить в нём фабричный метод, возвращая оттуда экземпляр нового продукта.
-
-
Когда вы хотите дать возможность пользователям расширять части вашего фреймворка или библиотеки.
-
Пользователи могут расширять классы вашего фреймворка через наследование. Но как сделать так, чтобы фреймворк создавал объекты из этих новых классов, а не из стандартных?
Решением будет дать пользователям возможность расширять не только желаемые компоненты, но и классы, которые создают эти компоненты. А для этого создающие классы должны иметь конкретные создающие методы, которые можно определить.
Например, вы используете готовый UI фреймворк для своего приложения. Но вот беда, требуется иметь круглые кнопки, вместо стандартных прямоугольных. Вы создаёте класс RoundButton. Но как сказать главному классу фреймворка (UIFramework), чтобы он теперь создавал круглые кнопки, вместо стандартных.
Для этого вы создаёте подкласс UIWithRoundButtons из базового класса фреймворка, переопределяете в нём метод создания кнопки (createButton) и вписываете туда создание своего класса кнопок. Затем, используете UIWithRoundButtons вместо стандартного UIFramework.
-
-
Когда вы хотите экономить системные ресурсы, повторно используя уже созданные объекты, вместо создания новых.
- Такая проблема обычно возникает при работе с тяжёлыми ресурсоёмкими объектами, такими как подключение к базе данных, файловой системе и т.д.
Представьте, сколько действий вам нужно совершить, чтобы повторно использовать существующие объекты:
- Сперва вам следует создать общее хранилище, чтобы хранить вы нём все создаваемые объекты.
- При запросе нового объекта, нужно будет заглянуть в хранилище и проверить, есть ли там неиспользуемый объект.
- А затем вернуть его клиентскому коду.
- Но если свободных объектов нет - создать новый, не забыв добавить его в хранилище.
Весь этот код нужно куда-то поместить, чтобы не засорять клиентский код.
-
Приведите все создаваемые продукты к общему интерфейсу.
-
В классе, который производит продукты, создайте пустой фабричный метод. В качестве возвращаемого типа укажите общий интерфейс продукта.
-
Затем, пройдитесь по коду класса и найдите все участки, создающие продукты. Поочерёдно замените эти участки вызовами фабричного метода, перенося в него код создания различных продуктов.
В фабричный метод, возможно, придётся добавить несколько параметров, контролирующих какой из продуктов нужно создать.
-
Для каждого типа продуктов заведите подкласс и переопределите в нём фабричный метод. Переместите туда код создания соответствующего продукта из суперкласса.
-
Если создаваемых продуктов слишком много для существующих подклассов создателя, вы можете подумать о введении параметров в фабричный метод, которые позволят возвращать различные продукты в пределах одного подкласса. (объединить по смыслу продукты в 1 фабрику)
Например, у вас есть класс Почта с подклассами АвиаПочта и НаземнаяПочта, а также классы продуктов Самолёт, Грузовик и Поезд. Авиа соответствует Самолётам, но для НаземнойПочты есть сразу два продукта. Вы могли бы создать новый подкласс почты для поездов, но проблему можно решить и по-другому. Клиентский код может передавать в фабричный метод НаземнойПочты аргумент, контролирующий какой из продуктов будет создан.
- Если после всех перемещений фабричный метод стал пустым, можете сделать его абстрактным. Если в нём что-то осталось - это будет его реализацией по умолчанию.
+ | - |
---|---|
Избавляет класс от привязки к конкретным классам продуктов. | Может привести к созданию больших параллельных иерархий классов, так как для каждого класса продукта надо создать свой подкласс создателя. |
Выделяет код производства продуктов в одно место, упрощая поддержку кода. | |
Упрощает добавление новых продуктов в программу. | |
Реализует принцип открытости/закрытости. |
-
Многие архитектуры начинаются с применения Фабричного метода (более простого и расширяемого через подклассы) и эволюционируют в сторону Абстрактной фабрики, Прототипа или Строителя (более гибких, но и более сложных).
-
Классы Абстрактной фабрики чаще всего реализуются с помощью Фабричного метода, хотя они могут быть построены и на основе Прототипа.
-
Фабричный метод можно использовать вместе с Итератором, чтобы подклассы коллекций могли создавать подходящие им итераторы.
-
Прототип не опирается на наследование, но ему нужна сложная операция инициализации. Фабричный метод наоборот, построен на наследовании, но не требует сложной инициализации.
-
Фабричный метод можно рассматривать как частный случай Шаблонного метода. Кроме того, Фабричный метод нередко бывает частью большого класса с Шаблонными методами.