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

نمط Observer

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 68 من 87.

يُعد Observer Pattern نمط تصميم سلوكي يحدد علاقة واحد إلى متعدد بين الكائنات. فعندما تتغير حالة كائن واحد (الـ subject)، يتم إخطار جميع التابعين له (الـ observers) وتحديثهم تلقائياً. فكر في الأمر كأنه اشتراك في نشرة إخبارية؛ حيث يتلقى المشتركون تحديثات كلما نُشر محتوى جديد.

يتضمن النمط مكونين رئيسيين: موضوع (subject) يحتفظ بقائمة من المراقبين (observers) ويخطرهم بالتغييرات، ومراقبون يحددون طريقة تحديث (update method) للاستجابة للإشعارات:

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class NewsPublisher {
    private List<Observer> subscribers = new ArrayList<>();
    
    public void subscribe(Observer observer) {
        subscribers.add(observer);
    }
    
    public void unsubscribe(Observer observer) {
        subscribers.remove(observer);
    }
    
    public void publish(String news) {
        for (Observer subscriber : subscribers) {
            subscriber.update(news);
        }
    }
}

class EmailSubscriber implements Observer {
    private String email;
    
    public EmailSubscriber(String email) {
        this.email = email;
    }
    
    public void update(String message) {
        System.out.println("Email to " + email + ": " + message);
    }
}

عندما ينشر الناشر الأخبار، يتم إخطار جميع المشتركين تلقائيًا:

NewsPublisher publisher = new NewsPublisher();
publisher.subscribe(new EmailSubscriber("alice@mail.com"));
publisher.subscribe(new EmailSubscriber("bob@mail.com"));

publisher.publish("Breaking news!");
// المخرجات:
// بريد إلكتروني إلى alice@mail.com: Breaking news!
// بريد إلكتروني إلى bob@mail.com: Breaking news!

يعزز نمط المراقب (Observer Pattern) الاقتران الضعيف—فالموضوع (subject) لا يحتاج إلى معرفة الفئات الملموسة (concrete classes) لمراقبيه، بل يحتاج فقط إلى معرفة أنها تنفذ واجهة Observer. وهذا يجعل من السهل إضافة أنواع مراقبين جديدة دون تعديل الموضوع.

challenge icon

التحدي

سهل

لنقم ببناء نظام تنبيه لأسعار الأسهم باستخدام نمط المراقب (Observer Pattern)! ستقوم بإنشاء نظام حيث يقوم مؤشر الأسهم بإخطار العديد من المستثمرين كلما تغير سعر السهم، مما يسمح لكل مستثمر بالتفاعل مع تحديثات الأسعار بطريقته الخاصة.

ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:

  • Observer.java: قم بتعريف واجهة Observer التي سينفذها جميع المستثمرين. يجب أن تعلن عن طريقة واحدة update(String stockName, double price) يستخدمها المراقبون لتلقي الإخطارات حول تغيرات الأسعار.
  • StockTicker.java: قم بإنشاء فئة الموضوع (subject class) التي تحتفظ بقائمة من المراقبين وتخطرهم بتغيرات الأسعار. يجب أن يحتوي StockTicker الخاص بك على:

    حقل String خاص لاسم السهم (يتم تعيينه عبر المنشئ).

    حقل double خاص للسعر الحالي (مبدئيًا 0).

    قائمة خاصة لتخزين المراقبين المشتركين.

    طريقة subscribe(Observer observer) تضيف مراقبًا إلى القائمة.

    طريقة unsubscribe(Observer observer) تزيل مراقبًا من القائمة.

    طريقة setPrice(double price) تقوم بتحديث السعر ثم تخطر جميع المراقبين عن طريق استدعاء طريقة update الخاصة بهم مع اسم السهم والسعر الجديد.

  • Investors.java: قم بإنشاء نوعين مختلفين من المستثمرين الذين ينفذون واجهة Observer:

    DayTrader - لديه حقل اسم خاص يتم تعيينه عبر المنشئ. عند التحديث، يطبع [name] received alert: [stockName] is now $[price] - Making quick trade!

    LongTermInvestor - لديه حقل اسم خاص يتم تعيينه عبر المنشئ. عند التحديث، يطبع [name] received alert: [stockName] is now $[price] - Holding steady.

    قم بتنسيق السعر بمنزلتين عشريتين.

  • Main.java: اجمع نظام المراقب الخاص بك معًا! ستتلقى ثلاثة مدخلات: اسم السهم (String)، وتحديث السعر الأول (double)، وتحديث السعر الثاني (double).

    قم بإنشاء StockTicker باسم السهم المعطى. قم بإنشاء DayTrader باسم "Alice" و LongTermInvestor باسم "Bob"، وقم بإشراك كليهما في المؤشر.

    قم بتعيين السعر إلى قيمة الإدخال الأولى (يتم إخطار جميع المراقبين). ثم قم بإلغاء اشتراك Alice. أخيرًا، قم بتعيين السعر إلى قيمة الإدخال الثانية (يجب إخطار Bob فقط هذه المرة).

ستتلقى ثلاثة مدخلات بالترتيب: اسم السهم (String)، السعر الأول (double)، السعر الثاني (double).

يوضح هذا التحدي قوة نمط المراقب (Observer Pattern): لا يعرف StockTicker أي شيء عن أنواع المستثمرين المحددة — فهو يعرف فقط أنهم ينفذون Observer. عندما تقوم بإلغاء اشتراك Alice، فإنها تتوقف عن تلقي التحديثات بينما يستمر إخطار Bob. هذا الاقتران الضعيف (loose coupling) يجعل من السهل إضافة أنواع مستثمرين جديدة دون تغيير المؤشر!

ورقة مرجعية

نمط المراقب (Observer Pattern) هو نمط تصميم سلوكي ينشئ علاقة واحد إلى متعدد بين الكائنات. عندما تتغير حالة كائن واحد (الـ subject)، يتم إخطار جميع التابعين له (الـ observers) وتحديثهم تلقائيًا.

يتضمن النمط مكونين رئيسيين:

  • Subject: يحتفظ بقائمة من المراقبين ويخطرهم بالتغييرات
  • Observers: يحددون طريقة تحديث (update method) للاستجابة للإخطارات

الهيكل الأساسي:

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class Subject {
    private List<Observer> observers = new ArrayList<>();
    
    public void subscribe(Observer observer) {
        observers.add(observer);
    }
    
    public void unsubscribe(Observer observer) {
        observers.remove(observer);
    }
    
    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

class ConcreteObserver implements Observer {
    public void update(String message) {
        // التفاعل مع الإخطار
    }
}

مثال على الاستخدام:

Subject subject = new Subject();
subject.subscribe(new ConcreteObserver());
subject.notifyObservers("State changed!");
// يتلقى جميع المراقبين المشتركين التحديث

يعزز نمط المراقب الارتباط الضعيف (loose coupling)—لا يحتاج الـ subject إلى معرفة الفئات الملموسة (concrete classes) لمراقبيه، بل يحتاج فقط إلى معرفة أنها تنفذ واجهة الـ Observer. وهذا يجعل من السهل إضافة أنواع مراقبين جديدة دون تعديل الـ subject.

جرّب بنفسك

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // قراءة المدخلات
        String stockName = scanner.nextLine();
        double firstPrice = scanner.nextDouble();
        double secondPrice = scanner.nextDouble();
        
        // TODO: إنشاء StockTicker باسم السهم المعطى
        
        // TODO: إنشاء DayTrader باسم "Alice"
        
        // TODO: إنشاء LongTermInvestor باسم "Bob"
        
        // TODO: تسجيل كل من Alice و Bob في الـ ticker
        
        // TODO: تعيين السعر إلى firstPrice (يتم إخطار كلا المراقبين)
        
        // TODO: إلغاء اشتراك Alice
        
        // TODO: تعيين السعر إلى secondPrice (يتم إخطار Bob فقط)
        
        scanner.close();
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming