Паттерн 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();
}Каждый метод-сеттер возвращает ссылку на builder (return *this), обеспечивая цепочку вызовов методов для fluent-интерфейса. Метод build() завершает процесс и возвращает созданный объект.
Используйте Builder при создании объектов с множеством необязательных компонентов, когда вам нужен читаемый код создания или когда один и тот же процесс создания должен создавать разные представления.
Задание
ЛегкоДавайте создадим систему Computer Builder, которая позволяет пользователям шаг за шагом настраивать пользовательские ПК. Это идеальный сценарий для паттерна Builder: компьютеры имеют множество необязательных компонентов, и мы хотим получить понятный и удобный для чтения способ собирать различные конфигурации.
Вы организуете свой код в трёх файлах:
Computer.h: определите product class, представляющий полностью настроенный компьютер.Ваш
Computerclass должен хранить эти компоненты как private-члены:cpu(строка),ram(строка),storage(строка),gpu(строка) иhasWifi(логическое значение). ИнициализируйтеhasWifiзначениемfalse, а строки по умолчанию — пустыми.Добавьте public method с именем
showSpecs(), который отображает конфигурацию компьютера. Для каждого установленного компонента (непустой строки) выведите его в отдельной строке. Возможность Wi‑Fi должна выводиться только в том случае, если она включена.ComputerBuilder.h: создайте builder class, который конструирует компьютеры с fluent-интерфейсом.Ваш
ComputerBuilderclass должен иметь private-член типаComputer, который он будет постепенно собирать. Implement следующие методы, каждый из которых возвращает ссылку на builder для Enable method chaining:setCPU(const std::string& cpu)setRAM(const std::string& ram)setStorage(const std::string& storage)setGPU(const std::string& gpu)addWifi(): enables возможность Wi‑Fi
Также Implement метод
build(), который возвращает completed объектComputerи сбрасывает builder для возможного повторного использования.main.cpp: объедините всё, чтобы собрать пользовательский компьютер.Прочитайте четыре входных значения:
- модель CPU (строка)
- спецификация RAM (строка)
- спецификация хранилища (строка)
- следует ли include Wi‑Fi:
yesилиno
Используйте
ComputerBuilder, чтобы создать компьютер с указанными CPU, RAM и хранилищем. Если значение Wi‑Fi на входе равноyes, также calladdWifi(). Обратите внимание, что в этой configuration GPU не указан: builder должен корректно обрабатывать необязательные компоненты.После сборки вызовите
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 делает процесс конструирования понятным и гибким. Вы можете легко добавить или пропустить компоненты, а chaining методов создаёт ясный, самодокументируемый код. Строка GPU не появляется, потому что мы никогда её не устанавливаем, что демонстрирует, как builder естественным образом обрабатывает необязательные компоненты.
Попробуйте сами
#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Итоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++