Factory ו־Abstract Factory
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 92 מתוך 104.
תבנית המפעל (Factory) מעבירה את יצירת האובייקטים לטיפול של מתודה או מחלקה נפרדת, וכך מאפשרת לך ליצור אובייקטים בלי לציין את המחלקה המדויקת שלהם. זה שימושי כאשר לוגיקת היצירה מורכבת או כאשר רוצים לנתק את קוד הלקוח ממימושים קונקרטיים.
מפעל פשוט משתמש בשיטה שמחזירה סוגים נגזרים שונים בהתאם לקלט:
#include <iostream>
#include <memory>
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// פונקציית מפעל
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}המפעל המופשט לוקח את הרעיון צעד נוסף קדימה, ויוצר משפחות של אובייקטים קשורים. במקום שיטת מפעל אחת, יש לך ממשק מפעל עם כמה שיטות יצירה:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// מפעל מופשט
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};השתמשו ב-Factory כשאתם זקוקים ליצירה גמישה של אובייקטים, וב-Abstract Factory כשאתם צריכים לוודא שאובייקטים קשורים פועלים יחד באופן עקבי.
אתגר
קלבואו נבנה מערכת ליצירת מסמכים באמצעות דפוסי התכנון Factory ו־Abstract Factory. תיצור מערכת שיכולה ליצור סוגים שונים של מסמכים (דוחות וחשבוניות) בפורמטי פלט שונים (PDF ו־HTML), וכך להדגים כיצד מפעלים מנתקים את יצירת האובייקטים מקוד הלקוח.
תארגן את הקוד שלך בארבעה קבצים:
Document.h: הגדר את היררכיית המוצרים של המסמכים שלך.צור מחלקת בסיס מופשטת
Documentעם מתודה וירטואלית טהורהgenerate()שמחזירה מחרוזת המתארת את המסמך שנוצר, ועם מפרק וירטואלי.לאחר מכן, צור ארבע מחלקות מסמכים קונקרטיות:
PDFReport:generate()מחזירה"Generating PDF Report"HTMLReport:generate()מחזירה"Generating HTML Report"PDFInvoice:generate()מחזירה"Generating PDF Invoice"HTMLInvoice:generate()מחזירה"Generating HTML Invoice"
DocumentFactory.h: ממש הן פונקציית מפעל פשוטה והן מפעל מופשט.ראשית, צור פונקציית מפעל פשוטה בשם
createDocumentשמקבלת שתי מחרוזות: סוג מסמך ("report"או"invoice") ופורמט ("pdf"או"html"). עליה להחזירstd::unique_ptr<Document>שמצביע למסמך הקונקרטי המתאים. החזרnullptrעבור צירופים לא תקינים.לאחר מכן, צור מחלקת מפעל מופשטת בשם
DocumentFactoryעם שתי מתודות וירטואליות טהורות:createReport(): מחזירהstd::unique_ptr<Document>createInvoice(): מחזירהstd::unique_ptr<Document>
ממש שני מפעלים קונקרטיים:
PDFFactory: יוצרתPDFReportו־PDFInvoiceHTMLFactory: יוצרתHTMLReportו־HTMLInvoice
DocumentService.h: צור שירות שמשתמש במפעל המופשט.הגדר מחלקה
DocumentServiceשמקבלתstd::unique_ptr<DocumentFactory>בבנאי שלה. צריכה להיות לה מתודה בשםgenerateAllDocuments()שמשתמשת במפעל כדי ליצור גם דוח וגם חשבונית, ואז מדפיסה את התוצאה של קריאה ל־generate()על כל אחד מהם, שורה אחת לכל תוצאה.main.cpp: הדגם את שתי גישות המפעל.קרא שתי קלטים:
- סוג מסמך (
reportאוinvoice) - פורמט (
pdfאוhtml)
ראשית, השתמש בפונקציית המפעל הפשוטה כדי ליצור מסמך על סמך הקלט. הדפס את התוצאה של קריאה ל־
generate()עליו.לאחר מכן, הדפס שורה ריקה.
בשלב הבא, צור
DocumentServiceעם המפעל המתאים לפי קלט הפורמט (השתמש ב־PDFFactoryעבורpdf, וב־HTMLFactoryעבורhtml). קרא ל־generateAllDocuments()כדי להראות כיצד המפעל המופשט יוצר משפחה של מסמכים קשורים.- סוג מסמך (
לדוגמה, עבור הקלטים report ו־pdf:
Generating PDF Report
Generating PDF Report
Generating PDF Invoiceעבור הקלטים invoice ו־html:
Generating HTML Invoice
Generating HTML Report
Generating HTML Invoiceשים לב כיצד המפעל הפשוט יוצר מסמך יחיד על סמך שני הפרמטרים, בעוד שהמפעל המופשט מבטיח שלכל המסמכים שנוצרים על ידי שירות יהיה אותו פורמט. זהו היתרון המרכזי של Abstract Factory כשצריך משפחות של אובייקטים קשורים שפועלים יחד באופן עקבי.
נסו בעצמכם
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// קראו את הקלטים
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: השתמשו בפונקציית המפעל הפשוט כדי ליצור מסמך
// בהתבסס על הקלטים documentType ו-format
// הדפיסו את התוצאה של הקריאה ל-generate() עליו
// הדפיסו שורה ריקה
cout << endl;
// TODO: צרו DocumentService עם המפעל המתאים
// בהתבסס על הקלט format (PDFFactory עבור "pdf", HTMLFactory עבור "html")
// קראו ל-generateAllDocuments() כדי להדגים את המפעל המופשט
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++ אונליין