Menu
Coddy logo textTech

תבנית העיצוב

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Java של Coddy. שיעור 72 מתוך 87.

תבנית הדקורטור היא תבנית עיצוב מבנית שמאפשרת לך להוסיף התנהגויות חדשות לאובייקטים באמצעות עטיפתם באובייקטים מיוחדים שנקראים דקורטורים. בניגוד לירושה, שמוסיפה התנהגות בזמן הקומפילציה, דקורטורים מוסיפים פונקציונליות באופן דינמי בזמן הריצה, מבלי לשנות את המחלקה המקורית.

דמיינו בית קפה שבו מתחילים עם קפה בסיסי ואפשר להוסיף תוספות כמו חלב, סוכר או קצפת. כל תוספת עוטפת את הקפה המקורי ומוסיפה לו את העלות ואת התיאור שלה. התבנית משתמשת בממשק משותף, כך שאפשר להתייחס לאובייקטים המעוטרים באותו אופן כמו לאובייקט המקורי:

interface Coffee {
    String getDescription();
    double getCost();
}

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }
    public double getCost() {
        return 2.0;
    }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    public String getDescription() {
        return coffee.getDescription() + ", Milk";
    }
    
    public double getCost() {
        return coffee.getCost() + 0.5;
    }
}

הדקורטור מחזיק הפניה לאובייקט העטוף ומעביר אליו קריאות, תוך הוספת התנהגות משלו. אפשר לערום כמה דקורטורים כדי לשלב תכונות:

Coffee order = new SimpleCoffee();
order = new MilkDecorator(order);
order = new SugarDecorator(order);

System.out.println(order.getDescription());  // קפה פשוט, חלב, סוכר
System.out.println(order.getCost());         // 2.75

תבנית הדקורטור מצטיינת כשצריך להוסיף אחריות לאובייקטים בלי ליצור התפוצצות של תתי־מחלקות. כל דקורטור הוא עצמאי, כך שאפשר לשלב ביניהם בחופשיות וליצור בדיוק את שילוב ההתנהגויות הדרוש לך.

challenge icon

אתגר

קל

בואו נבנה מערכת התראות באמצעות תבנית העיצוב Decorator! תיצור התראת בסיס שאפשר להרחיב באמצעות ערוצי מסירה שונים — התראות בדוא״ל, התראות SMS והתראות Push — על ידי עטיפת מעטרים זה בזה. כך פועלות מערכות בעולם האמיתי, שבהן ייתכן שצריך לשלוח הודעה אחת בכמה ערוצים בו־זמנית.

תארגן את הקוד שלך בארבעה קבצים:

  • Notifier.java: הגדר את הממשק Notifier, המשמש כממשק הרכיב של תבנית העיצוב שלך. עליו להצהיר על השיטה send(String message), שמטפלת בשליחת התראות.
  • BasicNotifier.java: צור את מחלקת הרכיב הקונקרטית שמממשת את Notifier. היא מייצגת את צורת ההתראה הפשוטה ביותר: התראה בתוך האפליקציה. כשקוראים ל-send, עליה להדפיס In-App: [message].
  • NotifierDecorators.java: בנה כאן את היררכיית המעטרים שלך. התחל במחלקה מופשטת NotifierDecorator שמממשת את Notifier ומחזיקה הפניה לאובייקט Notifier עטוף (שמועבר דרך הבנאי שלה).

    לאחר מכן, צור שלושה מעטרים קונקרטיים שיורשים מ-NotifierDecorator:

    EmailDecorator - כשקוראים ל-send, הוא תחילה מעביר את הקריאה למודיע העטוף, ואז מדפיס Email: [message].

    SMSDecorator - כשקוראים ל-send, הוא תחילה מעביר את הקריאה למודיע העטוף, ואז מדפיס SMS: [message].

    PushDecorator - כשקוראים ל-send, הוא תחילה מעביר את הקריאה למודיע העטוף, ואז מדפיס Push: [message].

  • Main.java: חבר את מערכת המעטרים שלך! יתקבלו שתי קלטים: הודעת ההתראה (String) ורשימה מופרדת בפסיקים של הערוצים להוספה (String), לדוגמה "email,sms" או "push,email,sms".

    התחל עם BasicNotifier. לאחר מכן, בהתאם לקלט הערוצים, עטוף אותו במעטרים המתאימים לפי הסדר שבו הם מופיעים. שמות הערוצים התקפים הם email, sms ו-push.

    לבסוף, קרא ל-send עם ההודעה שלך במודיע שעוטר במלואו.

יתקבלו שתי קלטים לפי הסדר: ההודעה לשליחה (String) והערוצים להפעלה (String, מופרדים בפסיקים).

לדוגמה, עבור הקלטים Server is down! ו-email,push, הפלט שלך יהיה:

In-App: Server is down!
Email: Server is down!
Push: Server is down!

שים לב שכל מעטר מוסיף התנהגות משלו תוך שמירה על ההתראה המקורית. סדר העטיפה קובע את סדר הפלט: הרכיב הפנימי ביותר (BasicNotifier) מתבצע ראשון, ואז כל מעטר מוסיף את השכבה שלו. אפשר לשלב כל צירוף של ערוצים בלי ליצור מחלקות נפרדות לכל צירוף!

נסו בעצמכם

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // קראו את ההודעה שיש לשלוח
        String message = scanner.nextLine();
        
        // קראו את רשימת הערוצים המופרדים בפסיקים (למשל, "email,sms" או "push,email,sms")
        String channelsInput = scanner.nextLine();
        
        // TODO: התחילו עם BasicNotifier
        
        // TODO: פצלו את קלט הערוצים לפי פסיקים
        
        // TODO: עברו על כל ערוץ ועטפו את רכיב ההתראות בדקורטור המתאים
        // הערוצים התקפים הם: "email", "sms", "push"
        
        // TODO: קראו ל-send של רכיב ההתראות לאחר שעטפתם אותו בכל הדקורטורים, והעבירו לו את ההודעה
        
    }
}
quiz iconבחנו את עצמכם

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

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

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