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

نمط Observer

جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 92 من 110.

يُنشئ نمط المراقب (Observer pattern) علاقة واحد إلى متعدد بين الكائنات: فعندما تتغير حالة كائن واحد (الموضوع - subject)، يتم إخطار جميع التابعين له (المراقبين - observers) تلقائياً. يُعد هذا النمط مثالياً لسيناريوهات مثل تحديثات واجهة المستخدم، أو أنظمة الأحداث، أو أي حالة تحتاج فيها أجزاء متعددة من تطبيقك إلى التفاعل مع التغييرات.

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

abstract class Observer {
  void update(String message);
}

class NewsAgency {
  final List<Observer> _observers = [];
  String _latestNews = '';

  void subscribe(Observer observer) {
    _observers.add(observer);
  }

  void unsubscribe(Observer observer) {
    _observers.remove(observer);
  }

  void publishNews(String news) {
    _latestNews = news;
    for (var observer in _observers) {
      observer.update(_latestNews);
    }
  }
}

class NewsChannel implements Observer {
  final String name;
  NewsChannel(this.name);

  @override
  void update(String message) {
    print('$name received: $message');
  }
}

void main() {
  var agency = NewsAgency();
  var cnn = NewsChannel('CNN');
  var bbc = NewsChannel('BBC');

  agency.subscribe(cnn);
  agency.subscribe(bbc);

  agency.publishNews('Breaking news!');
  // CNN received: Breaking news!
  // BBC received: Breaking news!
}

عندما يتم استدعاء publishNews، تقوم الوكالة بالمرور عبر جميع المراقبين المشتركين وتستدعي طريقة update الخاصة بهم. يمكن للمراقبين الاشتراك أو إلغاء الاشتراك في أي وقت، مما يجعل النظام مرنًا وضعيف الارتباط - لا يحتاج الموضوع إلى معرفة الأنواع المحددة لمراقبيه.

challenge icon

التحدي

سهل

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

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

  • stock_system.dart: أنشئ نظام المراقب الخاص بك هنا:
    • فئة مجردة (abstract class) باسم Investor تحتوي على طريقة void onPriceChange(String stockName, double price)
    • فئة DayTrader التي تنفذ Investor. تحتوي على حقل name وطريقة onPriceChange الخاصة بها تطبع: [name] sees [stockName] at $[price] - Making quick trade!
    • فئة LongTermInvestor التي تنفذ Investor. تحتوي على حقل name وطريقة onPriceChange الخاصة بها تطبع: [name] notes [stockName] at $[price] - Holding steady.
    • فئة StockTicker (الموضوع) تحتوي على:
      • حقل String stockName
      • قائمة خاصة لتخزين المستثمرين المشتركين
      • طريقة subscribe(Investor investor) لإضافة مستثمر
      • طريقة unsubscribe(Investor investor) لإزالة مستثمر
      • طريقة updatePrice(double newPrice) تقوم بإخطار جميع المستثمرين المشتركين بالسعر الجديد
  • main.dart: استورد نظام الأسهم الخاص بك واستعرض نمط المراقب قيد التشغيل:
    • أنشئ StockTicker لسهم باسم DART
    • أنشئ DayTrader باسم Alice و LongTermInvestor باسم Bob
    • قم بإشراك كلا المستثمرين في المؤشر
    • قم بتحديث السعر إلى 150.50 (يجب إخطار كلا المستثمرين)
    • اطبع سطراً فارغاً
    • قم بإلغاء اشتراك Alice من المؤشر
    • قم بتحديث السعر إلى 155.75 (يجب إخطار Bob فقط الآن)

يوضح هذا كيف ينشئ نمط المراقب نظاماً مفكك الارتباط (loosely coupled) - لا يحتاج StockTicker إلى معرفة ما إذا كان يقوم بإخطار متداولين يوميين أو مستثمرين طويلي الأجل، فهو يستدعي فقط onPriceChange لكل من هو مشترك!

المخرجات المتوقعة:

Alice sees DART at $150.5 - Making quick trade!
Bob notes DART at $150.5 - Holding steady.

Bob notes DART at $155.75 - Holding steady.

ورقة مرجعية

يُنشئ نمط المراقب (Observer pattern) علاقة واحد إلى متعدد حيث يقوم الموضوع (subject) بإخطار جميع مراقبيه عندما تتغير حالته.

يحتوي هذا النمط على دورين رئيسيين:

  • الموضوع (Subject): يحتفظ بقائمة من المراقبين ويخطرهم بالتغييرات
  • المراقب (Observer): يسجل لدى الموضوع ويستجيب للتحديثات

التنفيذ الأساسي:

abstract class Observer {
  void update(String message);
}

class Subject {
  final List<Observer> _observers = [];

  void subscribe(Observer observer) {
    _observers.add(observer);
  }

  void unsubscribe(Observer observer) {
    _observers.remove(observer);
  }

  void notifyObservers(String data) {
    for (var observer in _observers) {
      observer.update(data);
    }
  }
}

class ConcreteObserver implements Observer {
  final String name;
  ConcreteObserver(this.name);

  @override
  void update(String message) {
    print('$name received: $message');
  }
}

الفوائد الرئيسية:

  • اقتران ضعيف (Loose coupling) بين الموضوع والمراقبين
  • يمكن للمراقبين الاشتراك أو إلغاء الاشتراك ديناميكيًا
  • لا يحتاج الموضوع إلى معرفة أنواع المراقبين المحددة

جرّب بنفسك

import 'stock_system.dart';

void main() {
  // TODO: 'DART' لمخزون يسمى StockTicker قم بإنشاء
  
  // TODO: باسم 'Alice' DayTrader قم بإنشاء
  
  // TODO: باسم 'Bob' LongTermInvestor قم بإنشاء
  
  // TODO: قم بإشراك كلا المستثمرين في مؤشر الأسعار
  
  // TODO: قم بتحديث السعر إلى 150.50 (يجب إخطار كلا المستثمرين)
  
  // TODO: قم بطباعة سطر فارغ
  
  // TODO: من مؤشر الأسعار Alice قم بإلغاء اشتراك
  
  // TODO: قم بتحديث السعر إلى 155.75 (يجب إخطار Bob فقط الآن)
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming