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

نمط المراقب (Events)

جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 54 من 70.

يُنشئ نمط المراقب (Observer pattern) علاقة واحد إلى متعدد حيث يقوم كائن واحد (الموضوع) بإخطار عدة كائنات تابعة (المراقبين) عندما تتغير حالته. في لغة C#، توفر الـ events طريقة مدمجة لتنفيذ هذا النمط بأناقة.

يتضمن هذا النمط ناشرًا يقوم بإصدار الأحداث ومشتركين يستجيبون لها. إليك مثال عملي لمراقب أسعار الأسهم:

public class Stock
{
    private decimal _price;
    
    public event Action<decimal> PriceChanged;
    
    public decimal Price
    {
        get => _price;
        set
        {
            _price = value;
            PriceChanged?.Invoke(_price);  // إخطار جميع المراقبين
        }
    }
}

public class PriceAlert
{
    public void OnPriceChanged(decimal newPrice)
    {
        Console.WriteLine($"Alert: Price is now {newPrice}");
    }
}

public class PriceLogger
{
    public void OnPriceChanged(decimal newPrice)
    {
        Console.WriteLine($"Logged: {newPrice}");
    }
}

يربط المشتركون دوالهم بالحدث باستخدام += ويمكنهم فك الارتباط بـ -=:

var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();

stock.PriceChanged += alert.OnPriceChanged;
stock.PriceChanged += logger.OnPriceChanged;

stock.Price = 150.50m;
// المخرجات:
// تنبيه: السعر الآن هو 150.50
// تم التسجيل: 150.50

stock.PriceChanged -= logger.OnPriceChanged;  // إلغاء الاشتراك
stock.Price = 155.00m;
// المخرجات: تنبيه: السعر الآن هو 155.00

يعزز نمط المراقب (Observer pattern) الاقتران الضعيف - لا تحتاج فئة Stock إلى معرفة أي شيء عن مراقبيها. يمكن للمراقبين الجدد الاشتراك دون تعديل الناشر، مما يجعل النظام سهل التوسيع والصيانة.

challenge icon

التحدي

سهل

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

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

  • TemperatureSensor.cs: قم بإنشاء فئة TemperatureSensor في مساحة الأسماء Monitoring. هذا هو الناشر الخاص بك - الموضوع الذي ستراقبه الكائنات الأخرى. يجب أن يحتوي المستشعر الخاص بك على:
    • حقل خاص لتخزين درجة الحرارة الحالية
    • حدث يسمى TemperatureChanged يستخدم Action<double> لإخطار المشتركين بدرجة الحرارة الجديدة
    • خاصية Temperature تقوم برفع الحدث كلما تم تعيين درجة الحرارة إلى قيمة جديدة
  • Observers.cs: قم بإنشاء فئتي مراقب في نفس مساحة الأسماء التي ستشترك في تغييرات درجة الحرارة:
    • DisplayUnit - لديها طريقة OnTemperatureChanged(double temp) تطبع Display: {temp}C
    • AlarmSystem - لديها طريقة OnTemperatureChanged(double temp) تطبع Alarm Check: {temp}C
  • Program.cs: اجمع كل شيء معاً عن طريق إنشاء مستشعر وكلا المراقبين. قم بإشراك كلا المراقبين في حدث المستشعر، ثم قم بتحديث درجة الحرارة. بعد ذلك، قم بإلغاء اشتراك نظام الإنذار وتحديث درجة الحرارة مرة أخرى لإظهار أن وحدة العرض فقط هي التي تتلقى الإخطار.

ستتلقى مدخلين:

  • قراءة درجة الحرارة الأولى (على سبيل المثال، 22.5)
  • قراءة درجة الحرارة الثانية (على سبيل المثال، 28.0)

يجب أن يقوم برنامجك بما يلي:

  1. إنشاء المستشعر وكلا المراقبين
  2. إشراك كلا المراقبين في الحدث TemperatureChanged
  3. تعيين درجة حرارة المستشعر إلى قيمة الإدخال الأولى (يجب إخطار كلا المراقبين)
  4. إلغاء اشتراك AlarmSystem من الحدث
  5. تعيين درجة حرارة المستشعر إلى قيمة الإدخال الثانية (يجب إخطار وحدة العرض فقط)

على سبيل المثال، إذا كانت المدخلات هي 22.5 و 28.0، يجب أن يكون الناتج:

Display: 22.5C
Alarm Check: 22.5C
Display: 28C

يوضح هذا القوة الأساسية لنمط المراقب - فالمستشعر لا يعرف أو يهتم بمن يستمع إليه. يمكن للمراقبين الاشتراك وإلغاء الاشتراك ديناميكياً، ويقوم الناشر ببساطة بإخطار كل من يستمع حالياً!

ورقة مرجعية

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

في لغة C#، توفر الأحداث (events) طريقة مدمجة لتنفيذ هذا النمط. يتضمن النمط ناشراً (publisher) يقوم بإرسال الأحداث ومشتركين (subscribers) يستجيبون لها.

التنفيذ الأساسي للأحداث

قم بتعريف حدث باستخدام event Action<T> واستدعائه عند تغير الحالة:

public class Stock
{
    private decimal _price;
    
    public event Action<decimal> PriceChanged;
    
    public decimal Price
    {
        get => _price;
        set
        {
            _price = value;
            PriceChanged?.Invoke(_price);  // Notify all observers
        }
    }
}

إنشاء المراقبين

المراقبون هم فئات (classes) تحتوي على دوال تتطابق مع توقيع الحدث:

public class PriceAlert
{
    public void OnPriceChanged(decimal newPrice)
    {
        Console.WriteLine($"Alert: Price is now {newPrice}");
    }
}

public class PriceLogger
{
    public void OnPriceChanged(decimal newPrice)
    {
        Console.WriteLine($"Logged: {newPrice}");
    }
}

الاشتراك وإلغاء الاشتراك

استخدم += للاشتراك و -= لإلغاء الاشتراك من الأحداث:

var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();

stock.PriceChanged += alert.OnPriceChanged;    // Subscribe
stock.PriceChanged += logger.OnPriceChanged;   // Subscribe

stock.Price = 150.50m;  // Both observers notified

stock.PriceChanged -= logger.OnPriceChanged;   // Unsubscribe

stock.Price = 155.00m;  // Only alert notified

يعزز نمط المراقب (Observer pattern) الاقتران الضعيف (loose coupling) - حيث لا يحتاج الناشر إلى معرفة أي شيء عن مراقبيه. يمكن للمراقبين الجدد الاشتراك دون تعديل الناشر.

جرّب بنفسك

using System;
using Monitoring;

class Program
{
    public static void Main(string[] args)
    {
        // قراءة درجات الحرارة المدخلة
        double temp1 = Convert.ToDouble(Console.ReadLine());
        double temp2 = Convert.ToDouble(Console.ReadLine());

        // TODO: TemperatureSensor إنشاء مثيل لـ

        // TODO: AlarmSystem و DisplayUnit إنشاء مثيلات لـ

        // TODO: TemperatureChanged تسجيل كلا المراقبين في حدث

        // TODO: (يتم إخطار كلا المراقبين) temp1 تعيين درجة حرارة المستشعر إلى

        // TODO: من الحدث AlarmSystem إلغاء اشتراك

        // TODO: (يتم إخطار وحدة العرض فقط) temp2 تعيين درجة حرارة المستشعر إلى
    }
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming