CodeGym /وبلاگ جاوا /Random-FA /ضد الگوها چیست؟ بیایید به چند نمونه نگاه کنیم (قسمت دوم)
John Squirrels
مرحله
San Francisco

ضد الگوها چیست؟ بیایید به چند نمونه نگاه کنیم (قسمت دوم)

در گروه منتشر شد
ضد الگوها چیست؟ بیایید به چند نمونه نگاه کنیم (قسمت 1) امروز ما به بررسی محبوب ترین ضد الگوها ادامه می دهیم. اگر قسمت اول را از دست دادید، اینجاست . ضد الگوها چیست؟  بیایید به چند نمونه نگاه کنیم (قسمت 2) - 1بنابراین، الگوهای طراحی بهترین شیوه هستند. به عبارت دیگر، آنها نمونه هایی از راه های خوب و آزمایش شده زمان برای حل مسائل خاص هستند. به نوبه خود، ضد الگوها دقیقاً مخالف آنها هستند، به این معنا که آنها الگوهایی از دام یا اشتباه در هنگام حل مشکلات مختلف (الگوهای شیطانی) هستند. بیایید به سراغ ضدالگوی توسعه نرم افزار بعدی برویم.

8. چکش طلایی

چکش طلایی یک ضد الگویی است که با اطمینان از قابل اجرا بودن یک راه حل خاص در سراسر جهان تعریف می شود. مثال ها:
  1. یک برنامه نویس پس از مواجهه با یک مشکل و یافتن الگویی برای راه حل کامل، سعی می کند به جای جستجوی راه حل های مناسب برای موارد خاص، این الگو را در همه جا بچسباند و آن را در پروژه های فعلی و آینده اعمال کند.

  2. برخی از توسعه دهندگان یک بار نوع خود را از کش برای یک موقعیت خاص ایجاد کردند (زیرا هیچ چیز دیگری مناسب نبود). بعداً، در پروژه بعدی که منطق کش خاصی نداشت، آنها دوباره از نوع خود به جای استفاده از کتابخانه های آماده (مثلا Ehcache) استفاده کردند. نتیجه انبوهی از اشکالات و ناسازگاری ها و همچنین اتلاف وقت زیاد و اعصاب سرخ شده بود.

    هرکسی می تواند گرفتار این ضدالگو شود. اگر مبتدی هستید، ممکن است در مورد الگوهای طراحی آگاه نباشید. این ممکن است شما را به سمتی سوق دهد که سعی کنید همه مشکلات را به روشی که بر آن تسلط دارید حل کنید. اگر در مورد حرفه ای ها صحبت می کنیم، پس این را تغییر شکل حرفه ای یا nerdview می نامیم. شما الگوهای طراحی دلخواه خود را دارید و به جای استفاده از الگوی مناسب، از مورد علاقه خود استفاده می کنید، با این فرض که تناسب خوب در گذشته نتیجه مشابه را در آینده تضمین می کند.

    این دام می تواند نتایج بسیار غم انگیزی را به همراه داشته باشد - از اجرای بد، ناپایدار و سخت برای حفظ آن تا شکست کامل پروژه. همانطور که هیچ قرصی برای همه بیماری ها وجود ندارد، الگوی طراحی واحدی برای همه موارد وجود ندارد.

9. بهینه سازی زودرس

بهینه سازی زودرس یک ضد الگو است که نام آن برای خودش صحبت می کند.
"برنامه نویسان زمان زیادی را صرف فکر کردن و نگرانی در مورد مکان های غیر مهم در کد و تلاش برای بهینه سازی آنها می کنند، که فقط بر اشکال زدایی و پشتیبانی بعدی تاثیر منفی می گذارد. به طور کلی باید بهینه سازی را در 97٪ موارد فراموش کنیم. علاوه بر این. بهینه‌سازی زودرس ریشه همه بدی‌ها است. با این حال، ما باید تمام توجه خود را به 3 درصد باقی‌مانده داشته باشیم." - دونالد کنوت
به عنوان مثال، اضافه کردن پیش از موعد ایندکس به یک پایگاه داده. چرا این بد است؟ خوب، بد است که شاخص ها به عنوان یک درخت باینری ذخیره می شوند. در نتیجه، هر بار که یک مقدار جدید اضافه و حذف می شود، درخت دوباره محاسبه می شود و این باعث مصرف منابع و زمان می شود. بنابراین، فهرست‌ها باید فقط در مواقعی که نیاز فوری وجود دارد (اگر حجم زیادی از داده‌ها دارید و پرس‌وجوها بیش از حد طول می‌کشد) و فقط برای مهم‌ترین فیلدها (فیلدهایی که اغلب مورد پرسش قرار می‌گیرند) اضافه شوند.

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 */
   }
}
این زنجیره ما را وادار می کند که به دنبال پاسخ هایی مانند:
  1. چند روش VolkswagenAmarokدارد؟

  2. برای دستیابی به حداکثر انتزاع چه نوع باید به جای علامت سوال درج شود:

    
    ? someObj = new VolkswagenAmarok();
           someObj.brake();
پاسخ سریع به چنین سؤالاتی دشوار است - ما را ملزم می کند که نگاهی بیندازیم و بررسی کنیم، و گیج شدن آسان است. و اگر سلسله مراتب بسیار بزرگ‌تر، طولانی‌تر و پیچیده‌تر باشد، با انواع اضافه‌بارها و نادیده گرفتن‌ها، چه؟ ساختاری که ما خواهیم داشت به دلیل چندپارگی بیش از حد مبهم خواهد بود. بهترین راه حل کاهش تقسیمات غیر ضروری خواهد بود. در مورد ما، فناوری → خودرو → فولکس واگن آماروک را ترک می کنیم.

15. پیچیدگی تصادفی

پیچیدگی غیرضروری ضد الگویی است که در آن عارضه های بی مورد به راه حل معرفی می شود.
"هر احمقی می تواند کدی بنویسد که یک کامپیوتر بتواند آن را بفهمد. برنامه نویسان خوب کدهایی را می نویسند که انسان ها می توانند آن را درک کنند." - مارتین فاولر
پس پیچیدگی چیست؟ می توان آن را به عنوان درجه سختی که هر عملیات در برنامه انجام می شود تعریف کرد. به عنوان یک قاعده، پیچیدگی را می توان به دو نوع تقسیم کرد. اولین نوع پیچیدگی تعداد عملکردهایی است که یک سیستم دارد. تنها به یک روش می توان آن را کاهش داد - با حذف برخی از عملکردها. روش های موجود باید نظارت شود. اگر روشی دیگر استفاده نمی‌شود یا همچنان استفاده می‌شود، اما بدون آوردن هیچ مقداری، باید حذف شود. علاوه بر این، باید ارزیابی کنید که چگونه از همه روش‌های موجود در برنامه استفاده می‌شود، تا بفهمید سرمایه‌گذاری‌ها در کجا ارزش دارد (استفاده مجدد از کد زیاد) و به چه چیزی می‌توانید نه بگویید. نوع دوم پیچیدگی، پیچیدگی غیر ضروری است. تنها با یک رویکرد حرفه ای قابل درمان است. به جای انجام یک کار "سریع" (توسعه دهندگان جوان تنها کسانی نیستند که مستعد ابتلا به این بیماری هستند)، باید به این فکر کنید که چگونه آن را به ساده ترین شکل ممکن انجام دهید، زیرا بهترین راه حل همیشه ساده است. به عنوان مثال، فرض کنید جداول کوچک مرتبط با توضیحات برخی از موجودات، مانند یک کاربر، داریم: ضد الگوها چیست؟  بیایید به چند نمونه نگاه کنیم (قسمت 2) - 3بنابراین، شناسه کاربر، شناسه زبانی که توضیحات در آن ساخته شده است و خود توضیحات را داریم. به طور مشابه، ما توصیفگرهای کمکی برای ماشین ها، فایل ها، پلان ها و جداول مشتریان داریم. سپس درج مقادیر جدید در چنین جداولی چگونه به نظر می رسد؟

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);
   }
راحت، ساده و جمع و جور، فکر نمی کنید؟ نشانه‌ی یک توسعه‌دهنده خوب حتی این نیست که چقدر از الگوها استفاده می‌کند، بلکه این است که چقدر از الگوهای ضد الگو اجتناب می‌کند. جهل بدترین دشمن است، زیرا شما باید دشمنان خود را از روی دید بشناسید. خب، این تمام چیزی است که برای امروز دارم. از همتون سپاسگذارم! :)
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION