Event Bus ونمط Observer في C# (.NET): تنفيذ للخدمات المصغرة وواجهات المستخدم الرسومية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

Event Bus ونمط Observer في C# (.NET): من النظرية إلى التنفيذ

\n\n

في التطوير الحديث باستخدام C# (.NET)، غالباً ما تنشأ الحاجة لتنظيم التفاعل بين مكونات النظام مع الحفاظ على استقلاليتها. نمط Observer وتطوره — Event Bus (ناقل الأحداث) — هما أدوات أساسية لتحقيق الربط الضعيف (loose coupling). في هذه المقالة، سنشرح كيفية عمل هذه الآليات، وسننشئ تنفيذاً بسيطاً ولكن عملياً لـ Event Bus بلغة C#.

\n\n

نمط Observer: النهج الكلاسيكي

\n\n

يحدد نمط Observer علاقة "واحد إلى متعدد" بين الكائنات: عندما يغير كائن واحد (الناشر) حالته، يتم إخطار جميع الكائنات التابعة (المشتركين) تلقائياً. في .NET، هذا النمط مدمج من خلال الأحداث (event) والمفوضين.

\n\n

عيوب التنفيذ الكلاسيكي:

\n
    \n
  • اعتماد المشترك مباشرة على الناشر (الحاجة لتخزين مرجع للكائن).
  • \n
  • صعوبة إدارة الاشتراكات في الأنظمة الكبيرة.
  • \n
  • مشاكل مع الخيوط (تعدد المهام يتطلب مزامنة إضافية).
  • \n
\n\n

Event Bus: وسيط مركزي

\n\n

يحل Event Bus هذه المشاكل من خلال العمل كوسيط مركزي. بدلاً من الاشتراك مباشرة في كائن معين، تقوم المكونات بالتسجيل في ناقل الأحداث. ينشر الناشرون الأحداث في الناقل، ويقوم الناقل بتوزيعها على جميع المشتركين. هذا يطبق نمط Pub/Sub (نشر/اشتراك).

\n\n

مزايا Event Bus:

\n
    \n
  • الربط الضعيف: لا يعرف الناشرون والمشتركون شيئاً عن بعضهم البعض.
  • \n
  • قابلية التوسع: سهولة إضافة مشتركين جدد.
  • \n
  • العالمية: مناسب لواجهات المستخدم الرسومية (WPF/WinForms)، الخدمات المصغرة، والألعاب (Unity).
  • \n
\n\n

تنفيذ Event Bus في C# (.NET)

\n\n

سننشئ تنفيذاً أساسياً لـ Event Bus يدعم المعالجة غير المتزامنة والأحداث المُنمطة. لهذا، سنستخدم الواجهة IEvent والفئة العامة EventBus.

\n\n

الخطوة 1: تعريف الأنواع الأساسية.

\n
// الواجهة الأساسية لجميع الأحداث\npublic interface IEvent { }\n\n// مثال لحدث - رسالة مستخدم\npublic class UserCreatedEvent : IEvent\n{\n    public int UserId { get; }\n    public string Email { get; }\n\n    public UserCreatedEvent(int userId, string email)\n    {\n        UserId = userId;\n        Email = email;\n    }\n}
\n\n

الخطوة 2: إنشاء ناقل الأحداث.

\n
using System;\nusing System.Collections.Concurrent;\nusing System.Threading.Tasks;\n\npublic class EventBus\n{\n    // مخزن المشتركين: المفتاح - نوع الحدث، القيمة - قائمة المعالجات\n    private readonly ConcurrentDictionary<Type, ConcurrentBag<object>> _subscribers = new();\n\n    // الاشتراك في حدث\n    public void Subscribe<TEvent>(Func<TEvent, Task> handler) where TEvent : IEvent\n    {\n        var eventType = typeof(TEvent);\n        var handlers = _subscribers.GetOrAdd(eventType, _ => new ConcurrentBag<object>());\n        handlers.Add(handler);\n    }\n\n    // إلغاء الاشتراك (مبسط - إزالة جميع معالجات هذا النوع)\n    public void Unsubscribe<TEvent>() where TEvent : IEvent\n    {\n        _subscribers.TryRemove(typeof(TEvent), out _);\n    }\n\n    // نشر الحدث (غير متزامن)\n    public async Task PublishAsync<TEvent>(TEvent eventData) where TEvent : IEvent\n    {\n        if (!_subscribers.TryGetValue(typeof(TEvent), out var handlers))\n            return;\n\n        foreach (var handler in handlers)\n        {\n            if (handler is Func<TEvent, Task> typedHandler)\n            {\n                await typedHandler(eventData);\n            }\n        }\n    }\n}
\n\n

الخطوة 3: الاستخدام في التطبيق.

\n
public class                    

توصيات