תבנית השיטה התבניתית
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 99 מתוך 104.
תבנית שיטת התבנית מגדירה את השלד של אלגוריתם במחלקת בסיס, ומאפשרת למחלקות יורשות לדרוס שלבים ספציפיים בלי לשנות את המבנה הכללי של האלגוריתם. בניגוד ל-Strategy, שמחליפה אלגוריתמים שלמים, שיטת התבנית משאירה את האלגוריתם קבוע ומאפשרת להתאים אישית שלבים בודדים.
מחלקת הבסיס מממשת את שיטת התבנית (האלגוריתם) וקוראת למתודות מופשטות או וירטואליות שעל מחלקות המשנה לספק:
#include <iostream>
class DataProcessor {
public:
// שיטת תבנית - מגדירה את שלד האלגוריתם
void process() {
loadData();
processData();
saveResults();
}
virtual ~DataProcessor() = default;
protected:
virtual void loadData() = 0; // חובה לממש
virtual void processData() = 0; // חובה לממש
// נקודת הרחבה - ניתן לדרוס, עם התנהגות ברירת מחדל
virtual void saveResults() {
std::cout << "Saving to default location\n";
}
};
class CSVProcessor : public DataProcessor {
protected:
void loadData() override {
std::cout << "Loading CSV file\n";
}
void processData() override {
std::cout << "Parsing CSV data\n";
}
};
class JSONProcessor : public DataProcessor {
protected:
void loadData() override {
std::cout << "Loading JSON file\n";
}
void processData() override {
std::cout << "Parsing JSON data\n";
}
void saveResults() override {
std::cout << "Saving to cloud storage\n";
}
};
int main() {
CSVProcessor csv;
csv.process(); // משתמש ב-saveResults המוגדר כברירת מחדל
JSONProcessor json;
json.process(); // משתמש ב-saveResults מותאם אישית
}השיטה process() היא שיטת התבנית — היא מגדירה רצף קבוע של שלבים. מחלקות משנה מממשות את loadData() ואת processData() באופן שונה, אך הסדר לעולם אינו משתנה. השיטה saveResults() היא "נקודת הרחבה" — יש לה מימוש ברירת מחדל שמחלקות משנה יכולות לבחור לדרוס.
השתמשו ב-Template Method כשיש לכם אלגוריתם עם שלבים קבועים, אך נדרשת גמישות בפעולות מסוימות, או כשאתם רוצים להימנע משכפול קוד בין מחלקות דומות.
אתגר
קלבואו נבנה מערכת ליצירת דוחות באמצעות תבנית Template Method. תיצרו מסגרת שבה סוגים שונים של דוחות (כמו דוחות מכירות ודוחות מלאי) פועלים לפי אותו תהליך יצירה, אך כל סוג דוח מתאים אישית שלבים מסוימים. זהו מקרה מושלם לשימוש ב-Template Method: תהליך העבודה הכולל נשאר קבוע, בעוד שהשלבים הבודדים משתנים.
תארגנו את הקוד בשלושה קבצים:
ReportGenerator.h: הגדירו מחלקת בסיס מופשטת עם מתודת התבנית.צרו מחלקה
ReportGeneratorשמגדירה את השלד של האלגוריתם ליצירת הדוח. מתודת התבנית שלכםgenerateReport()צריכה לבצע את השלבים האלה לפי הסדר:gatherData(): וירטואלית טהורה, יש לממש אותה במחלקות יורשותformatReport(): וירטואלית טהורה, יש לממש אותה במחלקות יורשותaddHeader(): נקודת הרחבה עם התנהגות ברירת מחדל שמדפיסה--- Report ---printReport(): וירטואלית טהורה, יש לממש אותה במחלקות יורשות
המתודה
addHeader()משמשת כנקודת הרחבה שמחלקות יורשות יכולות לדרוס כדי להתאים אישית את הכותרת.Reports.h: ממשו שתי מחלקות קונקרטיות ליצירת דוחות.צרו מחלקה
SalesReportשבה:gatherData()מדפיסהGathering sales data from databaseformatReport()מדפיסהFormatting sales figuresprintReport()מדפיסהSales Total: $[amount], כאשר amount מועבר לבנאי
צרו מחלקה
InventoryReportשבה:gatherData()מדפיסהScanning inventory recordsformatReport()מדפיסהOrganizing inventory by categoryaddHeader()דורס את נקודת ההרחבה כדי להדפיס=== Inventory Report ===printReport()מדפיסהItems in stock: [count], כאשר count מועבר לבנאי
שתי המחלקות צריכות לקבל את הערכים המתאימים להן (amount או count) דרך הבנאים שלהן.
main.cpp: הדגימו את תבנית Template Method.קראו שתי קלטים:
- כמות המכירות (מספר שלם)
- כמות הפריטים במלאי (מספר שלם)
צרו
SalesReportעם כמות המכירות והפיקו את הדוח. לאחר מכן צרוInventoryReportעם כמות הפריטים במלאי והפיקו את הדוח. הדפיסו שורה ריקה בין שני הדוחות כדי להקל על הקריאה.
לדוגמה, עם הקלטים 15000 ו-250:
Gathering sales data from database
Formatting sales figures
--- Report ---
Sales Total: $15000
Scanning inventory records
Organizing inventory by category
=== Inventory Report ===
Items in stock: 250עם הקלטים 8500 ו-120:
Gathering sales data from database
Formatting sales figures
--- Report ---
Sales Total: $8500
Scanning inventory records
Organizing inventory by category
=== Inventory Report ===
Items in stock: 120שימו לב ששני הדוחות פועלים לפי אותו רצף מדויק של שלבים שהוגדר ב-generateReport(), אך כל אחד מהם מממש את השלבים האלה באופן שונה. InventoryReport מדגימה גם דריסה של מתודת נקודת ההרחבה כדי להתאים אישית את הכותרת, בעוד ש-SalesReport משתמשת בהתנהגות ברירת המחדל. זוהי תבנית Template Method בפעולה: מבנה האלגוריתם מקובע במחלקת הבסיס, אך הפרטים גמישים.
נסו בעצמכם
#include <iostream>
#include "Reports.h"
using namespace std;
int main() {
// קראו את הקלטים
int salesAmount;
int inventoryCount;
cin >> salesAmount;
cin >> inventoryCount;
// TODO: צרו SalesReport עם salesAmount והפיקו אותו
// TODO: הדפיסו שורה ריקה בין הדוחות
// TODO: צרו InventoryReport עם inventoryCount והפיקו אותו
return 0;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP
קבצים חיצונייםבנייה וקומפילציה של C++קובצי Header וקובצי מקורמרחבי שמות והיקףמבוא ל-OOP ב-C++מחלקות לעומת אובייקטיםמצביע thisמתודות (פונקציות איבר)תכונות (חברי נתונים)יסודות הבנאים וההורסיםסיכום – מחשבון פשוט4מאפייני מחלקה
חברי מופע לעומת חברים סטטייםגטרים וסטריםפונקציות איבר קבועותמילת המפתח mutableמתודות ומשתנים סטטייםפונקציות ומחלקות ידידותסיכום – מנהל חשבונות בנק7ירושה
ירושה בסיסיתרמות גישה בירושהסדר הקריאות לבנאי ולמפרקדריסת מתודותפונקציות וירטואליות ו־VTableירושה מרובהירושה וירטואליתסיכום – היררכיית עובדים2ניהול זיכרון
זיכרון Stack לעומת Heapמצביעים והפניותזיכרון דינמי (new/delete)מצביעים חכמים ב-C++RAII ב-C++סיכום – מנהל מערכים דינמיים5כימוס
מצייני גישה ב-C++מצייני גישה לעומקהסתרת מידעמבנה לעומת מחלקהמחלקות מקוננות ופנימיותסיכום – מערכת רשומות תלמידים8רב־צורתיות
רב־צורתיות בזמן קומפילציה לעומת בזמן ריצההעמסת פונקציותפונקציות וירטואליות — מבט נוסףפונקציות וירטואליות טהורותמחלקות מופשטותתכנון ממשקים ב־C++המרה דינמית ו־RTTIסיכום — מחשבון צורות11מושגי OOP מתקדמים
הרכבה לעומת הורשהMixins באמצעות CRTPאידיום Pimplמחיקת טיפוסיםמחלקות Enum וטיפוסיות חזקהטיפול בחריגות ב-OOPהיררכיות חריגות מותאמות אישית14תבניות עיצוב חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובתבנית השיטה התבניתיתתבנית המצבתבנית המורכבRAII כתבנית3בנאים והורסים
בנאי ברירת מחדלבנאי עם פרמטריםבנאי העתקהבנאי העברהרשימות אתחול בבנאיםבנאים מאציליםמעמיקים בנושא הורסיםכלל השלושה / החמישה / האפססיכום – מחלקת String6העמסת אופרטורים
מבוא להעמסת אופרטוריםהעמסת אופרטורים אריתמטייםהעמסת אופרטורי השוואהאופרטורי זרםהעמסת אופרטור השמההעמסת האופרטורים [] ו־()אופרטורים להמרת טיפוסיםסיכום – מחלקת Matrix9תבניות
תבניות פונקציהתבניות מחלקההתמחות תבניותתבניות ארגומנטים משתנותיסודות SFINAE ותכונות טיפוסיםסיכום – מכל גנריתרגלו בעצמכם: קומפיילר C++ אונליין