Menu
Coddy logo textTech

תבנית השיטה התבניתית

חלק מהיחידה תכנות מונחה עצמים במסלול ה-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 כשיש לכם אלגוריתם עם שלבים קבועים, אך נדרשת גמישות בפעולות מסוימות, או כשאתם רוצים להימנע משכפול קוד בין מחלקות דומות.

challenge icon

אתגר

קל

בואו נבנה מערכת ליצירת דוחות באמצעות תבנית Template Method. תיצרו מסגרת שבה סוגים שונים של דוחות (כמו דוחות מכירות ודוחות מלאי) פועלים לפי אותו תהליך יצירה, אך כל סוג דוח מתאים אישית שלבים מסוימים. זהו מקרה מושלם לשימוש ב-Template Method: תהליך העבודה הכולל נשאר קבוע, בעוד שהשלבים הבודדים משתנים.

תארגנו את הקוד בשלושה קבצים:

  • ReportGenerator.h: הגדירו מחלקת בסיס מופשטת עם מתודת התבנית.

    צרו מחלקה ReportGenerator שמגדירה את השלד של האלגוריתם ליצירת הדוח. מתודת התבנית שלכם generateReport() צריכה לבצע את השלבים האלה לפי הסדר:

    1. gatherData(): וירטואלית טהורה, יש לממש אותה במחלקות יורשות
    2. formatReport(): וירטואלית טהורה, יש לממש אותה במחלקות יורשות
    3. addHeader(): נקודת הרחבה עם התנהגות ברירת מחדל שמדפיסה --- Report ---
    4. printReport(): וירטואלית טהורה, יש לממש אותה במחלקות יורשות

    המתודה addHeader() משמשת כנקודת הרחבה שמחלקות יורשות יכולות לדרוס כדי להתאים אישית את הכותרת.

  • Reports.h: ממשו שתי מחלקות קונקרטיות ליצירת דוחות.

    צרו מחלקה SalesReport שבה:

    • gatherData() מדפיסה Gathering sales data from database
    • formatReport() מדפיסה Formatting sales figures
    • printReport() מדפיסה Sales Total: $[amount], כאשר amount מועבר לבנאי

    צרו מחלקה InventoryReport שבה:

    • gatherData() מדפיסה Scanning inventory records
    • formatReport() מדפיסה Organizing inventory by category
    • addHeader() דורס את נקודת ההרחבה כדי להדפיס === Inventory Report ===
    • printReport() מדפיסה Items in stock: [count], כאשר count מועבר לבנאי

    שתי המחלקות צריכות לקבל את הערכים המתאימים להן (amount או count) דרך הבנאים שלהן.

  • main.cpp: הדגימו את תבנית Template Method.

    קראו שתי קלטים:

    1. כמות המכירות (מספר שלם)
    2. כמות הפריטים במלאי (מספר שלם)

    צרו 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;
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר C++ אונליין