Monday, February 12, 2018

هندسة البرمجيات بالهجايص (6): أؤمر، ما تسألش


[الصورة من مقالة لمارتن فاولر استفدت منها في كتابة المقالة دي]

في عصر ما قبل الObject Oriented Programming، البرامج كانت بتتقسم بحيث إن البيانات بتبقى في حتة و العمليات اللي بتعالج البيانات دي في حتة تانية خالص. الOOP قالت لك لأ احنا هانرتب الدنيا بطريقة مختلفة، احنا نحطهم الاتنين مع بعض، وسموا الحركة دي (اللي هي جمع البيانات مع العمليات) encapsulation (الترجمة الحرفية ليها = تغليف، تضمين).
لما بحط البيانات و العمليات مع بعض بزود في التماسك cohesion لأن العمليات مرتبطة بالبيانات اللي موجودة معاها بالفعل، ودي حاجة كويسة. و لما بفصلهم بزود في الاقتران coupling لأن الclass اللي فيها العمليات هاتحتاج تعتمد على الclass اللي فيها البيانات، و دي حاجة وحشة. [اتكلمت عن الموضوع ده بالتفصيل في مقالة: أحط الحاجة فين]
عارفين الناس اللي بيدخلوا على الجروبات ويقولوا لو حد يعرف كذا يقول، و عادة الناس بترد عليه وتقول له: قول سؤالك على طول. اللي بيعرف هايرد عليك. وفيه جروبات بتمنع النوع ده من المشاركات من الأساس.
أهم هما دول اللي مش بيطبقوا الencapsulation كويس 
تعالوا نشوف القصة إيه...

فيه مبدأ عندنا بيقول: ما تسألش عن البيانات، وبعدين تقرر أنت هاتعمل بيها إيه. خلي المسئول عن تنفيذ اللي أنت عاوز تعمله بالبيانات دي هي الclass نفسها اللي فيها البيانات دي. و قل للclass دي تجيب من الآخر وتنفذ اللي أنت عاوزه على طول.المبدأ ده بيسموه Tell, Don't Ask. يعني إيه الكلام ده؟

- تعالوا ناخد مثال من ال.NET Framework، فيه class اسمها File دي بنستخدمها لما نيجي نتعامل مع الملفات على الهارد ديسك، الclass دي فيها method اسمها Exists. ودي بنستخدمها عشان نتأكد الملف موجود ولا لأ قبل ما نعمل عليه أي عمليات (قراءةRead أو كتابة Write أو حذف Delete) و إلا هايضرب Exception لو جيت اعمل العمليات دي على ملف مش موجود مثلا. بس لو ندهت للExists عشان أشوف الملف موجود ولا لأ قبل ما اعمل عليه أي عملية هاخالف المبدأ اللي معانا: Tell, Don't Ask، عشان أنا سألت الأول عن حالة الملف وبعدين أخدت القرار بناء على الحالة اللي عرفتها.

- تعالوا نشوف مثال تاني من ال.NET Framework برضو: لما تيجي تحول نص string لرقم، فيه عندك method في الint struct اسمها Parse و دي هاتضرب exception لو النص لا يمكن تحويله لرقم، يعني هاينطبق عليها نفس الكلام اللي قلناه في المثال اللي فات عند التعامل مع الملفات. بس الحقيقة الstruct دي فيها method تانية اسمها TryParse ودي "بتحاول" تحول النص لرقم، لو فشلت مش بتضرب exception. ودي متسقة جدا مع المبدأ اللي بنتكلم عليه، ومش محتاج هنا try-catch عشان أتفادى مشاكل فشل التحويل.
تعالوا نرجع للمثال الأول: لو كانت الFile class فيها methods اسمها: TryRead، TryDelete، TryWrite، مش هايكون أفضل؟
أظن هايكون أفضل.

- المبدأ ده مفيد جدا لما تيجي تصمم خدمة ويب web service. الفكرة إنك عاوز تقلل المشاوير اللي ما بينك و بين الخدمة عشان تتفادى مشاكل الشبكة. فلما تطبق المبدأ ده هاتلاقي إنك بتطلب من الخدمة تنفذ لك المهمة اللي أنت عاوزها دوغري، ودي بيسموها coarse grained، على عكس لو فضلت رايح جاي على الخدمة تسأل عن البيانات و بعدين تقرر و بعد كده بترجع تعيط إن الخدمة بطيئة. و دي بيسموها fine grained أو chatty.

المبدأ ده كويس جدا، بس أحيانا مش بيكون مناسب للسياق اللي أنا شغال فيه، أو لو طبقته هاضيع على نفسي مميزات أخرى، تعالوا نشوف أمثلة:
- تعالوا نرجع لأول مثال ذكرته فوق، لغاية لما مايكروسوفت تعمل TryDelete، استخدم Exists الأول وبعدين Delete و لا أحط الDelete في try-catch؟
لأ طبعا استخدم الExists وبعدين الDelete، و لا تستخدم الtry-catch في التحكم في مسارات البرنامج control flow أبدا. هنا مش أنا اللي عامل الFile class و مضطر استخدمها كما هي، وده مش عيب.

- لما بعمل بحث أو تصفية لبيانات أنا بسأل مش بأمر، بس قشطة مفيش مشكلة في كده.

- لو حطيت كل العمليات الممكنة و الغير ممكنة على البيانات هالاقي الدنيا كبرت جدا، و معظم استخدامات الclass اللي فيها البيانات و العمليات دي مش بتحتاج كل العمليات دي. في الحالة دي ممكن يبقى عندي طبقات من الclasses، كل طبقة بتعمل تجريد abstraction للطبقة اللي تحتها و تضيف عمليات جديدة عليها (أو غيرها من طرق تقسيم الclasses)

- أحيانا بفصل العمليات عن البيانات لأن العمليات دي ممكن تتغير، ودي هاتيجي معانا بالتفصيل إن شاء الله لما نتكلم على المبدأ Encapsulate what varies أو عند تطبيق ال strategy pattern.

- أحيانا بفصل العمليات عن البيانات عشان أقسم الدنيا لطبقات layers فتديني مرونة أعلى في التصميم، زي مثلا إني بفصل العمليات اللي بتتعامل مباشرة مع قاعدة البيانات (اللي هي CRUD Operations = إنشاء Create وقراءة Retrieve وتعديل Update وحذف Delete البيانات) عن العمليات اللي بتعالج البيانات دي، وتطبق عليها إجراءات العمل business rules مثلا.

- أحيانا بنفضل إننا نفصل العمليات الاستعلامية Queries (زي التقارير و لوحات المعلومات dashboards مثلا) عن العمليات الإجرائية Commands (زي إنشاء و تعديل وحذف البيانات) عشان نقدر نحسن كل واحدة على حدة، وفيه architecture style معروف في التقسيمة دي اسمه Command and Query Responsibility Segregation، أو اختصارا CQRS.

حابب أختم المقالة دي بكلمة لعم الأركتكتس، مارتن فاولر Martin Fowler، قال: التصميم الجيد عبارة عن شوية موازنات، وجمع البيانات مع العمليات يعتبر عامل واحد فقط نأخذه في حسباننا بجانب العوامل الأخرى.
Good design is all about trade-offs, and co-locating data and behavior is just one factor to bear in mind.

يا ريت تنشروا المقالة دي لو حسيتوا إنها مفيدة، أو تقولولي إيه اللي ماعجبكمش فيها في التعليقات عشان أحسن من نفسي في المقالات اللي جاية إن شاء الله 

No comments:

Post a Comment