Legacy Code: How do I add a feature
المقدمة
في المقال السابق اتكلمنا عن كيفيه عمل التغييرات لما بيكون الوقت ضيق ومحتاجين يكون التغيير في وقت سريع ومفيش وقت للتعامل مع تعقيدات الكود القديم
هنا احنا هنتكلم عن لما نحب نضيف ميزة جديدة في الكود ايه الطرق الممكنة والآمنة وهنبدأ بطريقة مشهورة جدا وهي ال TDD.
Test-Driven-Development - TDD
Is software development approach in which test cases are developed to specify and validate what the code will do. In simple terms, test cases for each functionality are created and tested first and if the test fails then the new code is written in order to pass the test and making code simple and bug-free
بعض الناس بتظن ان unit testing وال TDD هو نفس الشئ ولكن في اختلاف ما بينهم، كون ان احنا نكتب كود وبعدين نعمل ال unit tests دا مش بيعتبر TDD، لإن ال TDD بيمشي بالخطوات دي
وهنا احنا في الأول بنكتب ال unit test structure لل feature اللي احنا محتاجين نضيفها وبطبيعه الحال الكود مش هيكون قادر ي compile لإن الال feature اصلا لسه مش موجودة فبنروح للخطوة التانية
هنا احنا بنروح فعليا نكتب ال feature بتاعتنا وشغلها تمام وبعد كدا نرجع لل unit test
هنا بنراجع على ال unit test ولو فيه مشكلة في الكود اللي كتبناه بنصلحها وبنتأكد انها شغالة تمام
بعد كدا ممكن نعمل اي refactor في الكود نخليه بشكل احسن لو الامور تمام يبقى خلاص
ميزة ال TDD من وجهه نظر الكتاب انها بتخلينا مركزين علي مهمة واحدة بس أثناء التعديل ودا اللي بيخلي التعديل أأمن لانه مفيش مشتتات ولا تعديلات في أكتر من مكان في نفس الوقت، ولكن على مستوى العيوب في الطريقة دي انها بتاخد وقت أكبر في تنفيذ العمل.
Programming by difference
الطريقة دي من إضافة ال features بتعتمد بشكل أساسي على ال Inheritance عشن نضيف ال features بدون ما نتعامل مع الكود القديم زي المثال عندنا service مسؤولة عن انها تبعت notification عن طريق ال email
بعد فترة حبينا نضيف نوع تاني من ال notifiction عن طريق ال sms فزي ما بتقول الطريقة دي ان احنا نعمل subclassing من الأساسي ونعمل override لل method دي زي الصورة
ولو حبينا إن احنا نضيف feature جديدة لل notifications زي مثلا push notifications للموبايل فهتكون برضو بالشكل دا
زي ما لاحظنا في الحل دا ان احنا ممكن نضيف feature جديدة بدون ما نغير في الكود القديم ولا نقرب من مشاكله وفي الكود الجديد احنا نقدرنعمل unit test بالشكل المريح لينا، ولكن
لكن الحل دا في عيب كبير جدا لو تم استخدامه بشكل مفرط في الكود وهو مخالفة مبدأ Liskov Substitution from SOLID principles ودا بينص على ايه
Liskov Substitution Principle (LSP) states that objects of a superclass should be replaceable with objects of its subclasses without breaking the application
المبدأ دا بيقول ان المفروض ان احنا نستخدم ال Inheritance في شكل اعادة استخدام الكود اللي موجود في ال parent وأضيف عليه في ال children ولكن فكرة اني اعمل override كتير جوا الكود لل parent methods دا اللي بيؤدي لمخالفة المبدأ ومشاكل في الكود.
طيب ليه دا بسبب مشكلة؟
الفكرة بما ان احنا قلنا ان SmSNotificationSerivce is a NotificationService اللي هو علاقة ال Inheritance فالصحيح ان SmSNotificationSerivce بيعمل نفس اللي بيعمله بالضبط ال NotificationService وممكن الاقي فيه اضافات بالتالي احنا لو عندنا المثال دا
وجه developer بعد مدة طويلة وحب انه يستخدم ال parent class بحيث انه يقدر يخلي الجزء دا generic فهيلاقي على مستوى ال compilation مفيش اي مشكلة وممكن كمان على مستوى ال testing مفيش مشاكل ولكن علي مستوي ال run time هيلاحظ اختلاف وهو ان ال notification بقت بتوصل عن طريق ال email مش sms وهو حتى الآن مش عايز التغيير دا، فهنا يكون المقصود بال LSP violation.
طيب هل فيه حل بديل؟
عشان نتجنب مشكلة LSP violation احنا ممكن نستخدم الحل في الصورة وهو عبارة عن configuration Map بنقدر نبعتها مع ال constructor بحيث ان احنا نقدر نستعمل logic معين بناءا على المتطلبات أثناء كتابة الكود
طبعا في حالة ان الأنواع زادت بالتالي ممكن يكون NotificationSenderService يحصلها violation لل single responsibility بالتالي في الحالة دي احنا ممكن نطلع جزء ال configuration كاملا في class تاني وليكن مثلا NotificationConfiguration ويكون مسؤول عن تحديد النوع المطلوب من ال notification بناءا على criteria معينة.