Где бы вы использовали шаблон Builder вместо абстрактной фабрики?

Я видел, как этот вопрос поднимался здесь и там несколько раз, но я так и не нашел и не ответил, что был доволен.

Из Википедии:

Строитель фокусируется на построении сложного объекта шаг за шагом. Абстрактная фабрика выделяет семейство объектов товара (простых или сложных). Builder возвращает продукт в качестве последнего шага, но что касается абстрактной фабрики, продукт возвращается немедленно.

Но для клиента это не одно и то же? Он получает полный объект, как только он построен, поэтому для него нет никакой дополнительной функциональности.

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


Следующий фрагмент из Википедии - хорошая ссылка, чтобы понять мою точку зрения:

Часто проекты начинаются с использования Factory Method (менее сложный, более настраиваемый, подклассы разрастаются) и переходят к Abstract Factory, Prototype или Builder (более гибким, более сложным), когда дизайнер обнаруживает, где необходима большая гибкость.

Если это так, какую сложность нужно было бы ввести в вашей системе, когда бы вы перешли от абстрактной фабрики к сборщику?

Суть в том, что я не могу найти и пример, в котором ясно, что абстрактной фабрики будет недостаточно, и вместо этого вам понадобится Builder.

10 ответов

Решение

AbstractFactory для семейства сопутствующих товаров. Строитель для одного продукта.

Построитель предназначен для поэтапного построения сложного продукта, а AbstractFactory - для абстрактного построения (то есть, для нескольких реализаций) менее сложного продукта.


Примеры того, что разница означает для вызывающего кода:

  • Для клиентского кода, использующего AbstractFactory, каждый вызов метода создает один объект. Клиент должен собрать их вместе. С другой стороны, сборщик собирает их изнутри и предоставляет полный граф объектов в качестве последнего шага построения.

  • Конструктор позволяет передавать и проверять параметры один за другим для клиентского кода. Но он может создать конечный продукт за один шаг (например, вызов конструктора, передавая сразу все параметры, даже сложные, которые были собраны сами). Таким образом, продукт компоновщика может быть неизменным объектом (или графом) (который является бесценным в многопоточной среде по соображениям производительности и памяти).


ОБНОВЛЕНО в ответ на этот комментарий:

Lino> Хотя это все еще беспокоит меня:). Я имею в виду, что если вы создаете составные объекты и вы берете директора, передавая ответственность за вызов методов построения клиентам, тогда это работает для меня. Только тогда застройщик показался бы идеальной подгонкой. Но с режиссером это не так уж много...

Точно, у Builder нет директора, он позволяет каждому клиенту создавать сложный продукт в соответствии с его конкретными потребностями. Клиент является директором, потому что реализация директора не используется повторно.

Если вам нужно несколько раз повторно использовать сложный процесс сборки, тогда у вас может быть метод для инкапсуляции этого (и этот метод является вашим "директором").

Напротив, для AbstractFactory обычно требуется многократно используемый директор, который создает продукты одинаково (с помощью реализации Factory).

Здесь уже есть несколько очень хороших ответов. Я просто предлагаю аналогию.

Скажем, вы хотели новый стол для вашего нового офиса. Вы идете на "фабрику" и видите выбор, затем выбираете одну из полок. Если это соответствует вашим потребностям, отлично!

Теперь у вас есть большой офис с действительно причудливыми стенами. Теперь вам нужен стол с верхней частью в форме, подходящей для вашего офиса. И вы хотите, чтобы другие ножки подходили к остальной части мебели, а также подходили под ваш новый стул Aeron.

Вы возвращаетесь на тот же завод, не повезло. Вы идете к плотнику, который может изготовить на заказ то, что вы хотите. Вы даете свои спецификации и требования, и "строитель" скажет вам определенные ограничения. После нескольких итераций вы получили идеальный стол!

Ладно, отстойная история, но только для того, чтобы осветить ситуацию

Один хороший пример, когда вам нужен конструктор, - это если вы строите что-то по частям на основе аргументов командной строки. Например, рассмотрим конструктор с такими методами, как...

setName()
setType()
setOption()

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

Иногда эстетика одного рисунка просто лучше, чем эстетика другого.

Builder - это инверсия управления, подобная шаблону Template Method. Различные функции вашего конкретного строителя являются настраиваемыми этапами процесса. Абстрактная фабрика - это "просто" полиморфизм, где на выходе получается вновь построенный объект. Различные функции на конкретной фабрике - это различные настраиваемые процессы, каждый из которых, по-видимому, приводит к отдельному объекту.

Итак, вам нужен строитель, когда вам нужен общий общий "сюжет" для построения ваших объектов, но отдельные "сцены" на этом графике отличаются. Вам нужна фабрика, когда между разными реализациями не обязательно есть что-то общее.

Я думаю, что вы могли бы использовать и то и другое одновременно: Абстрактная фабрика выступала в качестве директора в шаблоне Builder, а каждая конкретная фабрика направляла вещи по-своему. Тем не менее, это выходит за пределы "салфетки" для диаграмм UML;-)

Строитель полезен для конфигурации. Абстрактная фабрика полезна, когда вы не хотите, чтобы вызывающие абоненты заботились о фактической реализации. Две разные цели.

На самом деле вы могли бы смешать две модели. Например, Java DocumentBuilderFactory - это классическая абстрактная фабрика: если вы прочитаете документы, вы увидите процесс, который он использует для выбора реализации. Однако создаваемые им DocumentBuilder можно настроить позже (хотя они не следуют прототипному подходу "построителя", когда каждый метод конфигурации возвращает объект, который был сконфигурирован).

Давайте рассмотрим аналогию.

Вы находитесь в сети быстрого питания, где быстрое питание, которое вы на самом деле получаете, довольно сложное. Это не только сложно, но только определенные люди могут принять ваш заказ на определенные виды пищи. Теперь вы этого не знаете, потому что менеджер магазина решил оставить одного человека за одним кассовым аппаратом. Но если вы посмотрите на него внимательно, это действительно продвинутая голограмма, потому что она "абстрактная". Абстрактный заказчик. Теперь, когда вы говорите с голограммой и размещаете свой заказ, компьютер с голограммой перенаправляет (полиморфизм) этот заказ на имплантат в мозг человека, принимающего заказ. Имплант заставляет человека на самом деле ударить в порядке реального регистра. Есть несколько таких "конкретных" контролируемых разумом людей, которые упорядочены по голограмме. Теперь, когда настоящий человек наносит удары по порядку, он отправляется на задний компьютер, где есть сборочная линия, или "строитель", для каждого типа производимой еды. Заказы отправляются на различные сборочные линии в зависимости от человека и его кассового аппарата.

Таким образом, голограмма - это абстрактная фабрика, контролируемые разумом люди в регистрах - это конкретные фабрики, а сборочные линии - строители. Таким способом вы можете визуализировать поток абстрактной фабрики, работающей с шаблоном компоновщика. Иногда лучше посмотреть, как шаблоны проектирования работают вместе, чтобы увидеть, как они отличаются.

Если вы ищете дополнительный кредит, попробуйте реализовать это с помощью дженериков. Это не требует столько кода, сколько вы думаете.

Я использую Абстрактную Фабрику, когда мне нужно будет создать несколько дочерних фабрик для создания разных типов объектов. Все эти дочерние фабрики реализуют или расширяют Абстрактную Фабрику. Я использую Builder, когда существует только один тип объекта, который необходимо построить.

Я не уверен, что это правильное использование, но это то, как я видел это, и как я делал это в прошлом.

Хорошие ответы, я хотел бы добавить мои в надежде, что я понимаю, что мое понимание верно.

Если у вас новый офис, и вам нужны только новые столы, вы должны использовать Builder. Письменный стол всегда будет письменным столом, но существует множество различных конфигураций письменного стола: размер, форма, наличие клети, ящиков и т. Д.

Если у вас новый офис, а внутренняя часть полностью пуста, вам потребуются различные объекты и вы будете использовать Абстрактную фабрику, которая создает мебель, из которой спускается наш стол. В этом случае Desk, как мы знаем, является "сложным" объектом, поэтому он использует шаблон Builder, но здесь он является частью направляющей абстрактной фабрики. Абстрактная фабрика также создаст простые объекты, такие как очень специфические лампы (StraightFunkyLamp), через класс StraightFunkyLampFactory.

На практике вы, вероятно, будете использовать DI + IOC для того, чтобы делать то, что делал строитель. FYI.

Для получения дополнительной информации о том, когда использовать шаблон Builder и его преимущества, вы можете проверить мой пост для другого аналогичного вопроса здесь

Другие вопросы по тегам