أنماط التصميم Design Patterns الأنماط السلوكية (Behavioral Pattern)
المعلوماتية >>>> برمجيات
هناك العديد من أنماط التصميم السلوكية المِعيارية، سنتحدث عن اثنين منها:
نمط المُراقِب observer pattern.
نمط الزائر visitor pattern.
نمط المُراقب (observer pattern)
كما في أنماط التصميم السلوكية كافة، يهتم نمط المُراقِب بكيفية تواصل الأغراض فيما بينها، فنوع التواصل الذي يسعى إلى تحقيقه هو إيجاد طريقة لتنبيه مجموعة من الأغراض المُتعلقة بغرض ما بأية تحديثات تطرأ على حالة الغرض* في حال تغييرها.
مثال على ذلك، لنفترض أنك تستعمل برنامج الإكسل Excel وتكتب معادلة رياضية تجمع فيها خليتان أخريان، فتكتب في الخلية A3، مثلاً، الآتي:
في الصورة السابقة، ستتواصل الخلية A3 مع الخلية A1 وتجلب مُحتواها (القيمة 10) وتتواصل مع الخلية A2 وتجلب مُحتواها (القيمة 5)، ثم تجمعهما وتُظهر الناتج فيها (وهي القيمة 15).
ولكن؛ قد تتساءل: "ماذا لو بعد انتهاء الجمع وظهور النتيجة في الخلية A3 غُيّرت قيمة الخلية A1 من 10 إلى 20؟ ما الطريقة التي ستُنبّه الخلية A3 بهذا التغيير؟"
لإسقاط المثال السابق في سياق البرمجة، نستطيع عدّ أنّ كُلًّا من الخلايا A1 وA2 وA3 أغراض تستطيع التواصل فيما بينها؛ إذ تكون الخلية A3 غرضًا يعتمد (depends) على الخليتين A1 وA2، ويتواصل معهم للحصول على قيمهم لإتمام عملية الجمع، ونبحث عن طريقة نستطيع بواسطتها تنبيه الخلية A3 بالتغييرات التي تطرأ على الخلايا التي يعتمد عليها.
هنا يأتي دور نمط المُراقب، فهو يقدّم حلًّا لهذه المشكلة؛ إذ يَنصح نمط المراقب بإنشاء لائحة من المُراقبين (Observers) داخل الخلية التي نريد معرفة التغيرات التي تطرأ عليها، وتُمثل هذه اللائحة الخلايا الأخرى كافة التي "تُراقب" و"تعتمد" على حالة هذه الخلية (أي تعتمد على القيمة الموجودة داخلها)، إضافةً إلى توفير دالّة تسمح بالمُراقبين بتسجيل أنفسهم ضمن هذه اللائحة وأخرى لإزالة أنفسهم من المراقبة.
في مثالنا السابق، يُسجل غرض الخلية A3 نفسه كمُراقب في لائحة المُراقبين الموجودة في الخلية A1 بغية الحصول على تنبيه عند تغيير حالة هذه الخلية (مثلاً، تغيير قيمتها من 10 إلى 20). وعندما يحصل تغيير فيها، تنبّه (notify) الخليةُ A1 الأغراض كافة الموجودة في لائحة المراقبين لديها لينجزوا الإجراء المناسب، وبما أن الخلية A3 سجلت نفسها في هذه اللائحة؛ فسيصل هذا التنبيه إليها وتؤدي الإجراء المناسب؛ وهو تغيير قيمتها بناءً على قيمة A1 الجديدة (1، 2).
متى نستخدم هذا النمط؟
عندما يكون في نظامك مُكوّنان يعتمد أحدهما على الآخر، وتريد تحقيق الفصل بينهما لإعادة استخدامها على نحو منفصل.
عندما يكون تغيير حالة مكونٍ من مكونات نظامك يتطلب تغييرًا في مكون آخر أو أكثر، ولا تعلم عدد المكونات الأخرى التي يجب أن تتغير.
عندما يكون هناك مكون يحتاج إرسالَ تنبيهات إلى مكونات اخرى بالتغييرات التي حصلت فيه، ولا يعلم مُسبقًا ما المكونات التي يجب تنبيهُها (3).
* حالة الغرض Object State: هي البيانات التي يحتويها في حقوله في لحظة ما.
نمط الزائر Visitor Pattern
يهدف هذا النمط إلى توفير طريقة تمكنك من تعريف عملية (Operation) جديدة دون تغيير بنية الصنف الذي تُجرَى فيه هذه العملية (3).
لنأخذ مثالًا يوضح "نمط الزائر" من الحياة العملية، وهو عربة التسوق ومحاسب المتجر؛ إذ تذهب أنت إلى المتجر لشراء مجموعة المنتجات وتضعها في عربة التسوق، بعد تعبئة هذه العربة تذهب إلى المحاسب فيأخذ هذه البضائع لحساب المبلغ الواجب دفعه مقابل هذه البضائع.
في هذا المثال تُعدّ عربة التسوق هي الصنف الذي تريد تعريف عملية جديدة عليها وهي حساب قيمة البضائع الموجودة بداخلها (والتي تُعبّر عن بنية الصنف). أما المُحاسب هو الشخص الزائر (Visitor) لعربة التسوق، ويستخدم المنتجات التي تحتويها لحساب السعر.
في حال أردتَ إضافة عملية تعبئة البضائع ضمن أكياس، تستطيع توظيف شخص جديد زائر لهذه العربة، يستخدم مُنتجاتها لتعبئتها ضمن أكياس.
متى تستخدم هذا النمط؟
عندما يكون هناك العديد من العمليات الفريدة وغير المرتبطة بهدف الغرض الأساسي (في مثالنا الغرض الأساسي هو عربة التسوق، والعملية غير المرتبطة بها هي عملية حساب قيمة المنتجات وعملية تعبئة المنتجات ضمن أكياس) والتي تريد تطبيقها على الأصناف الموجودة في هيكلية الغرض object structure (في مثالنا تُمثّل المنتجات داخل عربة التسوق الأصنافَ الموجودة والتي تُمثل محتويات عربة التسوق)، ولا تريد "تلويث" هذه الهيكلية بالعمليات الجديدة (أي لا تريد إضافة عملية حساب قيمة المنتج إلى عربة التسوق)؛ إذ يتيح نمط الزائر إمكانية تجميع العمليات المرتبطة بعضها ببعض في هيكلية واحد مستقلة عن هيكلية الغرض (كل عملية جديدة تُعد زائرًا للغرض الأساسي؛ فالمحاسب يزور عربة التسوق، ومُعبّئ الأكياس يزور عربة التسوق، وهما عمليتان مستقلتان عن عربة التسوق).
استخدم هذا النمط آخذًا بعين النظر أنّ الأصناف التي تتألف منها هيكلية الغرض يجب أن يكون حدوث التغييرات فيها نادر (عربة التسوق يجب أن تبقى كما هي؛ عبارة عن حاوية لمنتجات تزورها العمليات الأخرى مثل حساب المبلغ والتعبئة). لأن أي تغيير يطرأ على الأصناف الخاصة بهيكلية هذا الغرض يتطلب تعديلًا في الأصناف جميعها التي تُمثل الأصناف الزائرة (Visitors)، وهذا الأمر قد يكون مُكلف زمنيًّا (أي إذا غيّرنا عربة التسوق وأجرينا عليها تعديلًا؛ يجب علينا الذهاب إلى عملية حساب المبلغ وعملية التعبئة وإخبارها بالتعديلات الجديدة، وفي حال كان لدينا العديد من العمليات، سنحتاج إلى وقت طويل) (3).
باستخدام نمط الزائر، تحتاج هرميتين: إحداهما للغرض المُراد تعريف عملية جديدة فيه (عربة التسوق ومنتجاتها)، ويمثل الآخر الأصناف الزائرة التي من خلالها نُعرّف عن العمليات الجديدة على الغرض؛ إذ يمكنك إنشاء عملية جديدة عن طريق إضافة صنف جديد يرث من الزائر (عملية المحاسب هي عبارة عن صنف جديد يرث من الصنف المجرد (Visitor)، وعملية التعبئة هي صنف جديد يرث من الصنف المجرد (Visitor)، هدف الوراثة هو أن عربة التسوق لا تسمح إلا للأشخاص من نوع "زائر" بالوصول إلى المنتجات، بمعنى آخر؛ لا تسمح لأي شخص غير المُحاسب -الذي يجري عملية التعبئة- بالتعامل مع المنتجات التي وضعتها بالعربة). طالما الغرض الأساسي لا يتغير، يمكننا إضافة عمليات جديدة ببساطة عن طريق إنشاء أصناف زائرة جديدة.
كما هو واضح من النمطين السابقين، فإنّ الانماط السلوكية هدفها إيجاد وسيلة فعّالة للتواصل بين الأغراض المكونة للنظام البرمجي.
في المقال التالي، سنتحدث عن الأنماط البِنيوية (Structural patterns) وسنذكر بعضًا منها.
المصادر
2- Design Patterns [Internet]. Ocw.mit.edu. 2005 [cited 22 November 2020]. Available from: هنا
3- C. Martin R. Design Principles and Design Patterns [Internet]. Staff.cs.utu.fi. 2000 [cited 19 April 2021]. Available from: هنا