أكثر من التعقيد

سمعت مثل هذا التعبير لكي تصبح مبرمجًا ، عليك أن تكون كسولًا. لكن في بعض الأحيان يؤدي الكسل في البرمجة إلى ديون تقنية فظيعة. في مقالتي حول برنامج التقويم الاستراتيجي ، ذكرت أن انتهاك هذا المبدأ يمكن أن يؤدي إلى زيادة التعقيد أو حتى مضاعفته. قدم أحد زملائي مثالًا مثيرًا للاهتمام ، وبمساعدتي ، قررت توضيح كيف يبدو.



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

المشغل "if" ، أنماط مثل "الإستراتيجية" ، الأساليب متعددة الأشكال ليست قائمة كاملة من تقنيات البرمجة التي يمكن أن تحتوي على هذا التعقيد المفرط. أنا شخصياً ، بالمناسبة ، أعارض دائمًا استخدام الأنماط من قبل المطورين لمجرد أنهم يستطيعون وليس لحل مشكلة معينة.

هنا مثال بسيط. قد يبدو وهمية ، لكنها ليست كذلك. لم يتم تبسيطه ، لقد التقيت به في هذا النموذج خلال مراجعة الكود قبل بضع سنوات. في مكانين في الكود ، كانت هناك مكالمات لنفس الوظيفة ولكن مع معلمة منطقية مختلفة:

// first place doSomething(true); // second place doSomething(false); 

تصاميم مماثلة دائما تبدو مشبوهة وهذه الوظيفة لم يخيب لي. تم تمرير هذه المعلمة لغرض وحيد يتم فحصه داخل هذه الوظيفة:

 doSomething(flag: boolean): void { if(flag) { // do first thing } else { // do second thing } } 

يمكن أن يوصف هذا الاختيار بأنه "إذا تم استدعائي من المكان" أ "، فنحن نقوم بشيء واحد ، وإلا تم استدعائي من المكان" ب "، فنحن نقوم بعمل آخر". هذا العلم ، هذا "إذا" هو ما تدور حوله هذه الملاحظة بأكملها. التعقيد لا يأتي من متطلبات العمل. بطبيعة الحال ، أوصيت بتغيير الرمز على النحو التالي:

 // first place doFirstThing(); // second place doSecondThing(); //method is split into 2 parts each having their own responsibility doFirstThing(): void { // do first thing } doSecondThing(): void { // do second thing } 

هذا كل شيء ، ليس هناك المزيد من التعقيد. هذا هو المكان الذي يجب أن لا يكون فيه المطور كسولًا جدًا وكتابة توقيع وظيفة آخر.

هنا يمكنك الهتاف: "ولكن هذا مجرد واحد" إذا "" ، أو: "هذا الانتهاك واضح ، من يكتب الكود هكذا؟" وهنا يأتي المثال الثاني. إنه يوضح أنه قد يكون من الصعب بشكل ملحوظ رؤية الانتهاك ، وأيضًا أن تكلفة هذا الانتهاك يمكن أن تكون أكثر من واحدة فقط "إذا". كما في المثال الأول ، يتم استخدام الوظيفة في مكانين:

 // first place checkValidity(obj); // second place checkValidity(arrayOfObjs); 

يتحقق الأسلوب ، كما يلي من اسمه ، من صحة الكائن. ومع ذلك ، لم يكن من الواضح أنه يمكنه أيضًا التحقق من صلاحية مجموعة من الكائنات. قمت بتعديل أسماء المتغيرات للتأكيد على هذا الانتهاك. الطريقة تبدو هكذا:

 checkValidity(parm: MyType | MyType[]): void { if(Array.isArray(parm)) { parm.forEach(p => checkValidity(p)); } else { // here the object gets checked // and conditional exception is thrown } } 

ها هو ذا. واحد إذا أصبح الكثير من ifs. إذا كانت المصفوفة تحتوي على 100 كائن ، فسيتم تنفيذ هذا "if" 101 مرة. وعلى البيانات الحقيقية ، يمكن أن يكون لدينا 30 ألف كائن هناك ، وهذه بالفعل خسارة رائعة في الأداء.

من الواضح ، وفقًا لمبدأ المسؤولية الفردية ، يجب إعادة تشكيل هذه الطريقة بحيث يتم الحصول على طريقتين:

 checkItemsValidity(parms: MyType[]): void { parms.forEach(p => checkItemValidity(p)); } checkItemValidity(parm: MyType): void { // here the object gets checked // and conditional exception is thrown } 

في المقابل ، تحتاج أيضًا إلى ضبط نقاط الاتصال.

من المثير للاهتمام أن الأمثلة التي قدمتها في مقالة حول SRP أدت إلى زيادة SLOC ، ونفس الأمثلة ، على العكس من ذلك ، تؤدي إلى انخفاض طفيف في ذلك ، إلى جانب التحسن المتوقع في جودة الكود.

هذا كل شيء. فقط بضعة أمثلة بسيطة لإظهار أهم مبدأ رمز جيد.

Source: https://habr.com/ru/post/ar457018/


All Articles