В чем разница между шаблоном проектирования Builder и шаблоном проектирования Factory?
Какой из них более выгоден и почему?
Как мне представить мои результаты в виде графика, если я хочу проверить и сравнить / сопоставить эти шаблоны?
В чем разница между шаблоном проектирования Builder и шаблоном проектирования Factory?
Какой из них более выгоден и почему?
Как мне представить мои результаты в виде графика, если я хочу проверить и сравнить / сопоставить эти шаблоны?
Ответы:
В шаблонах проектирования обычно не существует «более выгодного» решения, которое бы работало для всех случаев. Это зависит от того, что вам нужно реализовать.
Из Википедии:
- Строитель фокусируется на построении сложного объекта шаг за шагом. Абстрактная фабрика выделяет семейство объектов товара (простых или сложных). Builder возвращает продукт в качестве последнего шага, но что касается абстрактной фабрики, продукт возвращается немедленно.
- Строитель часто строит Композит.
- Часто проекты начинаются с использования Factory Method (менее сложный, более настраиваемый, подклассы разрастаются) и переходят к Abstract Factory, Prototype или Builder (более гибким, более сложным), когда дизайнер обнаруживает, где требуется большая гибкость.
- Иногда шаблоны создания дополняют друг друга: Builder может использовать один из других шаблонов для реализации того, какие компоненты создаются. Abstract Factory, Builder и Prototype могут использовать Singleton в своих реализациях.
Википедия для шаблона проектирования фабрики: http://en.wikipedia.org/wiki/Factory_method_pattern
Запись в Википедии для шаблона проектирования строителя: http://en.wikipedia.org/wiki/Builder_pattern
Фабрика - это просто функция-обертка вокруг конструктора (возможно, в другом классе). Основное отличие состоит в том, что шаблон фабричного метода требует, чтобы весь объект был построен за один вызов метода, а все параметры были переданы в одной строке. Конечный объект будет возвращен.
Шаблон построителя , с другой стороны, по сути является объектом-оболочкой для всех возможных параметров, которые вы, возможно, захотите передать в вызов конструктора. Это позволяет вам использовать методы установки для медленного построения списка параметров. Одним дополнительным методом в классе построителя является метод build (), который просто передает объект построителя в нужный конструктор и возвращает результат.
В статических языках, таких как Java, это становится более важным, когда у вас есть более, чем несколько (потенциально необязательных) параметров, поскольку это устраняет необходимость иметь телескопические конструкторы для всех возможных комбинаций параметров. Также построитель позволяет вам использовать методы установки для определения доступных только для чтения или закрытых полей, которые нельзя изменить напрямую после вызова конструктора.
Базовый заводской пример
// Factory
static class FruitFactory {
static Fruit create(name, color, firmness) {
// Additional logic
return new Fruit(name, color, firmness);
}
}
// Usage
Fruit fruit = FruitFactory.create("apple", "red", "crunchy");
Пример Basic Builder
// Builder
class FruitBuilder {
String name, color, firmness;
FruitBuilder setName(name) { this.name = name; return this; }
FruitBuilder setColor(color) { this.color = color; return this; }
FruitBuilder setFirmness(firmness) { this.firmness = firmness; return this; }
Fruit build() {
return new Fruit(this); // Pass in the builder
}
}
// Usage
Fruit fruit = new FruitBuilder()
.setName("apple")
.setColor("red")
.setFirmness("crunchy")
.build();
Возможно, стоит сравнить примеры кода с этих двух страниц википедии:
http://en.wikipedia.org/wiki/Factory_method_pattern
http://en.wikipedia.org/wiki/Builder_pattern
Шаблон «Фабрика» можно рассматривать как упрощенную версию шаблона «Строитель».
В фабричном шаблоне фабрика отвечает за создание различных подтипов объекта в зависимости от потребностей.
Пользователь фабричного метода не должен знать точный подтип этого объекта. Пример метода фабрики createCar
мог бы возвратить Ford
или Honda
типизированный объект.
В шаблоне Builder различные подтипы также создаются методом builder, но состав объектов может отличаться в пределах одного и того же подкласса.
Чтобы продолжить пример с автомобилем, у вас может быть createCar
метод builder, который создает Honda
объект -типа с 4-цилиндровым двигателем или Honda
объект -типа с 6 цилиндрами. Шаблон строителя учитывает эту более тонкую гранулярность.
Схемы как шаблон Builder и шаблон метода завода доступны в Википедии.
Шаблон конструктора описывает объект, который знает, как создать другой объект определенного типа за несколько шагов. Он содержит необходимое состояние для целевого элемента на каждом промежуточном этапе. Подумайте, через что проходит StringBuilder для создания окончательной строки.
Шаблон фабричного проектирования описывает объект, который знает, как создать несколько различных, но связанных типов объектов за один шаг, где конкретный тип выбирается на основе заданных параметров. Подумайте о системе сериализации, где вы создаете свой сериализатор, и он создает желаемый объект в одном вызове загрузки.
Построение пошагового сложного объекта: шаблон строителя
Простой объект создается с помощью одного метода: шаблон фабричного метода
Создание объекта с использованием метода нескольких фабрик: абстрактный шаблон фабрики
Шаблон Builder и Factory, оба выглядят очень похожими на невооруженные глаза, потому что они оба создают объекты для вас.
Этот реальный пример прояснит разницу между ними.
Предположим, вы пошли в ресторан быстрого питания и заказали еду .
Пицца
Стручковый перец, помидор, барбекю курица, без ананаса
Таким образом, различные виды продуктов изготавливаются по шаблону Фабрики, но различные варианты (вкусы) конкретного продукта создаются по шаблону Builder.
Разные виды еды
Пицца, Бургер, Паста
Варианты Пиццы
Только сыр, сыр + помидор + стручковый перец, сыр + помидор и т. Д.
Вы можете увидеть пример реализации кода обеих моделей здесь
Builder шаблон
Factory Pattern
Оба являются Творческими паттернами для создания Объекта.
1) Factory Pattern - Предположим, у вас есть один суперкласс и N подклассов. Объект создается в зависимости от того, какой параметр / значение передается.
2) Шаблон Builder - для создания сложного объекта.
Ex: Make a Loan Object. Loan could be house loan, car loan ,
education loan ..etc. Each loan will have different interest rate, amount ,
duration ...etc. Finally a complex object created through step by step process.
Сначала несколько общих вещей, чтобы следовать моей аргументации:
Основная проблема при разработке больших программных систем состоит в том, что они должны быть гибкими и несложными для изменения. По этой причине есть некоторые показатели, такие как сцепление и сплоченность. Чтобы создать системы, которые можно легко изменять или расширять по функциональности без необходимости перепроектирования всей системы с нуля, вы можете следовать принципам проектирования (например, SOLID и т. Д.). Через некоторое время некоторые разработчики осознали, что, если они следуют этим принципам, есть некоторые похожие решения, которые хорошо работают для подобных проблем. Такими стандартными решениями оказались шаблоны проектирования.
Таким образом, шаблоны проектирования должны помочь вам следовать общим принципам проектирования, чтобы получить слабосвязанные системы с высокой когезией.
Отвечая на вопрос:
Задавая разницу между двумя шаблонами, вы должны спросить себя, какой шаблон делает вашу систему более гибкой. У каждого шаблона есть своя цель - организовать зависимости между классами в вашей системе.
Шаблон абстрактной фабрики: GoF: «Предоставить интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов».
Что это означает: предоставляя такой интерфейс, вызов конструктора каждого продукта семейства инкапсулируется в фабричный класс. И поскольку это единственное место во всей вашей системе, где вызываются эти конструкторы, вы можете изменить свою систему, внедрив новый класс фабрики. Если вы обмениваетесь представлением фабрики через другого, вы можете обмениваться целым набором продуктов, не затрагивая большую часть своего кода.
Шаблон Builder: GoF: «Отделите построение сложного объекта от его представления, чтобы один и тот же процесс построения мог создавать разные представления».
Что это значит: вы инкапсулируете процесс конструирования в другом классе, называемом директором (GoF). Этот директор содержит алгоритм создания новых экземпляров продукта (например, составление сложного продукта из других частей). Для создания составных частей всего продукта директор использует застройщика. Обменив конструктор с директором, вы можете использовать тот же алгоритм для создания продукта, но изменить представления отдельных частей (и, следовательно, представление продукта). Чтобы расширить или изменить свою систему в представлении продукта, все, что вам нужно сделать, это реализовать новый класс компоновщика.
Итак, вкратце: цель Abstract Factory Pattern - обменяться набором продуктов, которые созданы для совместного использования. Целью шаблона Builder является инкапсуляция абстрактного алгоритма создания продукта для его повторного использования для различных представлений продукта.
По моему мнению, вы не можете сказать, что Абстрактная фабричная модель - это старший брат Строительной модели. Да, они оба являются творческими шаблонами, но основное назначение шаблонов совершенно иное.
Одно поразительное различие между Строителем и фабрикой, которое я мог разглядеть, заключалось в следующем
предположим, у нас есть машина
class Car
{
bool HasGPS;
bool IsCityCar;
bool IsSportsCar;
int Cylenders;
int Seats;
public:
void Car(bool hasGPs=false,bool IsCityCar=false,bool IsSportsCar=false, int Cylender=2, int Seats=4);
};
В приведенном выше интерфейсе мы можем получить автомобиль следующим образом:
int main()
{
BadCar = new Car(false,false,true,4,4);
}
но что, если при создании Seats происходит какое-то исключение ??? ВЫ НЕ ПОЛУЧАЕТЕ ОБЪЕКТА // НО
Предположим, у вас есть реализация, как в следующем
class Car
{
bool mHasGPS;
bool mIsCityCar;
bool mIsSportsCar;
int mCylenders;
int mSeats;
public:
void Car() : mHasGPs(false), mIsCityCar(false), mIsSportsCar(false), mCylender(2), mSeats(4) {}
void SetGPS(bool hasGPs=false) {mHasGPs = hasGPs;}
void SetCity(bool CityCar) {mIsCityCar = CityCar;}
void SetSports(bool SportsCar) {mIsSportsCar = SportsCar;}
void SetCylender(int Cylender) {mCylenders = Cylender;}
void SetSeats(int seat) {mSeats = seat;}
};
class CarBuilder
{
Car* mCar;
public:
CarBuilder():mCar(NULL) { mCar* = new Car(); }
~CarBuilder() { if(mCar) { delete mCar; }
Car* GetCar() { return mCar; mCar=new Car(); }
CarBuilder* SetSeats(int n) { mCar->SetSeats(n); return this; }
CarBuilder* SetCylender(int n) { mCar->SetCylender(n); return this; }
CarBuilder* SetSports(bool val) { mCar->SetSports(val); return this; }
CarBuilder* SetCity(bool val) { mCar->SetCity(val); return this; }
CarBuilder* SetGPS(bool val) { mCar->SetGPS(val); return this; }
}
Теперь вы можете создавать как это
int main()
{
CarBuilder* bp =new CarBuilder;
Car* NewCar = bp->SetSeats(4)->SetSports(4)->SetCity(ture)->SetGPS(false)->SetSports(true)->GetCar();
bp->SetSeats(2);
bp->SetSports(4);
bp->SetCity(ture);
bp->SetSports(true)
Car* Car_II= bp->GetCar();
}
Здесь, во втором случае, даже если одна операция не удастся, вы все равно получите автомобиль.
Может быть, этот автомобиль не работает идеально позже, но у вас будет объект.
Потому что Factory Method дает вам Автомобиль за один вызов, тогда как Строитель строит один за другим.
Хотя, это зависит от потребностей соискателя, к которому идти.
+-------------------------------------------------------------------+---------------------------------------------------+
| Builder | Factory |
+-------------------------------------------------------------------+---------------------------------------------------+
| Return only single instance to handle complex object construction | Retrun various instances on multiple constructors |
| No interface required | Interface driven |
| Inner classes is involved (to avoid telescopic constructors) | Subclasses are involved |
+-------------------------------------------------------------------+---------------------------------------------------+
Телескопическая модель конструктора
Аналогия:
Строитель и Абстрактная Фабрика предназначались для разных целей. В зависимости от правильного варианта использования, вы должны выбрать подходящий шаблон дизайна.
Builder важных функций:
Фабрика (простая Фабрика) отличительные особенности:
Часто проекты начинаются с использования Factory Method (менее сложный, более настраиваемый, подклассы разрастаются) и переходят к Abstract Factory , Prototype или Builder. (более гибким, более сложным).
Посмотрите на соответствующие сообщения:
Держать строителя в отдельном классе (свободный интерфейс)
Шаблоны проектирования: метод фабрики против фабрики против абстрактной фабрики
Вы можете обратиться к ниже статьи для более подробной информации:
Фабрика : Используется для создания экземпляра объекта, где зависимости объекта полностью хранятся на фабрике. Для шаблона абстрактной фабрики часто существует много конкретных реализаций одной и той же абстрактной фабрики. Правильная реализация фабрики вводится через внедрение зависимостей.
Builder : Используются для создания неизменяемых объектов, когда зависимости объекта быть обработаны частично известны заранее и частично предоставленным клиентом строителя.
Шаблоны Abstract Factory и Builder - это шаблоны Creation, но с разными целями.
Abstract Factory Pattern делает упор на создание объектов для семейств связанных объектов, где:
Шаблон Builder ориентирован на создание сложного объекта шаг за шагом. Он отделяет представление от процесса построения сложного объекта, так что один и тот же процесс построения может использоваться для разных представлений.
Сложная конструкция - это когда конструируемый объект состоит из других объектов, которые представлены абстракциями.
Рассмотрим меню в McDonald's. Меню содержит напиток, основное и боковое. В зависимости от того, какие потомки отдельных абстракций составлены вместе, созданное меню имеет другое представление.
Там мы получили два экземпляра меню с разными представлениями. Процесс строительства в свою очередь остается прежним. Вы создаете меню с напитком, основным и боковым.
Используя шаблон построителя, вы отделяете алгоритм создания сложного объекта от различных компонентов, использованных для его создания.
С точки зрения шаблона компоновщика алгоритм инкапсулируется в директоре, тогда как компоновщики используются для создания составных частей. Изменение используемого построителя в алгоритме директора приводит к другому представлению, потому что другие части составлены в меню. Способ создания меню остается прежним.
Основное различие между ними заключается в том, что шаблон Builder в первую очередь описывает создание сложных объектов шаг за шагом. В шаблоне Абстрактная фабрика акцент делается на семейства объектов-продуктов . Строитель возвращает товар на последнем шаге . Находясь в шаблоне «Абстрактная фабрика», продукт доступен сразу .
Пример: допустим, мы создаем лабиринт
1. Абстрактная фабрика:
Maze* MazeGame::CreateMaze (MazeFactory& factory) {
Maze* maze = factory.MakeMaze(); /// product is available at start!!
/* Call some methods on maze */
return maze;
}
2. Строитель:
Maze* MazeGame::CreateMaze (MazeBuilder& builder) {
builder.buildMaze(); /// We don't have access to maze
/* Call some methods on builder */
return builder.GetMaze();
}
Я считаю, что использование и различие между шаблонами Factory & Builder можно легче понять / прояснить за определенный период времени, поскольку вы работали над одной и той же кодовой базой и меняли требования.
По моему опыту, обычно вы начинаете с паттерна Factory, включающего несколько статических методов-создателей, которые в первую очередь скрывают относительно сложную логику инициализации. Поскольку ваша иерархия объектов становится более сложной (или когда вы добавляете больше типов, параметров), вы, вероятно, в конечном итоге заполняете ваши методы большим количеством параметров, и не говоря уже о том, что вам придется перекомпилировать модуль Factory. Все эти вещи увеличивают сложность методов вашего создателя, снижают читаемость и делают модуль создания более хрупким.
Эта точка, возможно, будет точкой перехода / расширения. Таким образом, вы создаете модуль-обертку вокруг параметров конструкции, а затем вы сможете представлять новые (похожие) объекты, добавляя еще несколько абстракций (возможно) и реализации, не затрагивая фактическую логику создания. Итак, у вас была "менее" сложная логика.
Честно говоря, ссылка на что-то вроде «создание объекта за один шаг или несколько шагов - это различие», так как единственного фактора разнообразия мне было недостаточно, чтобы различать их, поскольку я мог использовать оба способа почти во всех случаях, с которыми я сталкивался до сейчас, не испытывая никакой пользы. Так вот что я наконец-то подумала об этом.
Основное преимущество шаблона компоновщика по сравнению с фабричным шаблоном заключается в том, что если вы хотите создать какой-то стандартный объект с множеством возможных настроек, но обычно вам приходится настраивать только несколько.
Например, если вы хотите написать HTTP-клиент - вы настроите некоторые параметры по умолчанию, такие как время записи / чтения по умолчанию, протоколы, кэш, DNS, перехватчики и т. Д.
Большинство пользователей вашего клиента будут просто использовать эти параметры по умолчанию, в то время как некоторые другие пользователи могут захотеть настроить некоторые другие параметры. В некоторых случаях вам просто нужно изменить тайм-ауты и использовать оставшиеся как есть, а в других случаях вам может понадобиться настроить, например, кэш.
Вот возможные способы создания экземпляра вашего клиента (взяты из OkHttpClient):
//just give me the default stuff
HttpClient.Builder().build()
//I want to use custom cache
HttpClient.Builder().cache(MyCache()).build()
//I want custom connection timeout
HttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).build()
//I am more interested in read/write timeout
HttpClient.Builder()
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS).build()
Если бы вы использовали фабричный шаблон для этого, вы в конечном итоге напишите множество методов со всеми возможными комбинациями параметров создания. Со сборщиком вы просто указываете тех, кто вам небезразличен, и позволяете строителю построить его для вас, заботясь обо всех остальных параметрах.
Шаблон сборки подчеркивает сложность создания объекта (решается с помощью «шагов»)
Абстрактный узор подчеркивает «просто» на «абстракции» (множественных, но связанных) объектов.
Разница очевидна В шаблоне строителя, строитель создаст объект определенного типа для вас. Вы должны сказать, что строитель должен построить. В фабричном шаблоне, используя абстрактный класс, вы напрямую создаете конкретный объект.
Здесь класс построителя действует как посредник между основным классом и классами определенного типа. Больше абстракции.
Оба очень похожи, но если у вас есть большое количество параметров для создания объектов, некоторые из которых являются необязательными со значениями по умолчанию, выберите шаблон Builder.
ИМХО
Строитель - это какая-то более сложная фабрика.
Но в Builder вы можете создавать объекты с использованием других фабрик. , которые необходимы для создания окончательного и действительного объекта.
Итак, говоря об эволюции «Творческих паттернов» по сложности, вы можете думать об этом следующим образом:
Dependency Injection Container -> Service Locator -> Builder -> Factory
Оба шаблона имеют одну и ту же необходимость: скрыть от некоторого клиентского кода логику построения сложного объекта. Но что делает «сложным» (или иногда усложняющим) объект? Главным образом, это связано с зависимостями, точнее состоянием объекта, состоящего из более частичных состояний. Вы можете ввести зависимости с помощью конструктора, чтобы установить начальное состояние объекта, но объекту может потребоваться много из них, некоторые будут в исходном состоянии по умолчанию (просто потому, что мы должны были узнать, что установка зависимости по умолчанию на ноль - не самый чистый способ ) и некоторый другой набор в состояние, управляемое некоторым условием. Более того, существуют свойства объекта, которые являются своего рода «забывчивыми зависимостями», но также они могут принимать необязательные состояния.
Есть два хорошо известных способа справиться с этой сложностью:
Композиция / агрегация: создайте объект, создайте его зависимые объекты, затем соедините вместе. Здесь конструктор может сделать прозрачным и гибким процесс, который определяет правила, определяющие конструкцию компонента.
Полиморфизм: правила построения объявляются непосредственно в определении подтипа, поэтому у вас есть набор правил для каждого подтипа, и некоторые условия решают, какое из этих правил применимо для построения объекта. Завод отлично вписывается в этот сценарий.
Ничто не мешает смешать эти два подхода. Семейство продуктов может абстрагироваться от создания объекта, выполненного с помощью компоновщика, а компоновщик может использовать фабрики для определения того, какой компонент объекта создается.
На мой взгляд, шаблон Builder используется, когда вы хотите создать объект из группы других объектов, а создание детали должно быть независимым от объекта, который вы хотите создать. Это помогает скрыть создание детали от клиента, чтобы сделать строителя и клиента независимыми. Используется для создания сложных объектов (объектов, которые могут состоять из сложных свойств)
В то время как фабричный шаблон указывает, что вы хотите создать объекты общего семейства, и вы хотите, чтобы он был сразу зарезервирован. Используется для более простых объектов.
Строитель и Абстрактная Фабрика
Шаблон проектирования Builder в некоторой степени очень похож на шаблон Abstract Factory. Вот почему важно уметь различать ситуации, когда используется один или другой. В случае абстрактной фабрики клиент использует методы фабрики для создания своих собственных объектов. В случае Builder класс Builder получает инструкции о том, как создать объект, а затем его запрашивают, но способ, которым этот класс составлен, зависит от класса Builder, и эта деталь делает различие между двумя шаблонами.
Общий интерфейс для продуктов
На практике продукты, созданные бетоностроителями, имеют существенно различную структуру, поэтому, если нет причин выводить разные продукты, общий родительский класс. Это также отличает шаблон Builder от шаблона Abstract Factory, который создает объекты, производные от общего типа.
Фабричный шаблон создает конкретную реализацию класса во время выполнения, т. Е. Его основное намерение - использовать полиморфизм, чтобы позволить подклассам решать, какой класс создавать. Это означает, что во время компиляции мы не знаем точный класс, который будет создан, в то время как шаблон Builder в основном занимается решением проблемы телескопирования конструкторов antipattern, которая возникает из-за большого количества необязательных полей класса. В паттерне компоновщика нет понятия полиморфизма, поскольку мы знаем, какой объект мы пытаемся построить во время компиляции.
Единственная общая тема этих двух шаблонов - сокрытие конструкторов и создание объектов за фабричными методами, а также метод сборки для улучшенного конструирования объектов.
Фабричный шаблон позволяет сразу создавать объект, а компоновочный шаблон - прерывать процесс создания объекта. Таким образом, вы можете добавить различные функции при создании объекта.