Паттерн 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 процесс конструирования продукта происходит более гибко.
⇑
Related topics
⇑