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

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

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

كلما نما الكود، ازدادت فوضاه.
أصبح كود عمليات التحقق، الذي كان يبدو فوضويًا بالفعل، منتفخًا أكثر فأكثر مع إضافة كل ميزة جديدة. كان تغيير عملية تحقق واحدة يؤثر أحيانًا على الأخريات. والأسوأ من ذلك، أنك عندما حاولت إعادة استخدام عمليات التحقق لحماية مكونات أخرى في النظام، اضطررت إلى تكرار بعض الكود، إذ احتاجت تلك المكونات إلى بعض عمليات التحقق وليس كلها.
أصبح النظام صعب الفهم جدًا ومكلف الصيانة. كافحت مع الكود لبعض الوقت، حتى قررت ذات يوم إعادة هيكلة كل شيء.
##Solution
كغيرها من أنماط التصميم السلوكية، تعتمد سلسلة المسؤوليات على تحويل سلوكيات معينة إلى كائنات مستقلة تُسمى معالجات. في حالتنا، يجب استخراج كل عملية تحقق إلى فئتها الخاصة بطريقة واحدة تُنفّذ التحقق. يتم تمرير الطلب مع بياناته إلى هذه الطريقة كوسيط.
يقترح النمط ربط هذه المعالجات في سلسلة. يحتوي كل معالج مرتبط على حقل لتخزين مرجع إلى المعالج التالي في السلسلة. بالإضافة إلى معالجة الطلب، تُمرّر المعالجات الطلب على طول السلسلة. يتنقل الطلب على طول السلسلة حتى تحصل جميع المعالجات على فرصة لمعالجته.
Here’s the best part: a handler can decide not to pass the request further down the chain and effectively stop any further processing.
In our example with ordering systems, a handler performs the processing and then decides whether to pass the request further down the chain. Assuming the request contains the right data, all the handlers can execute their primary behavior, whether it’s authentication checks or caching.

تصطف المعالجات واحدة تلو الأخرى، مكوّنةً سلسلة.
However, there’s a slightly different approach (and it’s a bit more canonical) in which, upon receiving a request, a handler decides whether it can process it. If it can, it doesn’t pass the request any further. So it’s either only one handler that processes the request or none at all. This approach is very common when dealing with events in stacks of elements within a graphical user interface.
For instance, when a user clicks a button, the event propagates through the chain of GUI elements that starts with the button, goes along its containers (like forms or panels), and ends up with the main application window. The event is processed by the first element in the chain that’s capable of handling it. This example is also noteworthy because it shows that a chain can always be extracted from an object tree.

يمكن تكوين سلسلة من فرع في شجرة كائنات.
It’s crucial that all handler classes implement the same interface. Each concrete handler should only care about the following one having the execute method. This way you can compose chains at runtime, using various handlers without coupling your code to their concrete classes.
##Structure
-
يُعلن المعالج عن الواجهة المشتركة لجميع المعالجات المحددة. عادةً يحتوي على طريقة واحدة فقط لمعالجة الطلبات، لكن أحيانًا قد يحتوي أيضًا على طريقة أخرى لتعيين المعالج التالي في السلسلة.
-
The Base Handler is an optional class where you can put the boilerplate code that’s common to all handler classes.
عادةً تُعرّف هذه الفئة حقلًا لتخزين مرجع إلى المعالج التالي. يمكن للعملاء بناء سلسلة بتمرير معالج إلى مُنشئ أو مُحدِّد المعالج السابق. يمكن للفئة أيضًا تنفيذ سلوك المعالجة الافتراضي: يمكنها تمرير التنفيذ إلى المعالج التالي بعد التحقق من وجوده.
-
تحتوي المعالجات المحددة على الكود الفعلي لمعالجة الطلبات. عند استقبال طلب، يجب على كل معالج أن يقرر ما إذا كان سيعالجه، وبالإضافة إلى ذلك، ما إذا كان سيمرره على طول السلسلة.
عادةً ما تكون المعالجات مكتفية بذاتها وغير قابلة للتغيير، وتقبل جميع البيانات الضرورية مرة واحدة فقط عبر المُنشئ.
-
The Client may compose chains just once or compose them dynamically, depending on the application’s logic. Note that a request can be sent to any handler in the chain—it doesn’t have to be the first one.
##Pseudocode
في هذا المثال، يتولى نمط سلسلة المسؤوليات عرض معلومات المساعدة السياقية لعناصر واجهة المستخدم الرسومية النشطة.

تُبنى فئات واجهة المستخدم الرسومية باستخدام نمط المركّب. يرتبط كل عنصر بعنصر حاويه. في أي لحظة، يمكنك بناء سلسلة من العناصر تبدأ بالعنصر نفسه وتمر بجميع عناصر الحاوي.
The application’s GUI is usually structured as an object tree. For example, the Dialog class, which renders the main window of the app, would be the root of the object tree. The dialog contains Panels, which might contain other panels or simple low-level elements like Buttons and TextFields.
يمكن للمكون البسيط عرض تلميحات سياقية موجزة، طالما أن المكون لديه نص مساعدة مُعيَّن. لكن المكونات الأكثر تعقيدًا تُحدد طريقتها الخاصة لعرض المساعدة السياقية، كعرض مقتطف من الدليل أو فتح صفحة في المتصفح.

That’s how a help request traverses GUI objects.
When a user points the mouse cursor at an element and presses the F1 key, the application detects the component under the pointer and sends it a help request. The request bubbles up through all the element’s containers until it reaches the element that’s capable of displaying the help information.
// تُعلن واجهة المعالج عن طريقة لتنفيذ
// طلب.
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)
// فتح صفحة مساعدة wiki.
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()
##Applicability
استخدم نمط سلسلة المسؤوليات عندما يُتوقَّع من برنامجك معالجة أنواع مختلفة من الطلبات بطرق متنوعة، لكن الأنواع الدقيقة للطلبات وتسلسلها غير معروفة مسبقًا.
يتيح النمط ربط عدة معالجات في سلسلة واحدة، وعند استقبال طلب، "سؤال" كل معالج عمّا إذا كان بمقدوره معالجته. وبهذه الطريقة تحصل جميع المعالجات على فرصة لمعالجة الطلب.
استخدم النمط عندما يكون من الضروري تنفيذ عدة معالجات بترتيب معين.
نظرًا لأنك تستطيع ربط المعالجات في السلسلة بأي ترتيب، فإن جميع الطلبات ستمر عبر السلسلة تمامًا كما خططت.
استخدم نمط CoR عندما يُفترض أن تتغير مجموعة المعالجات وترتيبها أثناء التشغيل.
إذا وفّرت مُحدِّدات (setters) لحقل المرجع داخل فئات المعالجات، فستتمكن من إدراج المعالجات وإزالتها وإعادة ترتيبها ديناميكيًا.
##How to Implement
-
أعلن عن واجهة المعالج وصِف توقيع الطريقة اللازمة لمعالجة الطلبات.
حدّد كيف سيمرر العميل بيانات الطلب إلى الطريقة. الطريقة الأكثر مرونة هي تحويل الطلب إلى كائن وتمريره إلى طريقة المعالجة كوسيط.
-
للتخلص من الكود المتكرر في المعالجات المحددة، قد يكون من المفيد إنشاء فئة معالج أساسية مجردة مشتقة من واجهة المعالج.
يجب أن تحتوي هذه الفئة على حقل لتخزين مرجع إلى المعالج التالي في السلسلة. فكّر في جعل الفئة غير قابلة للتغيير. ومع ذلك، إذا كنت تخطط لتعديل السلاسل أثناء التشغيل، فتحتاج إلى تعريف مُحدِّد لتغيير قيمة حقل المرجع.
يمكنك أيضًا تنفيذ السلوك الافتراضي المريح لطريقة المعالجة، وهو إعادة توجيه الطلب إلى الكائن التالي ما لم يبقَ أي كائن. ستتمكن المعالجات المحددة من استخدام هذا السلوك باستدعاء الطريقة الأصل.
-
قم بإنشاء فئات فرعية محددة للمعالجات واحدة تلو الأخرى ونفّذ طرق المعالجة الخاصة بها. يجب على كل معالج اتخاذ قرارين عند استقبال طلب:
- ما إذا كان سيعالج الطلب.
- ما إذا كان سيمرر الطلب على طول السلسلة.
-
قد يجمع العميل السلاسل بنفسه أو يتلقاها مُجمَّعة مسبقًا من كائنات أخرى. في الحالة الأخيرة، يجب تنفيذ بعض فئات المصنع لبناء السلاسل وفق الإعدادات أو متطلبات البيئة.
-
يمكن للعميل تشغيل أي معالج في السلسلة، وليس فقط الأول. سيتم تمرير الطلب على طول السلسلة حتى يرفض أحد المعالجات تمريره أو حتى يصل إلى نهاية السلسلة.
-
بسبب الطبيعة الديناميكية للسلسلة، يجب أن يكون العميل مستعدًا للتعامل مع السيناريوهات التالية:
- قد تتكون السلسلة من رابط واحد فقط.
- قد لا تصل بعض الطلبات إلى نهاية السلسلة.
- قد تصل طلبات أخرى إلى نهاية السلسلة دون معالجة.
##Pros & Cons
- يمكنك التحكم في ترتيب معالجة الطلبات.
- مبدأ المسؤولية الواحدة. يمكنك فصل الفئات التي تستدعي العمليات عن الفئات التي تنفذها.
- مبدأ الفتح/الإغلاق. يمكنك تقديم معالجات جديدة في التطبيق دون كسر كود العميل الحالي.
- قد تنتهي بعض الطلبات دون معالجة.
##Relations with Other Patterns
-
سلسلة المسؤوليات, الأمر, الوسيط and المراقب تتناول طرقًا مختلفة لربط مُرسِلي الطلبات ومستقبليها:
- سلسلة المسؤوليات تُمرّر طلبًا بشكل تسلسلي عبر سلسلة ديناميكية من المستقبلين المحتملين حتى يعالجه أحدهم.
- الأمر يُنشئ اتصالات أحادية الاتجاه بين المُرسِلين والمستقبلين.
- الوسيط يُلغي الاتصالات المباشرة بين المُرسِلين والمستقبلين، مُجبرًا إياهم على التواصل بشكل غير مباشر عبر كائن وسيط.
- المراقب يتيح للمستقبلين الاشتراك وإلغاء الاشتراك ديناميكيًا من استقبال الطلبات.
-
سلسلة المسؤوليات يُستخدم غالبًا بالتزامن مع المركّب. في هذه الحالة، عندما يحصل مكون ورقي على طلب، قد يمرره عبر سلسلة جميع المكونات الأصلية وصولًا إلى جذر شجرة الكائنات.
-
يمكن تنفيذ المعالجات في سلسلة المسؤوليات يمكن تنفيذها كـ الأوامر. في هذه الحالة، يمكنك تنفيذ الكثير من العمليات المختلفة على نفس كائن السياق، الذي يمثله طلب.
However, there’s another approach, where the request itself is a Command object. In this case, you can execute the same operation in a series of different contexts linked into a chain.
-
سلسلة المسؤوليات and المزخرف لديهما هياكل فئات متشابهة جدًا. يعتمد كلا النمطين على التركيب العودي لتمرير التنفيذ عبر سلسلة من الكائنات. ومع ذلك، توجد عدة اختلافات جوهرية.
The CoR handlers can execute arbitrary operations independently of each other. They can also stop passing the request further at any point. On the other hand, various Decorators can extend the object’s behavior while keeping it consistent with the base interface. In addition, decorators aren’t allowed to break the flow of the request.