תבנית 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(מחרוזת),ram(מחרוזת),storage(מחרוזת),gpu(מחרוזת) ו-hasWifi(בוליאני). אתחלו אתhasWifiלערךfalse, ואת המחרוזות למחרוזות ריקות כברירת מחדל.הוסיפו מתודה ציבורית בשם
showSpecs()שמציגה את תצורת המחשב. עבור כל רכיב שהוגדר (מחרוזת שאינה ריקה), הדפיסו אותו בשורה משלו. יש להדפיס את יכולת ה-Wi-Fi רק אם היא מופעלת.ComputerBuilder.h: צרו את מחלקת הבנאי שבונה מחשבים באמצעות ממשק שוטף.למחלקת
ComputerBuilderשלכם צריך להיות חבר פרטי מסוגComputerשאותו היא בונה. ממשו את המתודות האלה, שכל אחת מהן מחזירה הפניה לבנאי כדי לאפשר שרשור מתודות: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: חברו את כל החלקים כדי לבנות מחשב מותאם אישית.קראו ארבעה קלטים:
- דגם המעבד (מחרוזת)
- מפרט ה-RAM (מחרוזת)
- מפרט האחסון (מחרוזת)
- האם לכלול Wi-Fi:
yesאוno
השתמשו ב-
ComputerBuilderכדי לבנות מחשב עם המעבד, ה-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 אינה מופיעה כי לא הגדרנו אותו, וכך מודגם כיצד בנאים מטפלים ברכיבים אופציונליים באופן טבעי.
נסו בעצמכם
#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 ואחסון
// TODO: אם wifiChoice הוא "yes", קרא גם ל-addWifi()
// TODO: קרא ל-build() כדי לקבל את האובייקט Computer
// TODO: קרא ל-showSpecs() על המחשב שנבנה כדי להציג את התצורה
return 0;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP
קבצים חיצונייםבנייה וקומפילציה של C++קובצי Header וקובצי מקורמרחבי שמות והיקףמבוא ל-OOP ב-C++מחלקות לעומת אובייקטיםמצביע thisמתודות (פונקציות איבר)תכונות (חברי נתונים)יסודות הבנאים וההורסיםסיכום – מחשבון פשוט4מאפייני מחלקה
חברי מופע לעומת חברים סטטייםגטרים וסטריםפונקציות איבר קבועותמילת המפתח mutableמתודות ומשתנים סטטייםפונקציות ומחלקות ידידותסיכום – מנהל חשבונות בנק7ירושה
ירושה בסיסיתרמות גישה בירושהסדר הקריאות לבנאי ולמפרקדריסת מתודותפונקציות וירטואליות ו־VTableירושה מרובהירושה וירטואליתסיכום – היררכיית עובדים10סקירה כללית של STL
סקירה כללית והפילוסופיה של STLקונטיינרים של STLאיטרטוריםאלגוריתמים של STLפונקטורים וביטויי למבדהסיכום – תדירות מילים13תבניות עיצוב חלק 1
מבוא לתבניות עיצובתבנית SingletonFactory ו־Abstract Factoryתבנית Builderתבנית Observerתבנית Strategy2ניהול זיכרון
זיכרון Stack לעומת Heapמצביעים והפניותזיכרון דינמי (new/delete)מצביעים חכמים ב-C++RAII ב-C++סיכום – מנהל מערכים דינמיים5כימוס
מצייני גישה ב-C++מצייני גישה לעומקהסתרת מידעמבנה לעומת מחלקהמחלקות מקוננות ופנימיותסיכום – מערכת רשומות תלמידים8רב־צורתיות
רב־צורתיות בזמן קומפילציה לעומת בזמן ריצההעמסת פונקציותפונקציות וירטואליות — מבט נוסףפונקציות וירטואליות טהורותמחלקות מופשטותתכנון ממשקים ב־C++המרה דינמית ו־RTTIסיכום — מחשבון צורות3בנאים והורסים
בנאי ברירת מחדלבנאי עם פרמטריםבנאי העתקהבנאי העברהרשימות אתחול בבנאיםבנאים מאציליםמעמיקים בנושא הורסיםכלל השלושה / החמישה / האפססיכום – מחלקת String6העמסת אופרטורים
מבוא להעמסת אופרטוריםהעמסת אופרטורים אריתמטייםהעמסת אופרטורי השוואהאופרטורי זרםהעמסת אופרטור השמההעמסת האופרטורים [] ו־()אופרטורים להמרת טיפוסיםסיכום – מחלקת Matrix9תבניות
תבניות פונקציהתבניות מחלקההתמחות תבניותתבניות ארגומנטים משתנותיסודות SFINAE ותכונות טיפוסיםסיכום – מכל גנריתרגלו בעצמכם: קומפיילר C++ אונליין