Этот шаблон, который является порождающим шаблоном проектирования, предлагает интерфейс или абстракцию для создания типа объекта в суперклассе, но позволяет подклассу изменять тип объекта, который будет создан.
Предлагается, чтобы разработчики заменили создание объектов с помощью
оператора constructor
или с помощью оператора new
. Это звучит странно, но объект будет создан с использованием только constructor
, который вызывается внутри фабричного метода.
На первый взгляд это может показаться сложным, но поверьте мне, это способствует ослаблению связи и повторному использованию кода.
Давайте разберемся на примере
Постановка задачи
Представьте, что вы хотите добавить различное поведение ведения журнала для разных сред приложений, например Production
, Development
, Test
и Debug
.
- В
Production
: отображается толькоloglevel=error|warn
. - В
Development
: отображаются всеloglevel=info|error|warn|debug
. - В : должно отображаться только
loglevel=debug
.
Для простоты давайте работать только с двумя средами, т. е.
Production
иDevelopment
.
Реализация без использования шаблона
Реализация Logger
включает в себя так много if-else
или ' if
(то же самое можно выполнить с помощью switch
).
Представьте себе будущее
- Добавлено какое-то новое окружение, скажем,
Cloud
. Так что это приносит с собой новые проблемы и условия. - Заставить разработчика изменить реализацию класса
Logger
. - Обновление условия в соответствии с ним
- Изменены бизнес-правила для логгера
Таким образом, эту реализацию невозможно обслуживать, и она подвержена нежелательному поведению.
Реализация фабричного метода
Разработчик создает абстракцию или интерфейс с именем Logger
и предоставляет конкретную реализацию для различных подклассов общего суперкласса Logger
для различных сред, то есть ProductionLogger
, DevelopmentLogger
, TestingLogger
и DebugLogger
.
Таким образом, каждый метод подкласса Logger
должен содержать свою реализацию и бизнес-правила.
Я знаю, что это трудно понять, потому что добавляет сложности.
Для тех, кто не понимает диаграмму UML, на ней показано отношение IS-A между абстрактным классом Logger
и подклассами (DevelopmentLogger
, TestingLogger
и т. д.), так что DevelopmentLogger
является Logger
.
Разработчик создает файл interface
с именем ILoggerBuilder
. Это отвечает за предоставление объекта правильного типа Logger
в соответствии с требованием или некоторой логикой. Диаграмма UML показывает отношение IS-A между абстрактным классом Logger
и подклассами (DevelopmentLogger
, TestingLogger
и т. д.), так что DevelopmentLogger
является Logger
.
Разработчик создает interface
с именем ILoggerFactory
. Это отвечает за предоставление объекта правильного типа Logger
в соответствии с требованием или некоторой логикой.
EnvironmentLoggerFactory
реализует ILoggerFactory
, который отвечает за создание соответствующего объекта подкласса Logger
в соответствии со средой, в которой выполняется приложение, или, скажем, существует сложная логика для создания некоторого объекта;
Давайте напишем псевдокод
Теперь разработчик добавляет EnvironmentLoggerFactory
, который реализует ILoggerFactory
Реализация регистратора
Снова представьте будущее
- Добавляется какая-то новая среда, скажем,
Cloud
. - РЕШЕНИЕ. Теперь разработчик должен разработать новый подкласс
CloudLogger
, наследующийLogger
. - Правила можно внедрять, не думая, что они сломаются в среде разработки или производства.
- Изменены бизнес-правила для логгера, например, все логи в облаке должны храниться в базе данных
Применимость
- Когда разработчики не знают точного типа объектов, с которыми должен работать код
- Фабричный метод отделяет создание объекта от кода, который его использует
- Это обеспечивает простоту расширения конструкции объекта
- Когда разработчик хочет сэкономить ресурсы, повторно используя существующие объекты, а не перестраивая их
Общая реализация шаблона
- Все продукты должны быть унаследованы или реализованы
Product
- Добавьте фабричный метод внутри
CreatorFactory
, который всегда возвращает объект базового типаProduct
. - Замените все вызовы конструктора в коде фабричным методом.
Разработчик может передать аргумент внутри этого фабричного метода, чтобы управлять поведением при создании объекта
Преимущества
- Избегайте тесной связи
- Основной принцип с единой ответственностью, поэтому весь код создания объектов разделен, что упрощает управление кодом.
- Open/Close Principal, разработчик может представить новый продукт в соответствии с изменениями, не нарушая код клиента.
Заключение
Фабричный метод — это мощный способ создания сложных объектов; он продвигает слабую связанность, принцип единой ответственности и принцип открытия/закрытия.
Следуйте за мной на GitHub
Чтобы узнать больше об этом паттерне, посмотрите это видео Кристофера Охрави.