Thursday, March 1, 2018

هندسة البرمجيات بالهجايص (7): قانون ديميتر

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

* مقدمة

كان فيه مشروع برمجي اسمه مشروع ديميتر، الناس اللي شغالة فيه عملوا بعض القواعد للمبرمجين اللي شغالين على المشروع عشان يكتبوا كود OOP نضيف، و بعدين لقوا إن القواعد دي ينفع تعمم فنشروها بالاسم ده = قانون ديميتر Law Of Demeter. وديميتر دي إلهة النبات والطبيعة عند الإغريق. وطبعا دول ناس كفار مالناش دعوة بيهم، ومايهمناش دلوقتي ليه سموه بالاسم ده. اللي يهمنا الفكرة اللي وراه.

* إيه الفكرة؟ 

لما بييجوا يشرحوا الفكرة بالهجايص كده بيقولوا نفس الكلام اللي أمك كانت بتقوله لك وأنت صغير: اتكلم مع أصحابك بس، ما تتكلمش مع حد ما تعرفوش 
Only talk to your friends; don't talk to strangers.
لكن لما بييجوا يشرحوها كفنيين، بيشرحوها كالآتي:
لما بيكون عندك method/function ما، مسموح للmethod دي إنها تنده لـ5 أنواع فقط من الmethods:
1- أي method تانية معاها في نفس الclass
2- لو الmethod دي داخل لها parameters، مسموح لها تنده لأي method في أي parameter من اللي داخلين لها
3- لو الmethod دي أنشأت instantiates أي كائن object، مسموح لها تنده لأي method فيه
4- لو فيه field/attribute في نفس الclass معاها، مسموح لها تنده للmethods اللي فيه
5- لو فيه أي global object، تقدر تنده للmethods اللي فيه.

* طيب إيه علاقة الشرح اللي بالهجايص بالشرح الفني؟
بالهجايص: لو أنت رايح تقابل واحد صاحبك و لقيته جايب واحد صاحبه معاه، اتكلم مع صاحبك بس، وماتتكلمش مع صاحبه عشان أمك ما تزعلش منك!
في البرمجة: لو ندهت لmethod من اللي مسموح لك تنده لهم بحسب قانون ديميتر، فرجعت لك object، ما ينفعش تنده لأي method في الobject ده عشان اللي حطوا قانون ديميتر ما يزعلوش منك، يعني ما ينفعش تكتب كود زي كده:

GetObjectA().GetObjectB().DoSomething();

أو بعبارة أخرى:في اللغات اللي بتستخدم النقطة للوصول للبيانات fields/attributes أو العمليات methods/functions المسموحة على الobject، زي الjava و الC#، استخدم نقطة واحدة بس في الإجراء الواحد. use only one dot.
أي مبرمج مصري فهلوي أصيل، هايقول لك خلاص، لو الكود اللي فات ده بيخالف القانون، احنا ممكن نكتبه على أكثر من سطر، كده مثلا:

Var b = GetObjectA().GetObjectB();
b.DoSomething();

بس الحقيقة ده مخالف للقانون برضو، لأن الفكرة مش في تقليل عدد النقاط المستخدمة للوصول لتفاصيل الobjects، بل في تقليل الاعتمادية بين أجزاء البرنامج وتحسين الencapsulation، عشان كده هاتلاقي أي حاجة بتستخدم Fluent API (إنك تفضل تنده لmethods على نفس الobject) - زي jQuery مثلا - مش بتخالف القانون ده ولا حاجة.
مثال من jQuery:

$('<p></p>').html("Blah, blah, blah").addClass("foobar").appendTo("body");

ليه المثال ده مش بيخالف قانون ديميتر؟ لأ كل function من دول بترجع this، وبالتالي الإجراء اللي فات ملتزم بالنوع الأول المسوح في القانون.
ونفس الكلام للناس اللي شغال LINQ، لما بكتب كود زي المثال التالي مش بخالف قانون ديميتر:

employees.Where(x => x.IsActive).Select(x => x.JoinDate).ToList();

* مميزات تطبيق قانون ديميتر

تطبيق القانون ده بيخلي البرنامج مرن وسهل التعديل،عشان بيقلل الاعتمادية بين أجزاء البرنامج. بل إن فيه دراسة اتعملت على الموضوع ده سنة 1996 وطلعوا بنتيجة إن تقليل تسلسل نده الmethods ممكن يؤدي إلى تقليل الأخطاء البرمجية bugs.

* حالات استثنائية

- الحقيقة كلمة "قانون" دي مش مهضومة قوي في البرمجة، لأنها تحمل معنى إني ما ينفعش أكسره، وده مش الواقع، فمثلا لو طبقت القانون ده بحذافيره، هاتلاقي إنك عشان تفضل محافظ على استخدام نقطة واحدة في كل عملية، هاتضطر تعمل wrapper methods كتيرة، وده مش بس ممل وبيستهلك وقت، لأ ده بيزود احتمالية حدوث أخطاء برمجية بسبب كثرة الmethods اللي هاتكون في الclass (زي ما الدراسة اللي اتكلمنا عليها فوق قالت برضو).
- فيه حالات تانية برضو بنبقى عاوزين "نخالف" القانون ده عن عمد مش نطبقه! زي ما قلنا فوق، المقصود من المبدأ ده تحسين الencapsulation، عن طريق إخفاء المعلومات، بس فيه حالات ببقى عاوز "أظهر" فيها المعلومات مش أخفيها، وبالتالي تطبيق القانون ده بيبقى مش مناسب، زي مثلا لما يكون عندي View Model متقسم لمجموعات من الobjects، ونفس الكلام في حالة إني باستخدم Data Transfer Object متقسم لمجموعات من الobjects، الشاهد هنا لما بيكون عندي data objects مافيهاش behavior كتير، القانون ده مش بيبقى مش مناسب قوي.
- مثال ثالث لو هيكلية البيانات data structure مهمة بالنسبة لي، زي مثلا إني أكون بتعامل مع ملف إكسيل، وتقسيم البيانات في الملف (ورقة > جدول > حقل..إلخ) مهم بالنسبة لي لتسهيل الوصول للمعلومات وفهمها، ففي الحالة دي مخالفة القانون مافيهاش حاجة.
- كمان مثال، زي ما قلنا إن هدفنا تقليل الاعتمادية و تحسين الencapsulation، بحيث إن لو حاجة اتغيرت ما اضطرش أغير في حاجات تانية، طيب لو الحاجات دي ما بتتغيرش؟ قشطة مفيش مشكلة من "ترك" الاعتمادية في الحالة دي ومخالفة القانون.

* روح القانون

الحقيقة الحالات الاستثنائية للقانون ده كثيرة، وعشان كده فيه ناس اعترضت على تسميته قانون، وفيه ناس اقترحت تغيير اسمه، زي مارتن فاولر Martin Fowler، اقترح تسميته: اقتراح ديميتر المفيد أحيانا Occasionally Useful Suggestion of Demeter. بل إن فيه ناس قالت عليه مالوش أي تلاتة لازمة، زي ديفيد هانيمر هانسن David Heinemeier Hansson (اللي عمل Ruby On Rails)، قال بالنص:
I think the Law of Demeter is shit and never follow it.
بس خروجا من الخلاف، ممكن نستخدم الاسم الثاني - الأقل شهرة - للقانون ده: مبدأ المعرفة على قدر الحاجة Principle of least knowledge [محدش يقول لي الترجمة غلط، عشان أنا مترجمها بالمعنى مش ترجمة حرفية:)] وده حتى أكثر تعبيرا عن"روح القانون"، فمش لازم الobjects تعرف حاجات كتير بتفاصيل التفاصيل عن الobjects الثانية عشان تعرف تشتغل، إنما الأفضل إنها تعرف اللي هي محتاجاه بس.

* إزاي نطبق القانون ده؟

زي ما قلنا فوق، لو جيت تطبق القانون بحذافيره، هاتلاقي إنك بتعمل wrapper methods كتيرة، ودي حاجة مش ظريفة خالص، والحل هنا ممكن يكون إني أسأل السؤال التالي: هو ليه الobject ده عاوز يعرف تفاصيل الobjects التانية دي؟ ماذا لو طبقنا مبدأ "أؤمر، ما تسألش Tell, Don't Ask" اللي اتكلمنا عنه الحلقة اللي فاتت؟ مش كده نبقى طبقنا القانون واستفدنا من "روحه"؟ فضلا اقرأوا المقالة.

* الالتزام بالقانون على مستوى الarchitecture

فيه حالة واحدة تحضرني دلوقتي، أنا شايف إنه "قانون" ولازم ألتزم بيه مهما حصل، وهو على مستوى الarchitecture مش على مستوى الclasses، ففي حالة إنك مستخدم طبقات layers، كل طبقة مسموح لها تتعامل مع الطبقة اللي تحتها مباشرة بس، ومش مسموح إنها تتجاوز الطبقة اللي تحتها لطبقات أدنى، وده بيخلي الarchitecture بتاعك نظيف وسهل الصيانة نسبيا.

ماتنسوش تشيروا المقالة لو حسيتوا إنها مفيدة 
* مصادر اللي استفدت منها

https://en.wikipedia.org/wiki/Law_of_Demeter
https://dzone.com/articles/the-genius-of-the-law-of-demeter
https://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx/