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

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

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

1. فلج تحلیلی

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

2. خدا مخالفت کند

یک شی خدا یک ضد الگو است که غلظت بیش از حد انواع توابع و مقادیر زیادی از داده های متفاوت را توصیف می کند (شیئی که برنامه در اطراف آن می چرخد). یک مثال کوچک بزنید:
public class SomeUserGodObject {
   private static final String FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;
   private static final String FIND_BY_ID = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";
   private static final String FIND_ALL_CUSTOMERS = "SELECT id, u.email, u.phone, u.first_name_en, u.middle_name_en, u.last_name_en, u.created_date" +
           "  WHERE u.id IN (SELECT up.user_id FROM user_permissions up WHERE up.permission_id = ?)";
   private static final String FIND_BY_EMAIL = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_dateFROM users WHERE email = ?";
   private static final String LIMIT_OFFSET = " LIMIT ? OFFSET ?";
   private static final String ORDER = " ORDER BY ISNULL(last_name_en), last_name_en, ISNULL(first_name_en), first_name_en, ISNULL(last_name_ru), " +
           "last_name_ru, ISNULL(first_name_ru), first_name_ru";
   private static final String CREATE_USER_EN = "INSERT INTO users(id, phone, email, first_name_en, middle_name_en, last_name_en, created_date) " +
           "VALUES (?, ?, ?, ?, ?, ?, ?)";
   private static final String FIND_ID_BY_LANG_CODE = "SELECT id FROM languages WHERE lang_code = ?";
                                  ........
   private final JdbcTemplate jdbcTemplate;
   private Map<String, String> firstName;
   private Map<String, String> middleName;
   private Map<String, String> lastName;
   private List<Long> permission;
                                   ........
   @Override
   public List<User> findAllEnCustomers(Long permissionId) {
       return jdbcTemplate.query( FIND_ALL_CUSTOMERS + ORDER, userRowMapper(), permissionId);
   }
   @Override
   public List<User> findAllEn() {
       return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER, userRowMapper());
   }
   @Override
   public Optional<List<User>> findAllEnByEmail(String email) {
       var query = FIND_ALL_USERS_EN + FIND_BY_EMAIL + ORDER;
       return Optional.ofNullable(jdbcTemplate.query(query, userRowMapper(), email));
   }
                              .............
   private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
       switch (type) {
           case USERS:
               return findAllEnUsers(permissionId);
           case CUSTOMERS:
               return findAllEnCustomers(permissionId);
           default:
               return findAllEn();
       }
   }
                              ..............private RowMapper<User> userRowMapperEn() {
       return (rs, rowNum) ->
               User.builder()
                       .id(rs.getLong("id"))
                       .email(rs.getString("email"))
                       .accessFailed(rs.getInt("access_counter"))
                       .createdDate(rs.getObject("created_date", LocalDateTime.class))
                       .firstName(rs.getString("first_name_en"))
                       .middleName(rs.getString("middle_name_en"))
                       .lastName(rs.getString("last_name_en"))
                       .phone(rs.getString("phone"))
                       .build();
   }
}
در اینجا ما یک کلاس بزرگ را می بینیم که همه کارها را انجام می دهد. این شامل پرس و جوهای پایگاه داده و همچنین برخی از داده ها است. همچنین متد نمای findAllWithoutPageEn را می بینیم که شامل منطق تجاری است. چنین شیء خدایی برای نگهداری صحیح بسیار بزرگ و ناخوشایند می شود. ما باید در هر کدی با آن درگیر شویم. بسیاری از اجزای سیستم به آن متکی هستند و به شدت با آن همراه هستند. حفظ چنین کدی سخت تر و سخت تر می شود. در چنین مواردی، کد باید به کلاس های جداگانه تقسیم شود که هر کدام تنها یک هدف دارند. در این مثال، می‌توانیم شی God را به یک کلاس Dao تقسیم کنیم:
public class UserDaoImpl {
   private static final String FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;
   private static final String FIND_BY_ID = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";

                                   ........
   private final JdbcTemplate jdbcTemplate;

                                   ........
   @Override
   public List<User> findAllEnCustomers(Long permissionId) {
       return jdbcTemplate.query(FIND_ALL_CUSTOMERS + ORDER, userRowMapper(), permissionId);
   }
   @Override
   public List<User> findAllEn() {
       return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER, userRowMapper());
   }

                               ........
}
یک کلاس حاوی داده ها و روش هایی برای دسترسی به داده ها:
public class UserInfo {
   private Map<String, String> firstName;..
   public Map<String, String> getFirstName() {
       return firstName;
   }
   public void setFirstName(Map<String, String> firstName) {
       this.firstName = firstName;
   }
                    ....
و انتقال روش با منطق تجاری به یک سرویس مناسب تر است:
private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
   switch (type) {
       case USERS:
           return findAllEnUsers(permissionId);
       case CUSTOMERS:
           return findAllEnCustomers(permissionId);
       default:
           return findAllEn();
   }
}

3. سینگلتون

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

  2. یک سینگلتون اصول SOLID - اصل مسئولیت واحد را نقض می کند: کلاس سینگلتون علاوه بر وظایف مستقیم خود، تعداد نمونه ها را نیز کنترل می کند.

  3. وابستگی یک کلاس معمولی به singleton در رابط کلاس قابل مشاهده نیست. از آنجایی که یک نمونه singleton معمولاً به عنوان آرگومان متد ارسال نمی‌شود، بلکه مستقیماً از طریق getInstance() به دست می‌آید، باید وارد پیاده‌سازی هر متد شوید تا وابستگی کلاس به singleton را شناسایی کنید - فقط به عمومی یک کلاس نگاه کنید. قرارداد کافی نیست

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

    به عبارت دیگر، تک تنه جفت شدن را افزایش می دهد و هر آنچه در بالا ذکر شد چیزی جز نتیجه افزایش جفت نیست.

    و اگر در مورد آن فکر کنید، می توانید از استفاده از تک تن خودداری کنید. برای مثال، استفاده از انواع کارخانه‌ها برای کنترل تعداد نمونه‌های یک شی کاملاً ممکن است (و در واقع ضروری است).

    بزرگترین خطر در تلاش برای ساخت کل معماری برنامه بر اساس تک‌تون‌ها نهفته است. هزاران جایگزین شگفت انگیز برای این رویکرد وجود دارد. مهمترین مثال Spring، یعنی ظروف IoC آن است: آنها یک راه حل طبیعی برای مشکل کنترل ایجاد خدمات هستند، زیرا آنها در واقع "کارخانه های استروئیدی" هستند.

    در حال حاضر بسیاری از بحث های پایان ناپذیر و آشتی ناپذیر در مورد این موضوع در جریان است. این شما هستید که تصمیم می گیرید تک تک الگو باشد یا ضد الگو.

    ما در آن معطل نخواهیم شد. در عوض، ما به آخرین الگوی طراحی برای امروز خواهیم رفت - poltergeist.

4. Poltergeist

poltergeist یک ضد الگو شامل یک کلاس بی معنی است که برای فراخوانی متدهای یک کلاس دیگر استفاده می شود یا به سادگی یک لایه غیرضروری از انتزاع اضافه می کند . این ضد الگو خود را به صورت اشیاء کوتاه مدت و فاقد حالت نشان می دهد. این اشیاء اغلب برای مقداردهی اولیه سایر اشیاء دائمی تر استفاده می شوند.
public class UserManager {
   private UserService service;
   public UserManager(UserService userService) {
       service = userService;
   }
   User createUser(User user) {
       return service.create(user);
   }
   Long findAllUsers(){
       return service.findAll().size();
   }
   String findEmailById(Long id) {
       return service.findById(id).getEmail();}
   User findUserByEmail(String email) {
       return service.findByEmail(email);
   }
   User deleteUserById(Long id) {
       return service.delete(id);
   }
}
چرا ما به یک شی نیاز داریم که فقط یک واسطه باشد و کار خود را به دیگری واگذار کند؟ ما آن را حذف می کنیم و عملکرد کمی که داشت را به اشیاء با عمر طولانی منتقل می کنیم. در مرحله بعد، به سراغ الگوهایی می رویم که بیشتر مورد علاقه ما (به عنوان توسعه دهندگان عادی) هستند، یعنی ضد الگوهای توسعه .

5. کدنویسی سخت

بنابراین ما به این کلمه وحشتناک رسیدیم: کدنویسی سخت. ماهیت این ضد الگو این است که کد به شدت به یک پیکربندی سخت افزاری و/یا محیط سیستم خاص گره خورده است. این امر انتقال کد به پیکربندی های دیگر را بسیار پیچیده می کند. این ضد الگو ارتباط نزدیکی با اعداد جادویی دارد (این ضد الگوها اغلب در هم تنیده می شوند). مثال:
public Connection buildConnection() throws Exception {
   Class.forName("com.mysql.cj.jdbc.Driver");
   connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8&characterSetResults=UTF-8&serverTimezone=UTC", "user01", "12345qwert");
   return connection;
}
درد داره، نه؟ در اینجا ما تنظیمات اتصال خود را کدگذاری سخت می کنیم. در نتیجه، کد فقط با MySQL به درستی کار می کند. برای تغییر پایگاه داده، باید کد را وارد کنیم و همه چیز را به صورت دستی تغییر دهیم. یک راه حل خوب این است که پیکربندی را در یک فایل جداگانه قرار دهید:
spring:
  datasource:
    jdbc-url:jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username:  user01
    password:  12345qwert
گزینه دیگر استفاده از ثابت است.

6. لنگر قایق

در زمینه ضد الگوها، لنگر قایق به معنای نگه داشتن بخش هایی از سیستم است که پس از انجام برخی بهینه سازی ها یا بازسازی ها دیگر مورد استفاده قرار نمی گیرند. همچنین، برخی از بخش‌های کد را می‌توان «برای استفاده در آینده» نگه داشت، فقط در صورت نیاز ناگهانی به آن‌ها. در اصل، این کد شما را به زباله دانی تبدیل می کند. مثال:
public User update(Long id, User request) {
   User user = mergeUser(findById(id), request);
   return userDAO.update(user);
}
private User mergeUser(User findUser, User requestUser) {
   return new User(
           findUser.getId(),
           requestUser.getEmail() != null ? requestUser.getEmail() : findUser.getEmail(),
           requestUser.getFirstName() != null ? requestUser.getFirstName() : findUser.getFirstNameRu(),
           requestUser.getMiddleName() != null ? requestUser.getMiddleName() : findUser.getMiddleNameRu(),
           requestUser.getLastName() != null ? requestUser.getLastName() : findUser.getLastNameEn(),
           requestUser.getPhone() != null ? requestUser.getPhone() : findUser.getPhone());
}
ما یک روش به روز رسانی داریم که از یک روش جداگانه برای ادغام داده های کاربر از پایگاه داده با داده های کاربر ارسال شده به متد استفاده می کند (اگر کاربر ارسال شده به روش به روز رسانی یک فیلد تهی داشته باشد، مقدار فیلد قدیمی از پایگاه داده گرفته می شود) . سپس فرض کنید یک شرط جدید وجود دارد که رکوردها نباید با رکوردهای قدیمی ادغام شوند، اما در عوض، حتی اگر فیلدهای خالی وجود داشته باشند، برای بازنویسی موارد قدیمی استفاده می شود:
public User update(Long id, User request) {
   return userDAO.update(user);
}
این بدان معنی است که mergeUser دیگر استفاده نمی شود، اما حیف است که آن را حذف کنید - اگر این روش (یا ایده این روش) روزی به کارتان بیاید، چه؟ چنین کدی فقط سیستم ها را پیچیده می کند و باعث سردرگمی می شود و اساساً هیچ ارزش عملی ندارد. نباید فراموش کنیم که وقتی برای پروژه دیگری می روید، چنین کدی با "تکه های مرده" به سختی به یک همکار منتقل می شود. بهترین راه برای مقابله با لنگرهای قایق، اصلاح مجدد کد است، یعنی حذف بخش هایی از کد (من می دانم که دلخراش است). علاوه بر این، هنگام تهیه برنامه توسعه، لازم است چنین لنگرهایی در نظر گرفته شود (برای تخصیص زمان برای مرتب کردن).

7. آبریز آب

برای تشریح این ضدالگو ابتدا باید با الگوی استخر آبجکت آشنا شوید . یک مخزن شی (حوضه منابع) یک الگوی طراحی خلاقانه است ، مجموعه ای از اشیاء اولیه و آماده برای استفاده. هنگامی که یک برنامه به یک شی نیاز دارد، به جای اینکه دوباره ساخته شود، از این استخر گرفته می شود. هنگامی که یک شی دیگر مورد نیاز نیست، از بین نمی رود. در عوض، به استخر برگردانده می شود. این الگو معمولاً برای اجسام سنگینی استفاده می‌شود که ایجاد آنها در هر زمانی که نیاز است، زمان‌بر است، مانند هنگام اتصال به پایگاه داده. بیایید به یک مثال کوچک و ساده نگاه کنیم. در اینجا کلاسی وجود دارد که این الگو را نشان می دهد:
class ReusablePool {
   private static ReusablePool pool;
   private List<Resource> list = new LinkedList<>();
   private ReusablePool() {
       for (int i = 0; i < 3; i++)
           list.add(new Resource());
   }
   public static ReusablePool getInstance() {
       if (pool == null) {
           pool = new ReusablePool();
       }
       return pool;
   }
   public Resource acquireResource() {
       if (list.size() == 0) {
           return new Resource();
       } else {
           Resource r = list.get(0);
           list.remove(r);
           return r;
       }
   }
   public void releaseResource(Resource r) {
       list.add(r);
   }
}
این کلاس در قالب الگوی/ضد الگوی تک‌تنه فوق ارائه می‌شود، یعنی فقط یک شی از این نوع می‌تواند وجود داشته باشد. از اشیاء خاصی استفاده می کند Resource. به طور پیش فرض سازنده، استخر را با 4 نمونه پر می کند. هنگامی که یک شی را دریافت می کنید، از استخر حذف می شود (اگر شی در دسترس نباشد، یکی ایجاد می شود و بلافاصله برگردانده می شود). و در پایان روشی برای برگرداندن آبجکت داریم. اشیاء منبع به شکل زیر هستند:
public class Resource {
   private Map<String, String> patterns;
   public Resource() {
       patterns = new HashMap<>();
       patterns.put("proxy", "https://en.wikipedia.org/wiki/Proxy_pattern");
       patterns.put("bridge", "https://en.wikipedia.org/wiki/Bridge_pattern");
       patterns.put("facade", "https://en.wikipedia.org/wiki/Facade_pattern");
       patterns.put("builder", "https://en.wikipedia.org/wiki/Builder_pattern");
   }
   public Map<String, String> getPatterns() {
       return patterns;
   }
   public void setPatterns(Map<String, String> patterns) {
       this.patterns = patterns;
   }
}
در اینجا یک شی کوچک داریم که شامل یک نقشه با نام الگوهای طراحی به عنوان کلید و پیوندهای ویکی‌پدیا مربوطه به‌عنوان مقدار، و همچنین روش‌هایی برای دسترسی به نقشه است. بیایید نگاهی به موضوع اصلی بیندازیم:
class SomeMain {
   public static void main(String[] args) {
       ReusablePool pool = ReusablePool.getInstance();

       Resource firstResource = pool.acquireResource();
       Map<String, String> firstPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(firstResource);

       Resource secondResource = pool.acquireResource();
       Map<String, String> secondPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(secondResource);

       Resource thirdResource = pool.acquireResource();
       Map<String, String> thirdPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(thirdResource);
   }
}
همه چیز در اینجا به اندازه کافی واضح است: ما یک شی استخر دریافت می کنیم، یک شی با منابع از استخر دریافت می کنیم، نقشه را از شی Resource دریافت می کنیم، کاری با آن انجام می دهیم و همه اینها را در جای خود در استخر برای استفاده مجدد بیشتر قرار می دهیم. Voila، این الگوی طراحی استخر شی است. اما ما در مورد ضد الگوها صحبت می کردیم، درست است؟ بیایید مورد زیر را در روش اصلی در نظر بگیریم:
Resource fourthResource = pool.acquireResource();
   Map<String, String> fourthPatterns = firstResource.getPatterns();
// use our map somehow...
fourthPatterns.clear();
firstPatterns.put("first","blablabla");
firstPatterns.put("second","blablabla");
firstPatterns.put("third","blablabla");
firstPatterns.put("fourth","blablabla");
pool.releaseResource(fourthResource);
در اینجا، دوباره، یک شی Resource دریافت می کنیم، نقشه الگوهای آن را می گیریم، و کاری با نقشه انجام می دهیم. اما قبل از ذخیره نقشه در مجموعه ای از اشیاء، آن را پاک می کند و سپس با داده های خراب پر می شود، و شی Resource را برای استفاده مجدد نامناسب می کند. یکی از جزئیات اصلی یک مخزن اشیاء این است که وقتی یک شی برگردانده می شود، باید به حالت مناسب برای استفاده مجدد بیشتر بازگردد. اگر اشیایی که به استخر بازگردانده می شوند در حالت نادرست یا تعریف نشده باقی بمانند، طرح ما را آبجکت شی می نامند. آیا ذخیره اشیایی که برای استفاده مجدد مناسب نیستند منطقی است؟ در این شرایط می توانیم نقشه داخلی را در سازنده تغییرناپذیر کنیم:
public Resource() {
   patterns = new HashMap<>();
   patterns.put("proxy", "https://en.wikipedia.org/wiki/Proxy_pattern");
   patterns.put("bridge", "https://en.wikipedia.org/wiki/Bridge_pattern");
   patterns.put("facade", "https://en.wikipedia.org/wiki/Facade_pattern");
   patterns.put("builder", "https://en.wikipedia.org/wiki/Builder_pattern");
   patterns = Collections.unmodifiableMap(patterns);
}
تلاش ها و تمایل به تغییر محتویات نقشه به لطف UnsupportedOperationException که ایجاد می کنند از بین می رود. آنتی پترن ها تله هایی هستند که توسعه دهندگان به دلیل کمبود حاد زمان، بی دقتی، بی تجربگی یا فشار مدیران پروژه اغلب با آنها مواجه می شوند. عجله که رایج است، می تواند در آینده مشکلات بزرگی را برای برنامه به همراه داشته باشد، بنابراین باید از این خطاها اطلاع داشته باشید و از قبل از آنها اجتناب کنید. این پایان بخش اول مقاله است. ادامه دارد...
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION