Яка різниця між схемою дизайну Builder від схемою дизайну Factory?
Хто з них вигідніший і чому?
Як я можу представити свої результати як графік, якщо хочу перевірити та порівняти / порівняти ці зразки?
Яка різниця між схемою дизайну Builder від схемою дизайну Factory?
Хто з них вигідніший і чому?
Як я можу представити свої результати як графік, якщо хочу перевірити та порівняти / порівняти ці зразки?
Відповіді:
Що стосується дизайнерських моделей, зазвичай не існує "більш вигідного" рішення, яке працює для всіх випадків. Це залежить від того, що потрібно здійснити.
З Вікіпедії:
- Будівельник фокусується на побудові складного об'єкта поетапно. Анотація Фабрика підкреслює сімейство товарних об'єктів (простих чи складних). Builder повертає продукт як завершальний крок, але що стосується абстрактної фабрики, то продукт повертається негайно.
- Будівельник часто будує Композит.
- Часто проекти починають використовувати заводський метод (менш складний, більш настроюваний, підкласи розповсюджуються) і розвиваються в напрямку абстрактних заводів, прототипів або будівельників (більш гнучких, складніших), оскільки дизайнер виявляє, де потрібна більша гнучкість.
- Іноді креативні моделі є додатковими: Builder може використовувати один з інших шаблонів, щоб реалізувати, які компоненти будуються. Анотація Factory, Builder та Prototype можуть використовувати Singleton у своїх реалізаціях.
Запис у Вікіпедії для фабричного дизайну: http://en.wikipedia.org/wiki/Factory_method_pattern
Запис у Вікіпедії для моделі дизайну розробника: http://en.wikipedia.org/wiki/Builder_pattern
Фабрика - це просто функція обгортки навколо конструктора (можливо, в іншому класі). Ключова відмінність полягає в тому, що заводський шаблон методу вимагає, щоб весь об'єкт був побудований в одному виклику методу, з усіма параметрами, переданими в одному рядку. Кінцевий об’єкт буде повернутий.
З іншого боку, модель будівельника є, по суті, об'єктом обгортки навколо всіх можливих параметрів, які ви можете передати в виклик конструктора. Це дозволяє використовувати методи встановлення для повільного складання списку параметрів. Один додатковий метод класу builder - це метод build (), який просто передає об'єкт builder в потрібний конструктор і повертає результат.
У статичних мовах, таких як 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");
Приклад базового конструктора
// 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
Фабричний шаблон майже можна розглядати як спрощену версію шаблону Builder.
За фабричним зразком фабрика відповідає за створення різних підтипів об'єкта залежно від потреб.
Користувачу фабричного методу не потрібно знати точний підтип цього об’єкта. Приклад заводського методу createCar
може повернути об'єкт Ford
або Honda
набраний.
У шаблоні Builder різні підтипи також створюються методом builder, але склад об'єктів може відрізнятися в межах одного підкласу.
Для продовження прикладу автомобіля у вас може бути createCar
метод будівельника, який створює об'єкт- Honda
тип з 4-циліндровим двигуном або Honda
-типовий об'єкт з 6 циліндрами. Шаблон конструктора дозволяє досягти такої тонкої деталізації.
Діаграми як моделі Builder, так і фабричного методу доступні у Вікіпедії.
Шаблон дизайну конструктора описує об'єкт, який знає, як створити інший об'єкт певного типу протягом декількох кроків. Він містить необхідний стан для цільового елемента на кожному проміжному кроці. Подумайте, через що проходить StringBuilder, щоб отримати остаточний рядок.
Фабрична схема дизайну описує об'єкт, який знає, як створити кілька різних, але пов'язаних між собою видів об'єктів за один крок, де конкретний тип вибирається виходячи із заданих параметрів. Подумайте про систему серіалізації, де ви створюєте свій серіалізатор, і він конструює потрібне в об'єкті все за один виклик завантаження.
Побудова складного об'єкта поетапно: побудова малюнка
Простий об’єкт створюється за допомогою одного методу: заводський метод шаблону
Створення об'єкта за допомогою декількох заводських методів: абстрактний заводський візерунок
Зображення Builder і Factory pattern - обидва схожі на неозброєні очі, оскільки вони обидва створюють для вас об’єкти.
Цей приклад із реального життя зробить різницю між двома більш зрозумілими.
Припустимо, ви зайшли в ресторан швидкого харчування і замовили їжу .
Піца
Capsicum, помідор, курятина для барбекю, НЕ БУДУТЬ
Так різні страви виготовляються за фабричним малюнком, але різні варіанти (ароматизатори) певної їжі виробляються за допомогою моделі Builder.
Різні види їжі
Піца, бургер, паста
Варіанти піци
Тільки сир, сир + помідор + капсикум, сир + помідор і т.д.
Ви можете побачити приклад реалізації коду обох моделей тут
Builder шаблон
Factory Pattern
Обидва - це креативні візерунки, щоб створити об’єкт.
1) Заводська схема - Припустимо, у вас є один суперклас і N кількість підкласів. Об'єкт створений залежить від того, який параметр / значення передається.
2) Шаблон будівельника - для створення складного об'єкта.
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: "Забезпечте інтерфейс для створення сімей пов'язаних або залежних об'єктів, не вказуючи їх конкретні класи".
Що це означає: надаючи подібний інтерфейс, виклик конструктору кожного із продуктів сімейства інкапсулюється у заводський клас. А оскільки це єдине місце у всій вашій системі, де викликаються ці конструктори, ви можете змінити свою систему, застосувавши новий заводський клас. Якщо ви обмінюєте представництво заводу на інший, ви можете обмінятися цілим набором продуктів, не торкаючись більшості свого коду.
Шаблон будівельника: GoF: "Відокремте побудову складного об'єкта від його подання, щоб той самий процес будівництва міг створювати різні уявлення".
Що це означає: Ви інкапсулюєте процес будівництва в іншому класі, названому директором (GoF). Цей директор містить алгоритм створення нових екземплярів продукту (наприклад, складіть складний продукт з інших частин). Для створення цілісних частин усього продукту режисер використовує конструктор. Обмінюючи програму-конструктор, ви можете використовувати один і той же алгоритм для створення продукту, але змінювати уявлення про окремі частини (і так представлення продукту). Щоб розширити або змінити систему в представленні продукту, все, що вам потрібно зробити, - це впровадити новий клас будівельників.
Отже, коротше: мета абстрактного заводу - обмін набором продуктів, які виготовляються для спільного використання. Мета Шаблон 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);
}
але що робити, якщо під час створення місць трапляється якийсь виняток ??? НЕ ВІДБУДЕТЕ ОБ'ЄКТ У ВСІХ // АЛЕ
припустимо, у вас є реалізація, як описано нижче
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();
}
Тут, у другому випадку, навіть якщо одна операція не вдасться, ви все одно отримаєте Автомобіль.
Можливо, ця машина не спрацює ідеально пізніше, але, ви б мали це об'єкт.
Тому що заводський метод дає вам Автомобіль за один дзвінок, тоді як Будівельник будує по черзі.
Хоча, це залежить від потреб дизайну, на кого саме йти.
+-------------------------------------------------------------------+---------------------------------------------------+
| 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 & Builder - це як креативні моделі, але з різними намірами.
Абстрактний заводський візерунок наголошує на створенні об'єктів для сімей пов'язаних об'єктів, де:
Шаблон будівельника фокусується на побудові складного об'єкта поетапно. Це відокремлює подання від процесу побудови складного об'єкта, так що той самий процес побудови може бути використаний для різних уявлень.
Складна конструкція - це коли об'єкт, що будується, складається з різних інших об'єктів, які представлені абстракціями.
Розглянемо меню в Макдональдсі. Меню містить напій, головний та бічний. Залежно від того, які нащадки окремих абстракцій складаються разом, створене меню має ще одне подання.
Там ми отримали два екземпляри меню з різними поданнями. Процес будівництва в свою чергу залишається колишнім. Ви створюєте меню з напоєм, головним і бічним.
Використовуючи шаблон конструктора, ви відокремлюєте алгоритм створення складного об'єкта від різних компонентів, що використовуються для його створення.
З точки зору схеми побудови, алгоритм інкапсульований у директорі, тоді як будівельники використовуються для створення цілісних частин. Змінення використовуваного конструктора в алгоритмі директора призводить до різного представлення, оскільки інші частини складаються з меню. Спосіб створення меню залишається тим самим.
Основна відмінність між ними полягає в тому, що візерунок Builder в першу чергу описує створення складних об'єктів поетапно. У абстрактному заводському зразку акцент робиться на родини предметів-виробів . Builder повертає продукт на останньому кроці . Перебуваючи в абстрактній заводській схемі, продукт доступний відразу .
Приклад: Скажімо, що ми створюємо Maze
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 можна зрозуміти / уточнити легше за певний проміжок часу, коли ви працювали над тією ж базою коду та змінюючи вимоги.
З мого досвіду, як правило, ви починаєте з заводської схеми, що включає пару статичних методів створення, щоб передусім приховати порівняно складну логіку ініціалізації. Оскільки ваша ієрархія об'єктів стає складнішою (або при додаванні більше типів, параметрів), ви, ймовірно, отримаєте свої методи заповнені більшою кількістю параметрів, і не кажучи вже про те, що вам доведеться перекомпілювати заводський модуль. Всі ці речі, збільшують складність методів ваших творців, зменшують читабельність та роблять модуль створення більш крихким.
Ця точка, можливо, буде точкою переходу / розширення. Роблячи це, ви створюєте модуль обгортки навколо параметрів побудови, і тоді ви зможете представляти нові (подібні) об’єкти, додаючи ще кілька абстракцій (можливо) та реалізацій, не торкаючись фактичної логіки створення. Отже, у вас була "менш" складна логіка.
Відверто кажучи, посилаючись на щось таке, що "мати об'єкт, створений в один крок або кілька кроків, є різницею", оскільки єдиний фактор різноманітності не був достатнім для їх розрізнення, оскільки я міг використовувати обидва способи майже для всіх випадків, з якими стикався тепер, не відчуваючи ніякої користі. Тож це я остаточно подумав про це.
Основна перевага шаблону конструктора перед заводським зразком полягає в тому випадку, якщо ви хочете створити якийсь стандартний об'єкт з великою кількістю можливих налаштувань, але зазвичай ви налаштовуєте лише кілька.
Наприклад, якщо ви хочете написати 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 дуже схожий, певною мірою, на абстрактний заводський візерунок. Ось чому важливо вміти розрізняти ситуації, коли використовується та чи інша. У випадку з "Фабрика абстрактних" клієнт використовує фабричні методи для створення власних об'єктів. У випадку Builder клас Builder вказується, як створити об’єкт, а потім його запитують, але спосіб складання класу залежить від класу Builder, і ця деталь робить різницю між двома шаблонами.
Загальний інтерфейс для продуктів
На практиці вироби, створені бетонниками, мають суттєво різну структуру, тому, якщо немає причин отримувати різні продукти, загальний батьківський клас. Це також відрізняє модель "Builder" від "абстрактної фабрики", яка створює об'єкти, отримані із загального типу.
Фабрична схема створює конкретну реалізацію класу під час виконання, тобто його головний намір полягає у використанні поліморфізму для того, щоб підкласи могли вирішити, який клас інстанціювати. Це означає, що під час компіляції ми не знаємо точного класу, який буде створено, тоді як модель Builder в основному стосується вирішення проблеми телескопічних конструкторів антипатерн, яка виникає через велику кількість необов'язкових полів класу. У схемі конструктора немає поняття поліморфізму, оскільки ми знаємо, який об’єкт ми намагаємося побудувати під час компіляції.
Єдиною поширеною темою цих двох шаблонів є приховування конструкторів та створення об'єктів за заводськими методами та методом побудови для поліпшення побудови об'єктів.
Фабричний візерунок дозволяє вам створювати об’єкт відразу, тоді як модель розробника дозволяє вам порушити процес створення об'єкта. Таким чином ви можете додати різну функціональність під час створення об’єкта.