الفرق بين المراجعتين ل"Design Patterns/chain of responsibility"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(2.2 محتوى)
(2.3 محتوى)
سطر 167: سطر 167:
 
# لتلافي تكرار الشيفرة الأساسية في المداوِلات الحقيقية، سيكون من المجدي إنشاء فئة مداوِل أساسي مجرد (Abstract Base Handler)، متفرعة من واجهة المداوِل. ويجب أن يكون في تلك الفئة حقلٌ لتخزين مرجع إلى المداوِل التالي في السلسلة، ولا تنس جعل الفئة غير قابلة للتغيير (immutable)، لكن إن كنت تريد تعديل السلاسل أثناء وقت التشغيل فستحتاج إلى تعريف محدِّد (Setter) لتغيير قيمة الحقل المرجعي. كذلك تستطيع أيضًا تطبيق السلوك الافتراضي المناسب لأسلوب المعالجة، بأن توجه الطلب إلى الكائن التالي إلا أن يكون هو الكائن الأخير، وستكون المداوِلات الحقيقية (Concrete Handlers) قادرة على استخدام هذا السلوك من خلال استدعاء الأسلوب الأم.
 
# لتلافي تكرار الشيفرة الأساسية في المداوِلات الحقيقية، سيكون من المجدي إنشاء فئة مداوِل أساسي مجرد (Abstract Base Handler)، متفرعة من واجهة المداوِل. ويجب أن يكون في تلك الفئة حقلٌ لتخزين مرجع إلى المداوِل التالي في السلسلة، ولا تنس جعل الفئة غير قابلة للتغيير (immutable)، لكن إن كنت تريد تعديل السلاسل أثناء وقت التشغيل فستحتاج إلى تعريف محدِّد (Setter) لتغيير قيمة الحقل المرجعي. كذلك تستطيع أيضًا تطبيق السلوك الافتراضي المناسب لأسلوب المعالجة، بأن توجه الطلب إلى الكائن التالي إلا أن يكون هو الكائن الأخير، وستكون المداوِلات الحقيقية (Concrete Handlers) قادرة على استخدام هذا السلوك من خلال استدعاء الأسلوب الأم.
 
# أنشئ فئات فرعية للمداوِلات الحقيقية واحدة تلو الأخرى، ثم طبِّق أساليب المعالجة الخاصة بها، يجب أن يتخذ كل مداوِل قرارين عند استلام الطلب:
 
# أنشئ فئات فرعية للمداوِلات الحقيقية واحدة تلو الأخرى، ثم طبِّق أساليب المعالجة الخاصة بها، يجب أن يتخذ كل مداوِل قرارين عند استلام الطلب:
#* هل سيعالج الطلب؟
+
#* هل سيعالِج الطلب؟
#* هل سيمرر الطلب في السلسلة؟
+
#* هل سيمرِّر الطلب في السلسلة؟
 
# قد يجمِّع العميل سلاسل بنفسه أو يستلم سلاسل مبنية مسبقًا من كائنات أخرى، وفي الحالة الثانية فيجب أن تستخدم بعض فئات المصنع (factory classes) من أجل بناء سلاسل وفقًا لإعدادات التهيئة أو البيئة.
 
# قد يجمِّع العميل سلاسل بنفسه أو يستلم سلاسل مبنية مسبقًا من كائنات أخرى، وفي الحالة الثانية فيجب أن تستخدم بعض فئات المصنع (factory classes) من أجل بناء سلاسل وفقًا لإعدادات التهيئة أو البيئة.
 
# يستطيع العميل أن ينبِّه (trigger) أي مداوِل في السلسلة، وليس شرطًا أن ينبه المداوِل الأساسي فقط، وسيمرَّر الطلب في السلسلة حتى يرفض أحد المداوِلات أن يمرر الطلب لما بعده في السلسلة أو حتى يصل إلى نهاية السلسلة.
 
# يستطيع العميل أن ينبِّه (trigger) أي مداوِل في السلسلة، وليس شرطًا أن ينبه المداوِل الأساسي فقط، وسيمرَّر الطلب في السلسلة حتى يرفض أحد المداوِلات أن يمرر الطلب لما بعده في السلسلة أو حتى يصل إلى نهاية السلسلة.
سطر 187: سطر 187:
  
 
== العلاقات مع الأنماط الأخرى ==
 
== العلاقات مع الأنماط الأخرى ==
 +
# تختلف أنماط [[Design Patterns/chain of responsibility|سلسلة المسؤوليات (Chain of Responsibility)]] و<nowiki/>[[الأمر (Command)]] و<nowiki/>[[Design Patterns/mediator|الوسيط (Mediator)]] و<nowiki/>[[Design Patterns/observer|المراقب (Observer)]] في طرق توصيل مستقبلي الطلبات ومرسليها ببعضهم، وترى ذلك الاختلاف فيما يلي:
 +
#* تمرِّر سلسلة المسؤوليات الطلب بشكل تسلسلي في سلسلة مرنة من المستقبلين المحتملين إلى أن يعالج أحدهم الطلب.
 +
#* ينشئ نمط الأمر وصلات أحادية الاتجاه (Unidirectional) بين المرسلين والمستقبلين.
 +
#* يلغي نمط الوسيط الاتصالات المباشرة بين المرسلين والمستقبلين مجبرًا إياهم على التواصل بشكل غير مباشر من خلال كائن وسيط.
 +
#* يسمح نمط المراقب للمستقبلين بالاشتراك في استلام الطلبات وكذلك إلغاء الاشتراك بمرونة.
 +
# يستخدم نمط سلسلة المسؤوليات بالتزامن غالبًا مع نمط [[Design Patterns/composite|المركَّب (Composite)]]، وعندئذ يمرر العنصر الفرعي (Leaf Component - أصغر وحدة فرعية في شجرة الكائنات) الطلبَ في السلسلة التي تحتوي على كل المكونات الرئيسية (Parent Components) وصولًا إلى جذر شجرة الكائنات.
 +
# يمكن استخدام المداوِلات في نمط سلسلة المسؤوليات ككائنات من نمط الأمر (Commands)، وفي تلك الحالة تستطيع تنفيذ عمليات كثيرة على نفس الكائنا السياقي الممثَّل في الطلب. لكن هناك منظور آخر يكون فيه الطلب نفسه كائنًا من نمط الأمر (Command)، وهنا تستطيع تنفيذ نفس العملية في سلسلة من السياقات المختلفة المرتبطة ببعضها في سلسلة.
 +
# يتشابه نمطا سلسلة المسؤوليات و<nowiki/>[[Design Patterns/decorator|المزخرِف]] في بنية الفئات إلى حد كبير، فكلا النمطين يعتمدان على التركيب التكراري (Recursive Composition) لتمرير التنفيذ في سلسلة من الكائنات، لكن بينهما عدة اختلافات جوهرية.
 +
#* فمداوِالات نمط سلسلة المسؤوليات تستطيع تنفيذ عمليات متعاقبة (Arbitrary) مستقلة عن بعضها، كما يمكنها إيقاف تمرير الطلب عند أي نقطة في السلسلة.
 +
#* أما في نمط المزخرِف، فتستطيع مزخرِفات عديدة أن توسع سلوك الكائن مع الحفاظ على ثباته مع الواجهة الأساسية، ولا يُسمح للمزخرِفات أن تعطل سير الطلب.
 +
 +
==الاستخدام في لغة جافا==
 +
'''المستوى:''' ★ ★ ☆
 +
 +
'''الانتشار:'''  ★ ☆ ☆
 +
 +
'''أمثلة الاستخدام:''' نمط سلسلة المسؤوليات غير شائع في برامج جافا بما أنه يتطلب أن تعمل الشيفرة مع سلسلة من الكائنات. لكن أحد الاستخدامات المشهورة للنمط هو إرسال أحداث إلى المكونات الرئيسية (Parent Components) في فئات الواجهة الرسومية، كذلك يُستخدم في مرشِّحات الوصول التسلسلية (Sequential Access Filters). إليك بعض الأمثلة على النمط في مكتبات جافا:
 +
* <code>[http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain- javax.servlet.Filter#doFilter()]</code>
 +
 +
* <code>[http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log-java.util.logging.Level-java.lang.String- java.util.logging.Logger#log()]</code>
 +
يمكن ملاحظة النمط من خلال الأساليب السلوكية لكائن يستدعي -بشكل غير مباشر - نفس الأساليب في الكائنات الأخرى، بينما تتبع كل الكائنات نفس الواجهة المشتركة.
 +
 +
=== ترشيح الوصول Filtering Access ===
 +
يبين هذا المثال كيف يمكن لطلب يحتوي على بيانات المستخدم أن يمرر سلسلة متعاقبة من المداوِلات التي تنفذ أمورًا مختلفة كالت

مراجعة 14:46، 4 مايو 2019

نمط سلسلة المسؤوليات (Chain of Responsibility) هو نمط تصميم سلوكي (Behavioral) يسمح لك بتمرير طلبات على سلسلة من المداوِلات (Handlers)، ويقرر كل مداوِل عند استلام الطلب أن يعالجه أو يمرره إلى العامل التالي في السلسلة.

ضع الصورة.

المشكلة

تخيل أنك تعمل على نظام طلبات أونلاين، وتريد أن تقيد الوصول إلى النظام كي يكون إنشاء الطلبات مسموحًا به للمستخدمين الموثَّقين فقط. كذلك يجب أن يكون للمستخدمين الذين يملكون صلاحيات الإدارة حقُ الدخول إلى كل الطلبات.

ثم إنك أدركت بعد قليل من التخطيط أن عمليات التحقق تلك يجب أن تتم بشكل متسلسل، ويمكن للتطبيق أن يحاول توثيق المستخدم في النظام كلما استلم طلبًا يحتوي اعتماديات المستخدم (User Credentials). لكن إن لم تكن تلك الاعتماديات صحيحة وفشل التوثيق فما الداعي إلى إتمام أي من خطوات التحقق التالية؟

ضع الصورة. يجب أن ينجح الطلب في سلسلة من عمليات التحقق قبل أن يعالجه النظام بنفسه.

ولنفرض أنك في خلال الأشهر التالية لتلك الخطوة قد استخدمت مزيدًا من عمليات التحقق التسلسلية تلك على النحو التالي:

  • اقتراح من أحد أصدقائك أنه من الخطر تمرير بيانات صريحة مباشرة إلى نظام الطلبات، وعليه فقد أضفتَ خطوة تحقق إضافية لتعقيم (Sanitize) البيانات داخل الطلب.
  • لاحقًا، يلاحظ شخص أن النظام عرضة لاختراق من نوع (Brute Force) -محاولات إدخال كلمات مرور كثيرة-. ولتجنب هذا أضفتَ عملية تحقق ترشِّح الطلبات الفاشلة المتكررة من نفس عنوان الـ IP.
  • اقتراح آخر بتسريع النظام من خلال إعادة النتائج المحفوظة للطلبات المكررة التي تحتوي نفس البيانات، وعليه فقد أضفتَ عملية تحقق إضافية لا تسمح للطلب بالوصول إلى النظام إلا إن لم تكن هناك نتيجة مناسبة محفوظة من قبل.

الصورة. كلما زاد حجم الشيفرة زاد تعقيدها.

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

ويصعب حينها استيعاب النظام وتزيد تكلفة صيانته، وستجد نفسك تعاني معه وتصبر عليه إلى أن يأتي يوم وتعيد هيكلته بالكامل.

الحل

يعتمد نمط سلسلة المسؤوليات كغيره من أنماط التصميم السلوكية على تحويل سلوكيات بعينها إلى كائنات مستقلة بذاتها تدعى مُداوِلات، وفي حالتنا فإن كل عملية تحقق يجب أن تُستخرَج إلى فئتها الخاصة بأسلوب وحيد ينفذ التحقق. ويُمرَّر الطلب مع بياناته إلى هذا الأسلوب على أنه وسيط (Argument).

ويقترح النمط أن تربط بين هذيْن المُداوِليْن في سلسلة، ولكل مداوِل مرتبط حقلٌ لتخزين مرجع إلى المداوِل التالي في السلسلة، وتعالج المُداوِلات الطلبَ وتمرره بينها قُدُمًا في السلسلة حتى يكون قد مر عليها جميعًا وحصلت جميعها على فرصة لمعالجته. والجميل في الأمر أن المداوِل قد يقرر ألا يمرر الطلب إلى ما بعده في السلسلة، ويوقف أي معالجة لاحقة.

وفي مثالنا مع نظام الطلبات، فإن المداوِل (Handler) ينفذ عملية المعالجة ثم يقرر ما إن كان سيمرر الطلب لما بعده في السلسلة أم لا، وبافتراض أن الطلب يحتوي على البيانات المناسبة فإن كل المداوِلات تستطيع تنفيذ سلوكها الأساسي سواء كان ذلك السلوك حفظًا أو تحققًا من التوثيق.

الصورة. تصطف المداوِلات واحدًا تلو الآخر لتكون سلسلة.

لكن هناك منظور مختلف قليلًا (ورسمي أكثر)، يقرر فيه المداوِل حين يستلم الطلب ما إن كان يستطيع معالجة الطلب أم لا، وعليه فإن الطلب إما أن يُعالج بواسطة مداوِل واحد أو لا يُعالَج على الإطلاق. يشيع هذا المنظور عند التعامل مع الأحداث (Events) في مكدَّسات العناصر داخل واجهة المستخدم الرسومية.

فمثلًا، حين ينقر مستخدم على زر فإن الحدث يُنشر في سلسلة عناصر الواجهة التي تبدأ بالزر وتنتهي بنافذة التطبيق الأساسية مرورًا بالحاويات -كالاستمارات أواللوحات (Panels)-. ويعالَج الحدث بواسطة أول عنصر يستطيع معالجته في السلسلة، وهذا المثال جدير بالذكر لأنه يظهر إمكانية استخراج سلسلة من داخل شجرة كائنات، (انظر ش.5).

الصورة. يمكن تشكيل سلسلة من فرع داخل شجرة كائنات.

من المهم أن تستخدم كل فئات المعالِج نفسَ الواجهة، ويجب أن يهتم كل معالِج حقيقي (Concrete Handler) بالمعالج التالي له فحسب، ذلك الذي يحتوي على أسلوب execute. وهكذا يمكنك تركيب السلاسل أثناء التشغيل باستخدام معالِجات مختلفة دون ربط شيفرتك بفئاتها الحقيقية.

مثال واقعي

الصورة. مكالمة واحدة مع خدمة الدعم الفني قد تمر على أكثر من موظف.

لنفرض أنك اشتريت قطعة عتاد في حاسوبك، وتحاول الآن أن تجرب أداء هذه القطعة الجديدة على أنظمة التشغيل المختلفة التي لديك، ووجدت أن ويندوز يلتقط وجودها ويفعّلها تلقائيًا، بينما لا يشعر بها لينكس، فتقرر التحدث مع الدعم الفني.

أول شيء تسمعه في المكالمة هو الرسالة المسجلة الآلية التي تقترح بضعة حلول للمشاكل المشهورة، ولمّا لم تجد حل مشكلتك قررت التحدث إلى أحد الموظفين في الدعم الفني، لكن موظف الدعم الفني لا يفعل سوى تلاوة الردود المكررة على العملاء دون الاستماع إلى وصف مشكلتك أو تقديم حل لها، وفي النهاية لا تخرج منه بشيء نافع فتطلب التحدث مع أحد المهندسين.

وعندما يوصلك الموظف بالمهندس المختص الذي يرشدك إلى موقع تحميل التعريفات المناسبة لقطعة العتاد، وكيفية تثبيتها على لينكس، لتجد مشكلتك قد حُلت في النهاية.

البنية

الصورة.

  1. تصرح فئة Handler عن الواجهة المشتركة لكل المعالِجات الحقيقية، وتحتوي في العادة على أسلوب واحد فقط لمعالجة الطلبات، لكن قد تحتوي أحيانًا على أسلوب آخر لتهيئة المعالِج التالي في السلسلة.
  2. فئة Base Handler هي فئة اختيارية تستطيع وضع شيفرة أساسية مشتركة بين كل فئات المعالِجات. وعادة ما تحدد هذه الفئة حقلًا لتخزين مرجع إلى المعالِج التالي، ويستطيع العميل بناء سلسلة بتمرير معالِج إلى المنشئ (Constructor) أو محدِّد (Setter) المعالِج السابق. كذلك قد تستخدمُ الفئةُ سلوك المعالجة الافتراضي، بأن تمرر التنفيذ إلى المعالِج التالي بعد التحقق من وجوده.
  3. تحتوي Concrete Handlers على الشيفرة الحقيقية لمعالجة الطلبات، ويجب أن يقرر كل معالِج عند استلام الطلب ما إن كان سيعالجه أم لا، وكذلك ما إن كان سيمرره لما بعده في السلسلة أم لا. وتكون المعالجات في العادة مستقلة بذاتها وغير قابلة للتغيير، وتقبل البيانات التي تحتاجها مرة واحدة فقط من خلال المنشئ.
  4. قد يركب العميل (Client) سلاسل مرة واحدة فقط أو يركبها بشكل ديناميكي، وفقًا لمنطق التطبيق. لاحظ أن الطلب يمكن إرساله إلى أي معالِج في السلسلة، ولا يشترط أن يكون أول معالج.

مثال توضيحي

في هذا المثال، يكون نمط سلسلة المسؤوليات مسؤولًا عن عرض معلومات المساعدة السياقية لعناصر واجهة رسومية نشطة.

الصورة. بُنيت فئات الواجهة الرسومية بنمط المركّب. وكل عنصر يرتبط بعنصره الحاوي. وتستطيع بناء سلسلة عناصر في أي وقت تبدأ بالعنصر نفسه وتمر بكل عناصره الحاوية.

تُبنى واجهة التطبيق الرسومية عادة على أنها شجرة كائنات، فمثلًا ستكون فئة Dialog التي تخرِج (Render) نافذة التطبيق الرئيسية، ستكون جذر شجرة الكائنات. وتحتوي فئة Dialog على Panels التي قد تحتوي لوحات أخرى بداخلها أو عناصر بسيطة منخفضة المستوى مثل Buttons و TextFields.

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

الصورة. كيفية مرور طلب مساعدة على كائنات الواجهة الرسومية.

حين يقف مستخدم بمؤشر الماوس على عنصر ويضغط مفتاح F1، فإن التطبيق يلتقط المكون الذي تحت المؤشر ويرسل إليه طلب مساعدة، ويمر الطلب على كل حاويات العنصر حتى يصل إلى العنصر القادر على عرض معلومات المساعدة.

// تصرح واجهة المداول عن أسلوب لبناء سلسلة من المداوِلات، كما تصرح عن 
// أسلوب لتنفيذ الطلب.

interface ComponentWithContextualHelp is
    method showHelp()


// الفئة الأساسية للمكونات البسيطة.
abstract class Component implements ComponentWithContextualHelp is
    field tooltipText: string

    // تتصرف حاوية المكون كالرابط التالي في سلسلة المداوِلات.
    protected field container: Container

    // يعرض المكون تلميحًا إن كان قد خُصص له نص مساعدة، وإلا فإنه يرحِّل 
    // الاستدعاء إلى الحاوية إن وجدت.
    method showHelp() is
        if (tooltipText != null)
            // اعرض تلميحًا.
        else
            container.showHelp()


// تستطيع الحاويات احتواء كل من المكونات البسيطة والحاويات الأخرى
// في صورة فروع لها. وتتم علاقات السلسلة هنا.
// من الفئة الأم لها (showHelp) كما تكتسب الفئة سلوك أسلوب.
abstract class Container extends Component is
    protected field children: array of Component

    method add(child) is
        children.add(child)
        child.container = this


// قد لا يمثل الاستخدام الافتراضي للمساعدة مشكلة مع المكونات الأولية البسيطة.
class Button extends Component is
    // ...

// لكن قد تتخطى المكوناتُ المعقدةُ الاستخدام الافتراضي، وإن لم يكن 
// ممكنًا إثبات نص المساعدة بشكل آخر، فإن المكون الأساسي يستطيع
// استدعاء الاستخدام الأساسي
// Component انظر فئة.
class Panel extends Container is
    field modalHelpText: string

    method showHelp() is
        if (modalHelpText != null)
            // اعرض نافذة مشروطة مع نص مساعدة.
        else
            super.showHelp()

// ...تمامًا كما في الأعلى...
class Dialog extends Container is
    field wikiPageURL: string

    method showHelp() is
        if (wikiPageURL != null)
            // افتح صفحة مساعدة ويكي.
        else
            super.showHelp()


// شيفرة العميل.
class Application is
    // يهيئ كل تطبيق السلسلة بشكل مختلف.
    method createUI() is
        dialog = new Dialog("Budget Reports")
        dialog.wikiPageURL = "http://..."
        panel = new Panel(0, 0, 400, 800)
        panel.modalHelpText = "This panel does..."
        ok = new Button(250, 760, 50, 20, "OK")
        ok.tooltipText = "This is an OK button that..."
        cancel = new Button(320, 760, 50, 20, "Cancel")
        // ...
        panel.add(ok)
        panel.add(cancel)
        dialog.add(panel)

    // تخيل ما سيحدث هنا.
    method onF1KeyPress() is
        component = this.getComponentAtMouseCoords()
        component.showHelp()

قابلية التطبيق

استخدم نمط سلسلة المسؤوليات عندما يُفترض ببرنامجك أن يعالج أنواعًا مختلفة من الطلبات بطرق متعددة، لكن نفس تلك الأنواع وتسلسلاتها لا تكون معروفة بشكل مسبق.

يسمح لك النمط بربط مداوِلات متعددة في سلسلة واحدة، وعند استلام طلب فإنك تستطيع سؤال كل مداوِل عما إن كان يستطيع معالجة الطلب، وبهذه الطريقة تحصل كل المداوِلات على فرصة لمعالجة الطلب.

استخدم النمط حين يكون من الضروري تنفيذ عدة مداوِلات في ترتيب محدد.

بما أنك تستطيع ربط المداوِلات في السلسلة بأي ترتيب، فإن كل الطلبات ستمر خلال السلسلة كما خططت تمامًا.

استخدم نمط سلسلة المسؤوليات عندما يُفترض بمجموعة مداوِلات وترتيبها أن يتغيروا عند وقت التشغيل (Runtime).

إن كنت قد زودتَ حقلًا مرجعيًا داخل فئات المداوِل بمحدِّدات فستكون قادرًا على إدخال أو حذف أو إعادة ترتيب المداوِلات بمرونة.

كيفية الاستخدام

  1. صرِّح عن واجهة المداوِل وَصِف توقيع الأسلوب (Signature of the Method) لمعالجة الطلبات. قرر الكيفية التي سيمرر العميلُ بها بيانات الطلب إلى الأسلوب، وأكثر طريقة مرونةً هي تحويل الطلب إلى كائن وتمريره إلى أسلوب المعالجة كوسيط (Argument).
  2. لتلافي تكرار الشيفرة الأساسية في المداوِلات الحقيقية، سيكون من المجدي إنشاء فئة مداوِل أساسي مجرد (Abstract Base Handler)، متفرعة من واجهة المداوِل. ويجب أن يكون في تلك الفئة حقلٌ لتخزين مرجع إلى المداوِل التالي في السلسلة، ولا تنس جعل الفئة غير قابلة للتغيير (immutable)، لكن إن كنت تريد تعديل السلاسل أثناء وقت التشغيل فستحتاج إلى تعريف محدِّد (Setter) لتغيير قيمة الحقل المرجعي. كذلك تستطيع أيضًا تطبيق السلوك الافتراضي المناسب لأسلوب المعالجة، بأن توجه الطلب إلى الكائن التالي إلا أن يكون هو الكائن الأخير، وستكون المداوِلات الحقيقية (Concrete Handlers) قادرة على استخدام هذا السلوك من خلال استدعاء الأسلوب الأم.
  3. أنشئ فئات فرعية للمداوِلات الحقيقية واحدة تلو الأخرى، ثم طبِّق أساليب المعالجة الخاصة بها، يجب أن يتخذ كل مداوِل قرارين عند استلام الطلب:
    • هل سيعالِج الطلب؟
    • هل سيمرِّر الطلب في السلسلة؟
  4. قد يجمِّع العميل سلاسل بنفسه أو يستلم سلاسل مبنية مسبقًا من كائنات أخرى، وفي الحالة الثانية فيجب أن تستخدم بعض فئات المصنع (factory classes) من أجل بناء سلاسل وفقًا لإعدادات التهيئة أو البيئة.
  5. يستطيع العميل أن ينبِّه (trigger) أي مداوِل في السلسلة، وليس شرطًا أن ينبه المداوِل الأساسي فقط، وسيمرَّر الطلب في السلسلة حتى يرفض أحد المداوِلات أن يمرر الطلب لما بعده في السلسلة أو حتى يصل إلى نهاية السلسلة.
  6. بسبب طبيعة السلسلة المرنة فإن العميل يجب أن يكون مستعدًا للتعامل مع السيناريوهات التالية:
    • قد تتكون السلسلة من رابط واحد.
    • قد لا تصل بعض الطلبات نهاية السلسلة.
    • قد يصل بعضها الآخر إلى نهاية السلسلة دون معالجة.

المزايا والعيوب

المزايا

  • تستطيع التحكم في ترتيب معالجة الطلبات.
  • مبدأ المسؤولية الواحدة. تستطيع فصل الفئات التي تطلب (Invoke) العمليات عن الفئات التي تنفذ (Perform) العمليات.
  • مبدأ المفتوح/المغلق. تستطيع إدخال مداوِلات جديدة في التطبيق دون تعطيل شيفرة العميل الحالية.

العيوب

  • قد لا تُعالج بعض الطلبات.

العلاقات مع الأنماط الأخرى

  1. تختلف أنماط سلسلة المسؤوليات (Chain of Responsibility) والأمر (Command) والوسيط (Mediator) والمراقب (Observer) في طرق توصيل مستقبلي الطلبات ومرسليها ببعضهم، وترى ذلك الاختلاف فيما يلي:
    • تمرِّر سلسلة المسؤوليات الطلب بشكل تسلسلي في سلسلة مرنة من المستقبلين المحتملين إلى أن يعالج أحدهم الطلب.
    • ينشئ نمط الأمر وصلات أحادية الاتجاه (Unidirectional) بين المرسلين والمستقبلين.
    • يلغي نمط الوسيط الاتصالات المباشرة بين المرسلين والمستقبلين مجبرًا إياهم على التواصل بشكل غير مباشر من خلال كائن وسيط.
    • يسمح نمط المراقب للمستقبلين بالاشتراك في استلام الطلبات وكذلك إلغاء الاشتراك بمرونة.
  2. يستخدم نمط سلسلة المسؤوليات بالتزامن غالبًا مع نمط المركَّب (Composite)، وعندئذ يمرر العنصر الفرعي (Leaf Component - أصغر وحدة فرعية في شجرة الكائنات) الطلبَ في السلسلة التي تحتوي على كل المكونات الرئيسية (Parent Components) وصولًا إلى جذر شجرة الكائنات.
  3. يمكن استخدام المداوِلات في نمط سلسلة المسؤوليات ككائنات من نمط الأمر (Commands)، وفي تلك الحالة تستطيع تنفيذ عمليات كثيرة على نفس الكائنا السياقي الممثَّل في الطلب. لكن هناك منظور آخر يكون فيه الطلب نفسه كائنًا من نمط الأمر (Command)، وهنا تستطيع تنفيذ نفس العملية في سلسلة من السياقات المختلفة المرتبطة ببعضها في سلسلة.
  4. يتشابه نمطا سلسلة المسؤوليات والمزخرِف في بنية الفئات إلى حد كبير، فكلا النمطين يعتمدان على التركيب التكراري (Recursive Composition) لتمرير التنفيذ في سلسلة من الكائنات، لكن بينهما عدة اختلافات جوهرية.
    • فمداوِالات نمط سلسلة المسؤوليات تستطيع تنفيذ عمليات متعاقبة (Arbitrary) مستقلة عن بعضها، كما يمكنها إيقاف تمرير الطلب عند أي نقطة في السلسلة.
    • أما في نمط المزخرِف، فتستطيع مزخرِفات عديدة أن توسع سلوك الكائن مع الحفاظ على ثباته مع الواجهة الأساسية، ولا يُسمح للمزخرِفات أن تعطل سير الطلب.

الاستخدام في لغة جافا

المستوى: ★ ★ ☆

الانتشار:  ★ ☆ ☆

أمثلة الاستخدام: نمط سلسلة المسؤوليات غير شائع في برامج جافا بما أنه يتطلب أن تعمل الشيفرة مع سلسلة من الكائنات. لكن أحد الاستخدامات المشهورة للنمط هو إرسال أحداث إلى المكونات الرئيسية (Parent Components) في فئات الواجهة الرسومية، كذلك يُستخدم في مرشِّحات الوصول التسلسلية (Sequential Access Filters). إليك بعض الأمثلة على النمط في مكتبات جافا:

يمكن ملاحظة النمط من خلال الأساليب السلوكية لكائن يستدعي -بشكل غير مباشر - نفس الأساليب في الكائنات الأخرى، بينما تتبع كل الكائنات نفس الواجهة المشتركة.

ترشيح الوصول Filtering Access

يبين هذا المثال كيف يمكن لطلب يحتوي على بيانات المستخدم أن يمرر سلسلة متعاقبة من المداوِلات التي تنفذ أمورًا مختلفة كالت