Wzorzec Builder
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 93 z 104.
Wzorzec Builder oddziela tworzenie złożonego obiektu od jego reprezentacji, umożliwiając tworzenie różnych konfiguracji krok po kroku. Jest to szczególnie przydatne, gdy obiekt ma wiele opcjonalnych parametrów lub wymaga określonej kolejności tworzenia.
Zamiast konstruktora z wieloma parametrami (co może być mylące) Builder udostępnia przejrzyste, nazwane metody dla każdej opcji konfiguracji:
#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();
}Każda metoda ustawiająca zwraca referencję do konstruktora (return *this), umożliwiając łączenie metod w płynny interfejs. Metoda build() finalizuje i zwraca skonstruowany obiekt.
Użyj wzorca Builder podczas konstruowania obiektów z wieloma opcjonalnymi komponentami, gdy chcesz, aby kod tworzący obiekty był czytelny, lub gdy ten sam proces tworzenia powinien tworzyć różne reprezentacje.
Wyzwanie
ŁatwyZbudujmy system Computer Builder, który pozwala użytkownikom krok po kroku konfigurować niestandardowe komputery. To idealny przypadek do zastosowania wzorca Builder: komputery mają wiele opcjonalnych komponentów, a nam zależy na przejrzystym i czytelnym sposobie składania różnych konfiguracji.
Podzielisz kod na trzy pliki:
Computer.h: Zdefiniuj klasę produktu reprezentującą w pełni skonfigurowany komputer.Twoja klasa
Computerpowinna przechowywać następujące komponenty jako prywatne składowe:cpu(string),ram(string),storage(string),gpu(string) orazhasWifi(boolean). Domyślnie inicjalizujhasWifiwartościąfalse, a ciągi znaków pustymi wartościami.Dodaj publiczną metodę o nazwie
showSpecs(), która wyświetla konfigurację komputera. Dla każdego ustawionego komponentu (niepustego ciągu znaków) wypisz go w osobnym wierszu. Możliwość korzystania z Wi-Fi powinna być wyświetlana tylko wtedy, gdy jest włączona.ComputerBuilder.h: Utwórz klasę budującą komputery za pomocą płynnego interfejsu.Twoja klasa
ComputerBuilderpowinna mieć prywatną składową typuComputer, którą będzie rozbudowywać. Zaimplementuj poniższe metody. Każda z nich powinna zwracać referencję do obiektu buildera, aby umożliwić łączenie wywołań metod:setCPU(const std::string& cpu)setRAM(const std::string& ram)setStorage(const std::string& storage)setGPU(const std::string& gpu)addWifi(): włącza możliwość korzystania z Wi-Fi
Zaimplementuj również metodę
build(), która zwraca gotowy obiektComputeri resetuje builder, aby można go było ponownie użyć.main.cpp: Połącz wszystkie elementy, aby zbudować niestandardowy komputer.Wczytaj cztery dane wejściowe:
- Model procesora (string)
- Specyfikację pamięci RAM (string)
- Specyfikację pamięci masowej (string)
- Czy dołączyć Wi-Fi:
yeslubno
Użyj
ComputerBuilder, aby zbudować komputer z podanymi procesorem, pamięcią RAM i pamięcią masową. Jeśli wartością wejściową dla Wi-Fi jestyes, wywołaj równieżaddWifi(). Zwróć uwagę, że w tej konfiguracji nie podano karty graficznej: builder powinien poprawnie obsługiwać opcjonalne komponenty.Po zbudowaniu komputera wywołaj na nim metodę
showSpecs(), aby wyświetlić jego konfigurację.
Metoda showSpecs() powinna wyświetlać dane w następującym formacie (pokazując tylko ustawione komponenty):
CPU: [value]
RAM: [value]
Storage: [value]
GPU: [value]
Wifi: EnabledNa przykład dla danych wejściowych Intel i7-12700K, 32GB DDR5, 1TB NVMe SSD oraz yes:
CPU: Intel i7-12700K
RAM: 32GB DDR5
Storage: 1TB NVMe SSD
Wifi: EnabledDla danych wejściowych AMD Ryzen 5 5600X, 16GB DDR4, 512GB SSD oraz no:
CPU: AMD Ryzen 5 5600X
RAM: 16GB DDR4
Storage: 512GB SSDZwróć uwagę, że wzorzec Builder sprawia, iż proces tworzenia obiektu jest czytelny i elastyczny. Możesz z łatwością dodawać lub pomijać komponenty, a łączenie wywołań metod tworzy przejrzysty kod, który sam wyjaśnia swoje działanie. Wiersz dotyczący karty graficznej się nie pojawia, ponieważ jej nie ustawiliśmy. Pokazuje to, jak buildery w naturalny sposób obsługują opcjonalne komponenty.
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "ComputerBuilder.h"
using namespace std;
int main() {
// Wczytaj dane wejściowe
string cpu, ram, storage, wifiChoice;
getline(cin, cpu);
getline(cin, ram);
getline(cin, storage);
getline(cin, wifiChoice);
// TODO: Utwórz instancję ComputerBuilder
// TODO: Użyj łańcuchowego wywoływania metod, aby ustawić CPU, RAM i pamięć masową
// TODO: Jeśli wifiChoice ma wartość "yes", wywołaj także addWifi()
// TODO: Wywołaj build(), aby uzyskać obiekt Computer
// TODO: Wywołaj showSpecs() na zbudowanym komputerze, aby wyświetlić konfigurację
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników10Przegląd STL
Przegląd i filozofia STLKontenery STLIteratoryAlgorytmy STLFunktory i wyrażenia lambdaPowtórzenie – częstość występowania słów13Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonFabryka i fabryka abstrakcyjnaWzorzec BuilderWzorzec ObserverWzorzec Strategy2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online