Паттерн Builder. Огляд та дослідження. Реалізація на C++
Зміст
- 1. Паттерн Builder. Призначення. Загальні відомості
- 2. Структура паттерну Builder. Рисунок
- 3. Використання паттерну Builder клієнтом. Діаграма взаємодії
- 4. Приклад реалізації паттерну Builder на C++
- 5. Особливості застосування паттерну Builder
- Зв’язані теми
Пошук на інших ресурсах:
1. Паттерн Builder. Призначення. Загальні відомості
Паттерн Builder належить до породжуючих паттернів і використовується для породження об’єктів.
Необхідність використання паттерну Builder в програмі виникає у випадках, коли потрібно додавати нові можливості без суттєвої зміни коду. Під можливостями розуміються додаткові перетворення, що генерують кінцевий продукт (об’єкт). У паттерні Builder представлення об’єкту відділене від його конструювання (побудови). При цьому, для конкретної конструкції отримуються різні представлення.
⇑
2. Структура паттерну Builder. Рисунок
Найчастіше в літературі зустрічається наступна структура паттерну Builder (рисунок 1).
Рисунок 1. Структура паттерну Builder
На вищенаведеному рисунку 1, структура описує наступне:
- клас ConcreteBuilder, що породжує об’єкт Product – це є конкретний будівник. Клас реалізує метод BuildPart() інтерфейсу Builder. Також, клас містить метод GetResult(), який повертає покажчик (посилання) на сконструйований об’єкт (продукт). Таким чином клас ConcreteBuilder створює деяке представлення у програмі;
- інтерфейс (абстрактний клас) Builder, в якому задається метод або декілька методів, що надаються розпоряднику (клас Director), який викликає процес конструювання об’єкту;
- клас Director (розпорядник) – отримує посилання (покажчик) builder на інтерфейс Builder. Це є типова форма агрегації. З допомогою цього посилання клас може викликати екземпляр класу ConcreteBuilder. Якщо класів будівників (на кшталт ConcreteBuilder) декілька, то є можливість вибирати відповідні методи побудови об’єктів цих класів (продуктів);
- Product – це є конкретний продукт (об’єкт) класу ConcreteBuilder, який повертається методом GetResult().
⇑
3. Використання паттерну Builder клієнтом. Діаграма взаємодії
При використанні паттерна Builder клієнт має в розпорядженні :
- клас розпорядника Director;
- інтерфейс (абстрактний клас, клас) Builder.
Послідновність кроків клієнта наступна.
- Створити об’єкт класу типу ConcreteBuilder (створити будівельника).
- Створити екземпляр класу розпорядника Director і зконфігурувати його екземпляром класу ConcreteBuilder.
- У класі розпорядника Director викликати методи (запити), які звертаються до методів класу будівельника (ConcreteBuilder) з запитом побудувати продукт (об’єкт). У результаті, будівельник (ConcreteBuilder) створює об’єкт типу Product.
- Клієнт отримує продукт у будівельника та використовує його.
На рисунку 2 зображена діаграма взаємодії клієнта з розпорядником (Director) та будівником (ConcreteBuilder).
Рисунок 2. Паттерн Builder. Діаграма взаємодії
⇑
4. Приклад реалізації паттерну Builder на C++
У прикладі демонструється використання паттерну Builder для генерування об’єктів класу Product. Назви класів відповідають рисунку 1. Умовно прийнято, що клас Product містить код, який складається з двох цілочисельних частин: part1, part2. Ці частини потрібно побудувати використовуючи підхід паттерну Builder. У коді клієнта (функція main()) отримується екземпляр класу Product зі значеннями частин 25 та 777. Потім робиться перевірка отриманого результату.
На рисунку 3 зображено структуру, що використовується для рішення даної задачі.
Рисунок 3. Паттерн Builder. Структура розв’язку задачі
Текст рішення задачі на мові C++ (Console Application), що відповідає рисунку 3, наступний.
#include <iostream> using namespace std; // Клас, що є продуктом class Product { public: int part1; // частина 1 int part2; // частина 2 }; // Клас, що реалізує інтерфейс з клієнтом class Builder { public: virtual void CreateProduct() { } virtual void BuildPart1(int part1) { } virtual void BuildPart2(int part2) { } virtual Product* GetResult() { return nullptr; } }; // Клас, що є конкретним будівником class ConcreteBuilder : public Builder { private: Product* currentBuilder; public: // Конструктор ConcreteBuilder() { currentBuilder = nullptr; } // Реалізація віртуальних методів virtual void CreateProduct() { cout << "ConcreteBuilder::CreateProduct()" << endl; currentBuilder = new Product(); } // Побудувати частину 1 virtual void BuildPart1(int part1) { cout << "ConcreteBuilder::currentBuilder->part1 = " << part1 << endl; currentBuilder->part1 = part1; } // Побудувати частину 2 virtual void BuildPart2(int part2) { cout << "ConcreteBuilder::currentBuilder->part2 = " << part2 << endl; currentBuilder->part2 = part2; } // Метод, що повертає продукт для клієнта virtual Product* GetResult() { return currentBuilder; } // Деструктор ~ConcreteBuilder() { if (currentBuilder != nullptr) delete currentBuilder; } }; // Клас - розпорядник class Director { public: // Метод, що конструює частини void Construct(Builder& builder) { // 1. Створити продукт builder.CreateProduct(); // 2. Побудувати частину 1 builder.BuildPart1(25); // 3. Побудувати частину 2 builder.BuildPart2(777); } }; void main() { // Функція main() у даному випадку виступає клієнтом // 1. Оголосити покажчик на продукт, який потрібно отримати Product* product; // 2. Створити конкретний екземпляр класу ConcreteBuilder і заповнити значеннями ConcreteBuilder B; // 3. Створити клас-розпорядник і зконфігурувати його продуктом B Director D; D.Construct(B); // зконфігурувати // 4. Після конфігурування, передати створений продукт клієнту product = B.GetResult(); // 5. Вивести значення продукту для контролю cout << "product->part1 = " << product->part1 << endl; cout << "product->part2 = " << product->part2 << endl; }
Після виконання програма видасть наступний результат
ConcreteBuilder::CreateProduct() ConcreteBuilder::currentBuilder->part1 = 25 ConcreteBuilder::currentBuilder->part2 = 777 product->part1 = 25 product->part2 = 777
⇑
5. Особливості застосування паттерну Builder
У паттерні Builder можна виділити наступні характерні особливості застосування:
- паттерн побудований таким чином, що дозволяє змінювати внутрішнє представлення продукту. Можна виконувати різні варіанти побудови результуючого об’єкту;
- у паттерні строго відділяється код побудови об’єкту від коду представлення складного об’єкту;
- паттерн Builder покращує модульність. Це означає, що спосіб конструювання та представлення складного об’єкту інкапсулюються. Клієнт не знає про особливості реалізації класів, які визначають внутрішню структуру продукту;
- є можливість без зміни структури основного коду додавати нових будівельників (ConcreteBuilder2, ConcreteBuilder3 і т.д.);
- розпорядник (Director) може будувати різні варіанти продукту з тих самих частин. Також може бути декілька розпорядників (Director2, Director3 і т.д.);
- процес конструювання продукту може бути розбитий на частини (BuildPart1(), BuildPart2(), і т.д.) під керівництвом розпорядника (Director). У порівнянні з іншими породжуючими паттернами в паттерні Builder процес конструювання продукту відбувається більш гнучко.
⇑
Зв’язані теми
⇑