Паттерн Builder
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 93 из 104.
Паттерн Строитель (Builder) отделяет конструирование сложного объекта от его представления, позволяя создавать различные конфигурации шаг за шагом. Это особенно полезно, когда объект имеет множество необязательных параметров или требует определенной последовательности конструирования.
Вместо конструктора с множеством параметров (что становится запутанным), Строитель (Builder) предоставляет четкие именованные методы для каждого варианта конфигурации:
#include <iostream>
#include <string>
class Pizza {
public:
std::string dough;
std::string sauce;
std::string topping;
bool cheese;
void describe() const {
std::cout << dough << " dough, " << sauce << " sauce, "
<< topping << (cheese ? ", with cheese" : "") << "\n";
}
};
class PizzaBuilder {
private:
Pizza pizza;
public:
PizzaBuilder& setDough(const std::string& d) {
pizza.dough = d;
return *this;
}
PizzaBuilder& setSauce(const std::string& s) {
pizza.sauce = s;
return *this;
}
PizzaBuilder& setTopping(const std::string& t) {
pizza.topping = t;
return *this;
}
PizzaBuilder& addCheese() {
pizza.cheese = true;
return *this;
}
Pizza build() { return pizza; }
};
int main() {
Pizza margherita = PizzaBuilder()
.setDough("thin")
.setSauce("tomato")
.setTopping("basil")
.addCheese()
.build();
margherita.describe();
}Каждый метод-сеттер возвращает ссылку на строитель (return *this), позволяя использовать цепочку вызовов методов для реализации текучего интерфейса. Метод build() завершает создание и возвращает сконструированный объект.
Используйте Builder при создании объектов с множеством необязательных компонентов, когда вам нужен читаемый код конструирования или когда один и тот же процесс конструирования должен создавать различные представления.
Задание
ЛегкоДавайте создадим систему Computer Builder, которая позволяет пользователям настраивать кастомные ПК шаг за шагом. Это идеальный сценарий для паттерна Builder (Строитель) — компьютеры имеют множество опциональных компонентов, и нам нужен чистый, читаемый способ сборки различных конфигураций.
Вы организуете свой код в трех файлах:
Computer.h: Определите класс продукта, который представляет собой полностью сконфигурированный компьютер.Ваш класс
Computerдолжен хранить эти компоненты в качестве приватных членов:cpu(string),ram(string),storage(string),gpu(string) иhasWifi(boolean). ИнициализируйтеhasWifiзначениемfalse, а строки по умолчанию сделайте пустыми.Добавьте публичный метод
showSpecs(), который отображает конфигурацию компьютера. Для каждого установленного компонента (непустая строка) выведите его на отдельной строке. Наличие Wi-Fi должно выводиться только в том случае, если оно включено.ComputerBuilder.h: Создайте класс строителя, который конструирует компьютеры с использованием текучего интерфейса (fluent interface).Ваш класс
ComputerBuilderдолжен иметь приватный членComputer, который он собирает. Реализуйте следующие методы, каждый из которых возвращает ссылку на строителя для обеспечения цепочки вызовов (method chaining):setCPU(const std::string& cpu)setRAM(const std::string& ram)setStorage(const std::string& storage)setGPU(const std::string& gpu)addWifi()— включает возможность Wi-Fi
Также реализуйте метод
build(), который возвращает готовый объектComputerи сбрасывает состояние строителя для возможного повторного использования.main.cpp: Объедините все вместе, чтобы собрать кастомный компьютер.Прочитайте четыре входных значения:
- Модель процессора (string)
- Спецификация оперативной памяти (string)
- Спецификация накопителя (string)
- Нужно ли включать Wi-Fi:
yesилиno
Используйте
ComputerBuilderдля сборки компьютера с указанными CPU, RAM и накопителем. Если на вход для Wi-Fi поданоyes, также вызовитеaddWifi(). Обратите внимание, что GPU в этой конфигурации не указывается — строитель должен корректно обрабатывать опциональные компоненты.После сборки вызовите
showSpecs()для полученного компьютера, чтобы отобразить его конфигурацию.
Метод showSpecs() должен выводить данные в следующем формате (показывая только те компоненты, которые были установлены):
CPU: [value]
RAM: [value]
Storage: [value]
GPU: [value]
Wifi: EnabledНапример, при входных данных Intel i7-12700K, 32GB DDR5, 1TB NVMe SSD и yes:
CPU: Intel i7-12700K
RAM: 32GB DDR5
Storage: 1TB NVMe SSD
Wifi: EnabledПри входных данных AMD Ryzen 5 5600X, 16GB DDR4, 512GB SSD и no:
CPU: AMD Ryzen 5 5600X
RAM: 16GB DDR4
Storage: 512GB SSDОбратите внимание, как паттерн Builder делает процесс сборки читаемым и гибким — вы можете легко добавлять или пропускать компоненты, а цепочка методов создает понятный, самодокументированный код. Строка с GPU не появляется, потому что мы её не устанавливали, что демонстрирует, как строители естественным образом обрабатывают опциональные компоненты.
Шпаргалка
Паттерн Строитель (Builder) отделяет конструирование сложного объекта от его представления, позволяя выполнять пошаговую настройку. Он полезен, когда объект имеет множество необязательных параметров или требует определенной последовательности создания.
Вместо конструкторов с большим количеством параметров, Строитель предоставляет понятные именованные методы для каждого варианта конфигурации:
class Pizza {
public:
std::string dough;
std::string sauce;
std::string topping;
bool cheese;
};
class PizzaBuilder {
private:
Pizza pizza;
public:
PizzaBuilder& setDough(const std::string& d) {
pizza.dough = d;
return *this; // Enable method chaining
}
PizzaBuilder& setSauce(const std::string& s) {
pizza.sauce = s;
return *this;
}
PizzaBuilder& setTopping(const std::string& t) {
pizza.topping = t;
return *this;
}
PizzaBuilder& addCheese() {
pizza.cheese = true;
return *this;
}
Pizza build() { return pizza; }
};
// Usage with method chaining
Pizza margherita = PizzaBuilder()
.setDough("thin")
.setSauce("tomato")
.setTopping("basil")
.addCheese()
.build();Каждый метод-сеттер возвращает return *this, что позволяет использовать цепочку вызовов методов (method chaining) для создания текучего интерфейса. Метод build() завершает процесс и возвращает сконструированный объект.
Используйте Строитель при создании объектов с множеством необязательных компонентов, когда вам нужен читаемый код инициализации или когда один и тот же процесс конструирования должен создавать различные представления.
Попробуйте сами
#include <iostream>
#include <string>
#include "ComputerBuilder.h"
using namespace std;
int main() {
// Чтение входных данных
string cpu, ram, storage, wifiChoice;
getline(cin, cpu);
getline(cin, ram);
getline(cin, storage);
getline(cin, wifiChoice);
// TODO: Создать экземпляр ComputerBuilder
// TODO: Использовать цепочку методов для установки cpu, ram и storage
// TODO: Если wifiChoice равен "yes", также вызвать addWifi()
// TODO: Вызвать build(), чтобы получить объект Computer
// TODO: Вызвать showSpecs() для собранного компьютера, чтобы отобразить конфигурацию
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер