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

نمط Observer

جزء من قسم البرمجة كائنية التوجه في رحلة Java على Coddy. الدرس 68 من 87.

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

يتضمن النمط مكوّنين رئيسيين: موضوعًا يحتفظ بقائمة من المراقبين ويُخطرهم بالتغييرات، ومراقبين يعرّفون أسلوب تحديث للاستجابة للإشعارات:

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 على الاقتران غير المحكم: لا يحتاج الموضوع إلى معرفة الفئات الملموسة للمراقبين، بل يحتاج فقط إلى معرفة أنها تطبّق الواجهة Observer. وهذا يجعل من السهل إضافة أنواع جديدة من المراقبين دون تعديل الموضوع.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا للتنبيه عند تغيّر أسعار الأسهم باستخدام نمط المراقب! ستنشئ نظامًا يُخطر فيه شريط أسعار الأسهم عدة مستثمرين كلما تغيّر سعر السهم، مما يتيح لكل مستثمر التفاعل مع تحديثات السعر بطريقته الخاصة.

ستُنظّم شيفرتك عبر أربعة ملفات:

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

    حقل String خاص لاسم السهم (يُحدَّد عبر constructor).

    حقل double خاص للسعر الحالي (قيمته الأولية 0).

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

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

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

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

  • Investors.java: أنشئ نوعين مختلفين من المستثمرين يطبّقان الواجهة Observer:

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

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

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

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

    أنشئ StockTicker باستخدام اسم السهم المعطى. أنشئ DayTrader باسم "Alice" وLongTermInvestor باسم "Bob"، واشترك بهما مع شريط الأسعار.

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

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

يوضّح هذا التحدي قوة نمط المراقب: لا يعرف StockTicker شيئًا عن أنواع المستثمرين المحددة. إنه يعرف فقط أنها تطبّق Observer. عندما تلغي اشتراك Alice، تتوقف عن تلقي التحديثات بينما يستمر Bob في تلقي الإشعارات. يجعل هذا الاقتران المرن من السهل إضافة أنواع جديدة من المستثمرين دون تغيير شريط الأسعار!

جرّب بنفسك

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اختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Java عبر الإنترنت