سمعت مثل هذا التعبير لكي تصبح مبرمجًا ، عليك أن تكون كسولًا. لكن في بعض الأحيان يؤدي الكسل في البرمجة إلى ديون تقنية فظيعة. في مقالتي حول برنامج التقويم الاستراتيجي ، ذكرت أن انتهاك هذا المبدأ يمكن أن يؤدي إلى زيادة التعقيد أو حتى مضاعفته. قدم أحد زملائي مثالًا مثيرًا للاهتمام ، وبمساعدتي ، قررت توضيح كيف يبدو.
دعونا نقرر ما هو هذا التعقيد المفرط. لكن أولاً ، دعنا نتحدث عن العكس ، حول تعقيد المتطلبات. على سبيل المثال ، هناك متطلبات لحساب راتب الموظف من معدل الساعة وساعات العمل. وإذا كان الموظف يعمل في الشركة لأكثر من خمس سنوات ، فيحصل على مكافأة. هذا "إذا" يأتي من المتطلبات ، ولا يمكن تجنبه. بشكل أو بآخر ، سوف يصبح عنصر التعقيد في رمز التطبيق ، على الأرجح في شكل عامل شرطي "إذا". لكن في بعض الأحيان لا ينطلق التعقيد من المتطلبات ، ولكنه يتبع نهج المطور في حل المشكلة.
المشغل "if" ، أنماط مثل "الإستراتيجية" ، الأساليب متعددة الأشكال ليست قائمة كاملة من تقنيات البرمجة التي يمكن أن تحتوي على هذا التعقيد المفرط. أنا شخصياً ، بالمناسبة ، أعارض دائمًا استخدام الأنماط من قبل المطورين لمجرد أنهم يستطيعون وليس لحل مشكلة معينة.
هنا مثال بسيط. قد يبدو وهمية ، لكنها ليست كذلك. لم يتم تبسيطه ، لقد التقيت به في هذا النموذج خلال مراجعة الكود قبل بضع سنوات. في مكانين في الكود ، كانت هناك مكالمات لنفس الوظيفة ولكن مع معلمة منطقية مختلفة:
تصاميم مماثلة دائما تبدو مشبوهة وهذه الوظيفة لم يخيب لي. تم تمرير هذه المعلمة لغرض وحيد يتم فحصه داخل هذه الوظيفة:
doSomething(flag: boolean): void { if(flag) {
يمكن أن يوصف هذا الاختيار بأنه "إذا تم استدعائي من المكان" أ "، فنحن نقوم بشيء واحد ، وإلا تم استدعائي من المكان" ب "، فنحن نقوم بعمل آخر". هذا العلم ، هذا "إذا" هو ما تدور حوله هذه الملاحظة بأكملها. التعقيد لا يأتي من متطلبات العمل. بطبيعة الحال ، أوصيت بتغيير الرمز على النحو التالي:
هذا كل شيء ، ليس هناك المزيد من التعقيد. هذا هو المكان الذي يجب أن لا يكون فيه المطور كسولًا جدًا وكتابة توقيع وظيفة آخر.
هنا يمكنك الهتاف: "ولكن هذا مجرد واحد" إذا "" ، أو: "هذا الانتهاك واضح ، من يكتب الكود هكذا؟" وهنا يأتي المثال الثاني. إنه يوضح أنه قد يكون من الصعب بشكل ملحوظ رؤية الانتهاك ، وأيضًا أن تكلفة هذا الانتهاك يمكن أن تكون أكثر من واحدة فقط "إذا". كما في المثال الأول ، يتم استخدام الوظيفة في مكانين:
يتحقق الأسلوب ، كما يلي من اسمه ، من صحة الكائن. ومع ذلك ، لم يكن من الواضح أنه يمكنه أيضًا التحقق من صلاحية مجموعة من الكائنات. قمت بتعديل أسماء المتغيرات للتأكيد على هذا الانتهاك. الطريقة تبدو هكذا:
checkValidity(parm: MyType | MyType[]): void { if(Array.isArray(parm)) { parm.forEach(p => checkValidity(p)); } else {
ها هو ذا. واحد إذا أصبح الكثير من ifs. إذا كانت المصفوفة تحتوي على 100 كائن ، فسيتم تنفيذ هذا "if" 101 مرة. وعلى البيانات الحقيقية ، يمكن أن يكون لدينا 30 ألف كائن هناك ، وهذه بالفعل خسارة رائعة في الأداء.
من الواضح ، وفقًا لمبدأ المسؤولية الفردية ، يجب إعادة تشكيل هذه الطريقة بحيث يتم الحصول على طريقتين:
checkItemsValidity(parms: MyType[]): void { parms.forEach(p => checkItemValidity(p)); } checkItemValidity(parm: MyType): void {
في المقابل ، تحتاج أيضًا إلى ضبط نقاط الاتصال.
من المثير للاهتمام أن الأمثلة التي قدمتها في مقالة حول SRP أدت إلى زيادة SLOC ، ونفس الأمثلة ، على العكس من ذلك ، تؤدي إلى انخفاض طفيف في ذلك ، إلى جانب التحسن المتوقع في جودة الكود.
هذا كل شيء. فقط بضعة أمثلة بسيطة لإظهار أهم مبدأ رمز جيد.