Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط Observer

جزء من قسم البرمجة كائنية التوجه في رحلة C++ على Coddy. الدرس 94 من 104.

يعرّف نمط Observer تبعيةً من واحد إلى متعدد بين objects: عندما يغيّر أحد objects (وهو الموضوع) حالته، يتم إخطار جميع التابعين له (وهم المراقبون) تلقائيًا. وهذا مثالي لأنظمة الأحداث، وتحديثات واجهة المستخدم، أو أي سيناريو تحتاج فيه objects متعددة إلى التفاعل مع التغييرات.

يتضمن النمط دورين أساسيين: Subject الذي يحتفظ بقائمة من المراقبين ويُخطرهم، وObservers الذين ينفذون واجهة تحديث:

#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 عندما تتطلب التغييرات التي تطرأ على object واحد تحديث objects أخرى، ولا تريد أن تكون هذه objects مرتبطة ببعضها ارتباطًا وثيقًا.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا لمراقبة Weather Station باستخدام نمط Observer. ستنشئ محطة طقس تتعقب بيانات درجة الحرارة وتُخطر تلقائيًا وحدات عرض متعددة كلما تغيرت درجة الحرارة: وهو تطبيق واقعي كلاسيكي لهذا النمط.

ستنظم التعليمات البرمجية عبر ثلاثة ملفات:

  • Observer.h: عرّف واجهة المراقب ومراقب عرض ملموسًا.

    أنشئ صنف Observer مجردًا مع method افتراضية محضة update(double temperature) تنفذها المراقبات لتلقي الإشعارات، إلى جانب destructor افتراضي.

    ثم أنشئ صنف TemperatureDisplay يرث من Observer. لكل شاشة اسم (من النوع string) يُعيَّن من خلال constructor الخاص بها. عند استدعاء update()، ينبغي أن تطبع:

    [DisplayName]: Temperature is [temperature] degrees
  • WeatherStation.h: أنشئ subject يحتفظ بدرجة الحرارة ويُخطر المراقبين.

    ينبغي أن يقوم صنف WeatherStation بما يلي:

    • تخزين list من مؤشرات المراقبين ودرجة الحرارة الحالية (تهيئتها إلى 0.0)
    • امتلاك method باسم attach(Observer* obs) لتسجيل المراقبين
    • امتلاك method باسم detach(Observer* obs) لإزالة المراقبين
    • امتلاك method باسم setTemperature(double temp) تحدّث درجة الحرارة وتُخطر جميع المراقبين المرفقين
    • امتلاك method خاص باسم notify() يستدعي update() على كل مراقب باستخدام درجة الحرارة الحالية
  • main.cpp: وضّح نمط Observer أثناء العمل.

    اقرأ ثلاثة inputs:

    1. اسم الشاشة الأولى (من النوع string)
    2. اسم الشاشة الثانية (من النوع string)
    3. قيمة درجة حرارة (من النوع double)

    أنشئ WeatherStation وكائنين من TemperatureDisplay باستخدام الاسمين المُقدَّمين. أرفق كلتا الشاشتين بالمحطة، ثم اضبط درجة الحرارة على قيمة input. ينبغي أن تتلقى كلتا الشاشتين التحديث تلقائيًا وأن تطبعا رسائلهما.

    بعد ذلك، افصل الشاشة الأولى واضبط درجة حرارة جديدة أعلى من input بمقدار 5 درجات. ينبغي أن تتلقى الشاشة الثانية فقط هذا التحديث.

على سبيل المثال، باستخدام inputs Kitchen وBedroom و22.5:

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

وباستخدام inputs 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++ عبر الإنترنت