Menu
Coddy logo textTech

תבנית Observer

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 94 מתוך 104.

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

התבנית כוללת שני תפקידים מרכזיים: נושא שמתחזק רשימה של צופים ומודיע להם, וצופים שמממשים ממשק עדכון:

#include <iostream>
#include <vector>
#include <algorithm>

class Observer {
public:
    virtual void update(int value) = 0;
    virtual ~Observer() = default;
};

class Subject {
    std::vector<Observer*> observers;
    int state = 0;
    
public:
    void attach(Observer* obs) {
        observers.push_back(obs);
    }
    
    void detach(Observer* obs) {
        observers.erase(
            std::remove(observers.begin(), observers.end(), obs),
            observers.end());
    }
    
    void setState(int value) {
        state = value;
        notify();
    }
    
    void notify() {
        for (Observer* obs : observers) {
            obs->update(state);
        }
    }
};

class Display : public Observer {
    std::string name;
public:
    Display(const std::string& n) : name(n) {}
    void update(int value) override {
        std::cout << name << " received: " << value << "\n";
    }
};

int main() {
    Subject sensor;
    Display screen1("Screen1"), screen2("Screen2");
    
    sensor.attach(&screen1);
    sensor.attach(&screen2);
    sensor.setState(42);  // שני הצגים קיבלו הודעה
}

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

השתמשו ב-Observer כאשר שינויים באובייקט אחד מחייבים לעדכן אובייקטים אחרים, ואינכם רוצים שהאובייקטים האלה יהיו מצומדים זה לזה באופן הדוק.

challenge icon

אתגר

קל

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

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

  • Observer.h: הגדירו את ממשק הצופה ואת צופה התצוגה הקונקרטי.

    צרו מחלקה מופשטת בשם Observer עם שיטה וירטואלית טהורה בשם update(double temperature), שהצופים יממשו כדי לקבל התראות, וכן עם הורס וירטואלי.

    לאחר מכן, צרו מחלקה בשם TemperatureDisplay שיורשת מ־Observer. לכל תצוגה יש שם (מחרוזת) שנקבע דרך הבנאי שלה. כשקוראים ל־update(), היא אמורה להדפיס:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h: צרו את הנושא שמנהל את הטמפרטורה ומודיע לצופים.

    המחלקה WeatherStation שלכם צריכה:

    • לשמור רשימה של מצביעים לצופים ואת הטמפרטורה הנוכחית (אתחלו אותה ל־0.0)
    • לכלול שיטה בשם attach(Observer* obs) לרישום צופים
    • לכלול שיטה בשם detach(Observer* obs) להסרת צופים
    • לכלול שיטה בשם setTemperature(double temp) שמעדכנת את הטמפרטורה ומודיעה לכל הצופים המצורפים
    • לכלול שיטה פרטית בשם notify() שקוראת ל־update() אצל כל צופה עם הטמפרטורה הנוכחית
  • main.cpp: הדגימו את תבנית Observer בפעולה.

    קראו שלושה ערכי קלט:

    1. שם לתצוגה הראשונה (מחרוזת)
    2. שם לתצוגה השנייה (מחרוזת)
    3. ערך טמפרטורה (double)

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

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

לדוגמה, עבור ערכי הקלט Kitchen, Bedroom ו־22.5:

Kitchen: Temperature is 22.5 degrees
Bedroom: Temperature is 22.5 degrees
Bedroom: Temperature is 27.5 degrees

עבור ערכי הקלט Office, Lobby ו־18.0:

Office: Temperature is 18 degrees
Lobby: Temperature is 18 degrees
Lobby: Temperature is 23 degrees

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

נסו בעצמכם

#include <iostream>
#include <string>
#include "WeatherStation.h"

using namespace std;

int main() {
    // קראו את הקלט
    string display1Name, display2Name;
    double temperature;
    
    cin >> display1Name;
    cin >> display2Name;
    cin >> temperature;
    
    // TODO: צרו אובייקט WeatherStation
    
    // TODO: צרו שני אובייקטים מסוג TemperatureDisplay עם השמות שהתקבלו בקלט
    
    // TODO: חברו את שני הצגים לתחנת מזג האוויר
    
    // TODO: הגדירו את הטמפרטורה לערך שהתקבל בקלט
    // (שני הצגים אמורים להדפיס את ההודעות שלהם)
    
    // TODO: נתקו את הצג הראשון
    
    // TODO: הגדירו את הטמפרטורה לערך הגבוה ב-5 מעלות מהערך שהתקבל בקלט
    // (רק הצג השני אמור להדפיס)
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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