نمط المراقب (Observer)
نمط المراقب (Observer)
يُعرِّف نمط المراقب علاقة تبعية من نوع "واحد-إلى-متعدد" بين الكائنات: حين يغيّر كائن ما (يُسمّى الموضوع أو الناشر) حالتَه، يُخطَر جميع التابعون له (المراقبون أو المستمعون) تلقائيًا. هذا هو النمط الأساسي الذي يقوم عليه كل نظام قائم على الأحداث استخدمتَه من قبل — من مستمعي Swing ورد فعل Android، إلى أحداث تطبيق Spring والتدفقات التفاعلية.
المشكلة التي يحلّها
بدون نمط المراقب، يضطر الموضوع الذي يريد إخطار الآخرين إلى الاحتفاظ بمراجع مُرمَّجة لكل طرف مهتم. أضِف مستهلكًا جديدًا وستضطر إلى تعديل الموضوع — وهذا انتهاك صريح لمبدأ الفتح/الإغلاق. يُفصِل نمط المراقب منتج الأحداث عن مستهلكيها، فيستطيع كل طرف التطور باستقلالية.
البنية الكلاسيكية لـ GoF في Java
يتطلب الحد الأدنى واجهتين وموضوعًا واحدًا ملموسًا:
يخزّن الموضوع الملموس مراقبيه في قائمة ويُوزّع كل إشعار عليهم:
List.copyOf(observers) قبل الحلقة يمنع ConcurrentModificationException حين يُلغي مراقب تسجيلَ نفسه استجابةً لاستدعائه — وهو سيناريو شائع جدًا في الإنتاج.
مثال عملي: معالجة الطلبيات
تخيّل خدمة تجارة إلكترونية حيث يجب أن يُطلق تسجيل طلب جديد عدة تأثيرات جانبية — إرسال بريد تأكيد، وتحديث المخزون، وتسجيل حدث تحليلي. مع نمط المراقب يُسجّل كل اهتمام باستقلالية:
خدمة OrderService التي تستدعي notifyObservers لا تعرف شيئًا عن البريد الإلكتروني أو المخزون أو التحليلات. أضِف تأثيرًا جانبيًا جديدًا بكتابة صف جديد فحسب — دون أي تغيير في الكود الموجود.
حافلة الأحداث ذات النوع المحكم والموضوعات المفلترة
نادرًا ما تستخدم أنظمة الإنتاج قائمة مراقبين واحدة. تحسين شائع هو تعيين أنواع الأحداث لقوائم مستمعين منفصلة، متجنبًا بذلك رقصة instanceof داخل كل مراقب:
استخدام Consumer<T> مع lambda يُزيل الحاجة إلى كتابة صفوف مراقب منفصلة للتفاعلات البسيطة — وهذا أقرب بكثير لكيفية تعامل أطر العمل الحديثة مع الأحداث داخليًا.
دعم Java المدمج للمراقب
شحنت Java دعمًا للمراقب بأشكال متعددة على مرّ السنين:
java.util.Observer/java.util.Observable— مُهمَلان منذ Java 9. تجنّبهما في الكود الجديد: سلامة الخيوط معطوبة والتصميم غير مرن.- java.beans PropertyChangeSupport — لا يزال يُستخدم في كود سطح المكتب/Swing لإشعارات تغيير الخصائص.
- Flow API (
java.util.concurrent.Flow) — تدفقات تفاعلية من Java 9+:PublisherوSubscriberوSubscriptionوProcessor. واعية بالضغط الخلفي؛ الأساس الحديث للبرمجة التفاعلية. - Spring ApplicationEvent — يلفّ Spring نمط المراقب خلف
ApplicationEventPublisherو@EventListener، مقدمًا معالجة أحداث تصريحية قادرة على العمل غير المتزامن مع حقن الاعتمادية.
java.util.Observable أبدًا. أُهمِل في Java 9 لأسباب وجيهة: الإشعار غير آمن للخيوط، وتصميمه يربط المراقبين بصف أساسي ملموس بدلًا من واجهة. استخدم Flow API أو حافلة أحداث مخصصة أو إطار عمل مثل Spring Events.
اعتبارات الخيوط والذاكرة
تتفاجأ معظم الفرق بمشكلتين في بيئة الإنتاج:
- سلامة الخيوط. إذا سجّل مراقبون أو أُزيلوا من خيوط متعددة، فلا بد من حماية القائمة. استبدل
ArrayListبـCopyOnWriteArrayListللحصول على بديل آمن للخيوط خالٍ من الأقفال يحلّ مشكلة النسخة تلقائيًا أيضًا. - تسرّب الذاكرة. موضوع يحتفظ بمراجع قوية للمراقبين يُبقيهم أحياء إلى أجل غير مسمى. أُطر واجهة المستخدم (Swing, JavaFX, Android) مشهورة بهذا: تسجّل مستمعًا في activity/fragment دون إلغاء تسجيله قط، فتظل التسلسل الهرمي للعرض بأكمله في الذاكرة. اقرن دائمًا
addObserverبـremoveObserverمقابلة في أسلوب إيقاف دورة الحياة.
المقايضات ومتى تستخدم نمط المراقب
- استخدمه عندما يجب أن تتفاعل مكوّنات متعددة غير مترابطة مع تغيّرات الحالة وتريد إبقاء الناشر جاهلًا بمستهلكيه.
- احذر من عواصف الإشعارات المتتالية — مراقب يُطلق حدثًا آخر قد يخلق سلاسل يصعب تتبّعها. سجّل إرسال الأحداث في بيئة التطوير.
- فضّل الحافلات ذات الأنواع المحكمة أو أُطر العمل (Spring Events, Guava EventBus, RxJava) على الحافلات المبنية يدويًا بمفاتيح نصية في الإنتاج — فهي تضيف أمانًا وقت الترجمة ودعمًا للعمل غير المتزامن.
- فكّر في التدفقات التفاعلية (Project Reactor, RxJava) حين تحتاج إلى ضغط خلفي أو تركيب أو خطوط معالجة غير متزامنة معقدة؛ نمط المراقب بذاته هو إطلاق ونسيان.
الخلاصة
نمط المراقب هو محرّك البنية القائمة على الأحداث. جوهره عقد بين ناشر لا يعرف من يستمع وأي عدد من المستمعين لا يعرف أحدهم الآخر. في Java الحديثة يتجلّى ذلك في حافلات الأحداث القائمة على lambda، وواجهة Flow API، والتجريدات على مستوى أُطر العمل مثل Spring Events. المهارات الاحترافية الأساسية هي: الحفاظ على سلامة قائمة المراقبين للخيوط، ومنع تسرّب الذاكرة بإلغاء التسجيل بانضباط، ومعرفة متى تلجأ إلى تجريد تفاعلي أعلى مستوى بدلًا من بناء حافلتك الخاصة.