هو جيد جاف أو يمكن أن يكسر O من الصلبة

كان مبدأ DRY (لا تكرر نفسك) واضحًا للجميع منذ فترة طويلة وهو محبوب من قبل العديد من المبرمجين. ويتفق الكثيرون على أن النسخ / اللصق ليس رائعًا على الإطلاق. في هذه المقالة ، أريد أن أعطي مثالاً على أنه في البرمجة الصناعية يكون استخدام Copy / Paste أكثر ملاءمة ويساعد على تنفيذ مبدأ Open-Closed بشكل جميل من SOLID.

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

على سبيل المثال ، يوجد نظام إدارة الطرود. لنفترض أن المهمة قد حان لإضافة القدرة على إنشاء حزم عاجلة ، بالإضافة إلى الحزم البسيطة.

لدينا فئة Parcel تصف كيفية عمل الحزمة العادية:

public interface IParcel { string Barcode {get; set;} } public class Parcel: IParcel { public string Barcode {get; set;} } 

من المغري جدًا إضافة حقل ببساطة إلى كل من فئة Parcel القديمة وواجهة IParcel:

 public interface IParcel { string Barcode {get; set;} bool IsUrgent {get; set;} } public class Parcel: IParcel { public string Barcode {get; set;} public bool IsUrgent {get; set;} } 

ومع ذلك ، يجب ألا يجتاز هذا الرمز CodeReview! يجب على مدقق كود صارم وذو خبرة أن يعيدها مع الملاحظة: "مثل هذا التنفيذ ينتهك المبدأ المفتوح."

من الأفضل إنشاء فئة UrgentParcel جديدة ، ولن تحتاج إلى تغيير الواجهة أو فئة Parcel. ستظل ملفات الفصل والواجهة كما هي:

 public class UrgentParcel: IParcel { public string Barcode {get; set;} } 

سيكون هذا احترامًا لمبدأ Open Open ، ولن يتلقى هذا الرمز التعليقات مع CodeReview.

الآن دعنا نعود إلى DRY والطريقة التي تجعل من الصعب تنفيذ مبدأ Open-open.

تخيل أنه في فئة الطرود لدينا حقل "حالة الحزمة" وبعض المنطق لتغيير هذه الحالة:

 public class Parcel: IParcel { public string Barcode {get; set;} //   ( ,     ..) public ParcelStatuses Status {get; } // ,      "" public void ArrivedToRecipient(){ this.Status = ParcelStatuses.Arrived; } } 

هل يجب نسخ هذا المنطق إلى فئة UrgentParcel؟ يقول مبدأ DRY أنه بأي حال من الأحوال. من الأفضل أن ترث فئة UrgentParcel ببساطة من فئة Parcel ، مما يحل المشكلة ولا يلزم نسخ / لصق نص أسلوب ArrivedToRecipient إلى فئة UrgentParcel.

ومع ذلك ، إذا لم تقم بنسخ الشفرة ، ولكنك ورثتها ، فستؤدي التغييرات التي يتم إجراؤها على أسلوب ArrivedToRecipient في فئة Parcel على الفور إلى تغيير في سلوك فئة UrgentParcel ، مما سينتهك مبدأ Open-open. هذا انتهاك بالفعل ، لأن المهمة مع الطرود العاجلة (تنفيذ فئة UrgentParcel) تم تسليمها واختبارها ، ونتيجة لذلك ، تعمل "في المعركة". لذا ، فإن هذا المنطق ، الذي تم تنفيذه في طريقة UrgentParcel.ArrivedToRecipient وتطبيقه على الطرود العاجلة ، يناسب الجميع ولا يجب أن يتغير عندما يتغير عمل أنواع أخرى من الطرود. لذلك ، تم تصميم المبدأ المفتوح المغلق بدقة لحماية النظام من مثل هذه الإجراءات من قبل المبرمجين الصغار عديمي الخبرة الذين ، أثناء حل المشكلة ، كالمعتاد "وجهاً لوجه" ، لا يدركون بعد جميع التبعيات ، وتؤثر تغييراتهم في مكان واحد على العديد من المجالات الوظيفية الأخرى .

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

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

هنا مثال لمثل هذا التعليق:

 public class Parcel: IParcel{ ... /// <summary> ///    "" /// </summary> /// <remarks>NOTE:     : <see cref="UrgentParcel"/></remarks> public void ArrivedToRecipient(){ ... } } public class UrgentParcel: IParcel{ ... /// <summary> ///    "" /// </summary> /// <remarks>NOTE:      : <see cref="Parcel"/></remarks> public void ArrivedToRecipient(){ ... } } 

من المثير للاهتمام رأي المجتمع في هذه القضية. شكرا مقدما لتعليقاتك.

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


All Articles