Открыть меню

Разбор паттерна «фабричный метод»

Назначение из «Банды Четырех»:

Определяет интерфейс для создания объекта, но оставляет подклассам ­решение о том, экземпляры какого класса должны создаваться. Фаб­ричный метод позволяет классу делегировать создание экземпляров подклассам.

График из «Банды Четырех»:

Паттерн «порождающего» типа. Технически так и есть, но когда пытаешься понять реальное использование такого шаблона, то всё больше кажется, что задача этого шаблона — разделение ответственности класса, а не создание объектов. Особенно если сравнить с какой-нибудь «фабрикой». Но об этом дальше…

Структура кода при использовании шаблона

Из определения имеем следующие обязательные составляющие паттерна:

  1. Родительский класс-создатель с абстрактным фабричным методом, либо интерфейс с ним (FactoryMethod на скрине).
  2. Фабричный метод определяет интерфейс возвращаемого объекта.
  3. Подклассы, которые реализуют выше описанный метод (ConcreteCreator). Знают, какие конкретно объекты надо вернуть и возвращают их.
  4. Классы продуктов, которые будут возвращаться из подклассов из метода FactoryMethod.

Фактически мы описали структуру кода при использовании паттерна. Хотя сам фабричный метод — это всего лишь один FactoryMethod().

Последствия: вторая иерархия классов (под каждый продукт отдельный создатель).

Противоречие из примеров

Обратим внимание на метод AnOperation(). В нескольких источниках (refactoring.guru, та же банда четырех) пишут, что Creator теперь может использовать объект с единым интерфейсом Product и ему не нужно иметь реализацию поведения под каждый экземпляр. Так-то оно так, но мне первое время это казалось странным.

Во-первых, какого хрена класс Creator кроме создания ещё хранит какую-то бизнес-логику с объектами? Это не очевидно по названию и странно.

Во-вторых, очевидно, что работа с одним интерфейсом лучше, чем реализация под каждый класс продукта, но это ведь скорее результат применения этого шаблона, а не его часть? То есть, убери мы со схем AnOperation() метод, это по определению не перестало бы быть «фабричным методом».

Если бы мы пусть и некрасиво, но сделали бы так:

Использование

Покрутив в голове реальные проекты, в разработке которых я участвовал, я не смог придумать, как применить паттерн в таком виде. Потому что важная особенность паттерна — иерархия классов создателей. Насколько создание объектов продуктов должно отличаться, чтоб мы для этого дела захотели аж под каждый продукт сделать своего Создателя?

А потом я понял, что суть паттерна в другом. Дочерние классы — это не фабрики и не должны ими быть. Поэтому и пример с классом Creator мне не зашел.

Вот ещё подсказочка к верному использованию(всё из той же книги):

Потенциальный недостаток фабричного метода состоит в том, что клиентам,
возможно, придется создавать подкласс класса Creator для создания лишь
одного объекта ConcreteProduct . Порождение подклассов оправданно, если
клиенту так или иначе приходится создавать подклассы Creator , в противном
случае клиенту придется иметь дело с дополнительным уровнем подклассов.

Вот как я понял полезное использование этого паттерна:

Есть, например, иерархия классов. Скажем, Bank. Мы не создавали её специально под паттерн, она просто уже была. И у каждого банка есть свой адаптер для взаимодействия с api банка.

И в такой ситуации можно добавить фабричный метод getBankAdapter():AbstractAdapter, чтобы каждый банк сам назначил свой адаптер.
И да, в абстрактном банке будет метод с общий механикой, использующий getBankAdapter() дочерних классов. Например, sendLead(). Вот тут я и понял, почему выше были примеры с методом anOpearion().
То есть, суть именно в отдельном методе для класса, а не в добавлении классов Creator для каждого продукта. Есть класс, который занимается совсем другим функционалом, а мы лишь добавляем ему небольшое делегирование.

Схема примера:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

© 2022 Продвижение сайтов в Санкт-Петербурге · Копирование материалов сайта без разрешения запрещено