8. المطرقة الذهبية
المطرقة الذهبية هي نمط مضاد تحدده الثقة في أن حلاً معينًا قابل للتطبيق عالميًا. أمثلة:-
بعد مواجهة مشكلة وإيجاد نمط للحل الأمثل، يحاول المبرمج لصق هذا النمط في كل مكان، وتطبيقه على المشاريع الحالية وجميع المشاريع المستقبلية، بدلاً من البحث عن الحلول المناسبة لحالات معينة.
-
قام بعض المطورين ذات مرة بإنشاء نسخة خاصة بهم من ذاكرة التخزين المؤقت لموقف معين (لأنه لم يكن هناك أي شيء آخر مناسب). لاحقًا، في المشروع التالي الذي لم يتضمن منطقًا خاصًا لذاكرة التخزين المؤقت، استخدموا متغيرهم مرة أخرى بدلاً من استخدام المكتبات الجاهزة (على سبيل المثال، Ehcache). وكانت النتيجة مجموعة من الأخطاء وعدم التوافق، بالإضافة إلى الكثير من الوقت الضائع والأعصاب المحمومة.
يمكن لأي شخص أن يقع في حب هذا النمط المضاد. إذا كنت مبتدئًا، فقد لا تكون على دراية بأنماط التصميم. قد يقودك هذا إلى محاولة حل جميع المشكلات بالطريقة الوحيدة التي أتقنتها. إذا كنا نتحدث عن المحترفين، فإننا نسمي هذا التشوه المهني أو "النظرة المهووسة". لديك أنماط التصميم المفضلة لديك، وبدلاً من استخدام النمط الصحيح، يمكنك استخدام المفضل لديك، على افتراض أن الملاءمة الجيدة في الماضي تضمن نفس النتيجة في المستقبل.
يمكن أن يؤدي هذا المأزق إلى نتائج حزينة للغاية - بدءًا من التنفيذ السيئ وغير المستقر وصعوبة الحفاظ عليه وحتى الفشل الكامل للمشروع. وكما لا يوجد قرص واحد لجميع الأمراض، لا يوجد نمط تصميم واحد لجميع المناسبات.
9. التحسين المبكر
التحسين المبكر هو نمط مضاد اسمه يتحدث عن نفسه.10. كود السباغيتي
كود السباغيتي هو نمط مضاد يتم تحديده بواسطة كود سيئ التنظيم ومربك ويصعب فهمه، ويحتوي على جميع أنواع التفرع، مثل استثناءات الالتفاف والشروط والحلقات. في السابق، كان عامل goto هو الحليف الرئيسي لهذا النمط المضاد. لم تعد عبارات Goto تُستخدم حقًا بعد الآن، مما يزيل عددًا من الصعوبات والمشاكل المرتبطة بها.public boolean someDifficultMethod(List<String> XMLAttrList) {
...
int prefix = stringPool.getPrefixForQName(elementType);
int elementURI;
try {
if (prefix == -1) {
...
if (elementURI != -1) {
stringPool.setURIForQName(...);
}
} else {
...
if (elementURI == -1) {
...
}
}
} catch (Exception e) {
return false;
}
if (attrIndex != -1) {
int index = attrList.getFirstAttr(attrIndex);
while (index != -1) {
int attName = attrList.getAttrName(index);
if (!stringPool.equalNames(...)){
...
if (attPrefix != namespacesPrefix) {
if (attPrefix == -1) {
...
} else {
if (uri == -1) {
...
}
stringPool.setURIForQName(attName, uri);
...
}
if (elementDepth >= 0) {
...
}
elementDepth++;
if (elementDepth == fElementTypeStack.length) {
...
}
...
return contentSpecType == fCHILDRENSymbol;
}
}
}
}
}
يبدو فظيعا، أليس كذلك؟ لسوء الحظ، هذا هو النمط المضاد الأكثر شيوعًا :( حتى الشخص الذي يكتب مثل هذا الرمز لن يتمكن من فهمه في المستقبل. سيفكر المطورون الآخرون الذين يرون الكود، "حسنًا، إذا كان يعمل، فلا بأس — من الأفضل عدم لمسها". في كثير من الأحيان، تكون الطريقة في البداية بسيطة وشفافة جدًا، ولكن مع إضافة متطلبات جديدة، تصبح الطريقة مثقلة تدريجيًا بالمزيد والمزيد من العبارات الشرطية، مما يحولها إلى وحشية مثل هذه. إذا كانت هذه الطريقة يبدو أنك بحاجة إلى إعادة هيكلته إما بشكل كامل أو على الأقل الأجزاء الأكثر إرباكًا. عادةً، عند جدولة مشروع، يتم تخصيص وقت لإعادة البناء، على سبيل المثال، 30% من وقت السبرنت مخصص لإعادة البناء والاختبارات. بالطبع، هذا يفترض أنه لا يوجد أي استعجال (ولكن متى يحدث ذلك). يمكنك العثور على مثال جيد لرمز السباغيتي وإعادة هيكلته هنا
.
11. الأرقام السحرية
الأرقام السحرية هي نمط مضاد يتم فيه استخدام جميع أنواع الثوابت في البرنامج دون أي تفسير للغرض منها أو معناها. أي أن تسميتها بشكل عام سيئة أو في الحالات القصوى، لا يوجد تعليق يشرح ماهية التعليقات أو سببها. مثل رمز السباغيتي، يعد هذا أحد أكثر الأنماط المضادة شيوعًا. الشخص الذي لم يكتب الكود قد يكون لديه أو لا يكون لديه أدنى فكرة عن الأرقام السحرية أو كيفية عملها (وبمرور الوقت، لن يتمكن المؤلف نفسه من شرحها). ونتيجة لذلك، يؤدي تغيير رقم أو إزالته إلى توقف الكود بطريقة سحرية عن العمل معًا. على سبيل المثال، 36 و 73 . لمحاربة هذا النمط المضاد، أوصي بمراجعة الكود. يجب أن يتم الاطلاع على الكود الخاص بك من قبل المطورين الذين لا يشاركون في الأقسام ذات الصلة من الكود. ستكون عيونهم جديدة وسيكون لديهم أسئلة: ما هذا ولماذا فعلت ذلك؟ وبالطبع، تحتاج إلى استخدام أسماء توضيحية أو ترك تعليقات.12. برمجة النسخ واللصق
برمجة النسخ واللصق هي نمط مضاد حيث يتم نسخ ولصق التعليمات البرمجية الخاصة بشخص آخر دون تفكير، مما قد يؤدي إلى آثار جانبية غير متوقعة. على سبيل المثال، طرق النسخ واللصق ذات الحسابات الرياضية أو الخوارزميات المعقدة التي لا نفهمها بشكل كامل. قد ينجح هذا في حالتنا الخاصة، ولكن في بعض الظروف الأخرى قد يؤدي إلى مشاكل. لنفترض أنني بحاجة إلى طريقة لتحديد الحد الأقصى للعدد في المصفوفة. ومن خلال البحث في الانترنت وجدت هذا الحل:public static int max(int[] array) {
int max = 0;
for(int i = 0; i < array.length; i++) {
if (Math.abs(array[i]) > max){
max = array[i];
}
}
return max;
}
نحصل على مصفوفة بالأرقام 3 و6 و1 و4 و2، وترجع الطريقة 6. رائع، لنحتفظ بها! لكن لاحقًا حصلنا على مصفوفة تتكون من 2.5، -7، 2، و3، والنتيجة هي -7. وهذه النتيجة ليست جيدة. المشكلة هنا هي أن Math.abs() تُرجع القيمة المطلقة. الجهل بهذا يؤدي إلى الكارثة، ولكن في حالات معينة فقط. بدون فهم متعمق للحل، هناك العديد من الحالات التي لن تتمكن من التحقق منها. قد تتجاوز التعليمات البرمجية المنسوخة أيضًا البنية الداخلية للتطبيق، سواء من الناحية الأسلوبية أو على المستوى المعماري الأساسي. سيكون مثل هذا الرمز أكثر صعوبة في القراءة والصيانة. وبالطبع، يجب ألا ننسى أن نسخ كود شخص آخر بشكل مباشر هو نوع خاص من السرقة الأدبية. في تلك الحالات عندما لا يفهم المبرمج تمامًا ما يفعله ويقرر استخدام الحل الذي يفترض أنه يعمل من قبل شخص آخر، فإن هذا لا يدل على نقص المثابرة فحسب، بل إن هذه الإجراءات تضر أيضًا بالفريق والمشروع. وأحيانًا الشركة بأكملها (لذا انسخ والصق بعناية).
13. إعادة اختراع العجلة
إعادة اختراع العجلة هو نمط مضاد، يُعرف أيضًا أحيانًا باسم إعادة اختراع العجلة المربعة . في جوهره، هذا القالب هو عكس نمط النسخ واللصق المضاد المذكور أعلاه. في هذا النمط المضاد، يقوم المطور بتنفيذ الحل الخاص به لمشكلة توجد لها حلول بالفعل. في بعض الأحيان تكون هذه الحلول الموجودة أفضل مما يخترعه المبرمج. في أغلب الأحيان، يؤدي هذا فقط إلى ضياع الوقت وانخفاض الإنتاجية: قد لا يجد المبرمج حلاً على الإطلاق أو قد يجد حلاً بعيدًا عن الأفضل. ومع ذلك، لا يمكننا استبعاد إمكانية إنشاء حل مستقل، لأن القيام بذلك هو طريق مباشر لبرمجة النسخ واللصق. ويجب أن يسترشد المبرمج بالمهام البرمجية المحددة التي تظهر ليتمكن من حلها بكفاءة، سواء باستخدام الحلول الجاهزة أو عن طريق إنشاء حلول مخصصة. في كثير من الأحيان، يكون سبب استخدام هذا النمط المضاد هو التسرع ببساطة. والنتيجة هي تحليل سطحي (للبحث عن) الحلول الجاهزة. إن إعادة اختراع العجلة المربعة هي الحالة التي يكون فيها للنمط المضاد قيد النظر نتيجة سلبية. أي أن المشروع يتطلب حلاً مخصصًا، ويقوم المطور بإنشائه، ولكن بشكل سيء. وفي الوقت نفسه، يوجد بالفعل خيار جيد ويستخدمه الآخرون بنجاح. خلاصة القول: يتم فقدان قدر كبير من الوقت. أولاً، نقوم بإنشاء شيء لا يعمل. ثم نحاول إعادة هيكلته، وفي النهاية نستبدله بشيء موجود بالفعل. أحد الأمثلة على ذلك هو تنفيذ ذاكرة التخزين المؤقت المخصصة الخاصة بك عند وجود الكثير من التطبيقات بالفعل. بغض النظر عن مدى موهبتك كمبرمج، يجب أن تتذكر أن إعادة اختراع عجلة مربعة هو على الأقل مضيعة للوقت. وكما تعلمون، الوقت هو المورد الأكثر قيمة.14. مشكلة اليويو
مشكلة اليويو هي نمط مضاد تكون فيه بنية التطبيق معقدة للغاية بسبب التجزئة المفرطة (على سبيل المثال، سلسلة الميراث المقسمة بشكل مفرط). تنشأ "مشكلة اليويو" عندما تحتاج إلى فهم برنامج يكون تسلسله الهرمي للوراثة طويلًا ومعقدًا، مما يؤدي إلى إنشاء استدعاءات أسلوب متداخلة بعمق. ونتيجة لذلك، يحتاج المبرمجون إلى التنقل بين العديد من الفئات والأساليب المختلفة من أجل فحص سلوك البرنامج. يأتي اسم هذا النمط المضاد من اسم اللعبة. على سبيل المثال، دعونا نلقي نظرة على سلسلة الميراث التالية: لدينا واجهة تقنية:public interface Technology {
void turnOn();
}
ترثها واجهة النقل:
public interface Transport extends Technology {
boolean fillUp();
}
ثم لدينا واجهة أخرى، GroundTransport:
public interface GroundTransportation extends Transport {
void startMove();
void brake();
}
ومن هناك، نستمد فئة السيارة المجردة:
public abstract class Car implements GroundTransportation {
@Override
public boolean fillUp() {
/* some implementation */
return true;
}
@Override
public void turnOn() {
/* some implementation */
}
public boolean openTheDoor() {
/* some implementation */
return true;
}
public abstract void fixCar();
}
التالي هو فئة فولكس فاجن المجردة:
public abstract class Volkswagen extends Car {
@Override
public void startMove() {
/* some implementation */
}
@Override
public void brake() {
/* some implementation */
}
}
وأخيرًا نموذج محدد:
public class VolkswagenAmarok extends Volkswagen {
@Override
public void fixCar(){
/* some implementation */
}
}
تجبرنا هذه السلسلة على البحث عن إجابات لأسئلة مثل:
-
كم عدد الأساليب لديها
VolkswagenAmarok
؟ -
ما هو النوع الذي يجب إدراجه بدلاً من علامة الاستفهام لتحقيق أقصى قدر من التجريد:
? someObj = new VolkswagenAmarok(); someObj.brake();
15. التعقيد العرضي
التعقيد غير الضروري هو نمط مضاد يتم فيه إدخال تعقيدات غير ضرورية إلى الحل.public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description)throws Exception {
switch (type){
case CAR:
jdbcTemplate.update(CREATE_RELATION_WITH_CAR, languageId, serviceId, description);
case USER:
jdbcTemplate.update(CREATE_RELATION_WITH_USER, languageId, serviceId, description);
case FILE:
jdbcTemplate.update(CREATE_RELATION_WITH_FILE, languageId, serviceId, description);
case PLAN:
jdbcTemplate.update(CREATE_RELATION_WITH_PLAN, languageId, serviceId, description);
case CUSTOMER:
jdbcTemplate.update(CREATE_RELATION_WITH_CUSTOMER, languageId, serviceId, description);
default:
throw new Exception();
}
}
وبناء على ذلك، لدينا هذا التعداد:
public enum ServiceType {
CAR(),
USER(),
FILE(),
PLAN(),
CUSTOMER()
}
يبدو أن كل شيء بسيط وجيد... ولكن ماذا عن الطرق الأخرى؟ في الواقع، سيكون لديهم أيضًا مجموعة من switch
البيانات ومجموعة من استعلامات قاعدة البيانات المتطابقة تقريبًا، والتي بدورها ستؤدي إلى تعقيد فصلنا وتضخمه بشكل كبير. كيف يمكن جعل كل هذا أسهل؟ دعونا نقوم بترقية التعداد الخاص بنا قليلاً:
@Getter
@AllArgsConstructor
public enum ServiceType {
CAR("cars_descriptions", "car_id"),
USER("users_descriptions", "user_id"),
FILE("files_descriptions", "file_id"),
PLAN("plans_descriptions", "plan_id"),
CUSTOMER("customers_descriptions", "customer_id");
private String tableName;
private String columnName;
}
الآن أصبح لكل نوع أسماء الحقول الأصلية لجدوله. ونتيجة لذلك، تصبح طريقة إنشاء الوصف:
private static final String CREATE_RELATION_WITH_SERVICE = "INSERT INTO %s(language_id, %s, description) VALUES (?, ?, ?)";
public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description) {
jdbcTemplate.update(String.format(CREATE_RELATION_WITH_SERVICE, type.getTableName(), type.getColumnName()), languageId, serviceId, description);
}
مريحة وبسيطة وصغيرة الحجم، ألا تعتقد ذلك؟ إن الإشارة إلى المطور الجيد ليست حتى في عدد المرات التي يستخدم فيها الأنماط، بل في عدد المرات التي يتجنب فيها الأنماط المضادة. الجهل هو أسوأ عدو، لأنك تحتاج إلى معرفة أعداءك عن طريق البصر. حسنا، هذا كل ما لدي لهذا اليوم. شكرا لكل شخص! :)
GO TO FULL VERSION