JavaRush /وبلاگ جاوا /Random-FA /آنتی الگوها چیست؟ بیایید به مثال ها نگاه کنیم (قسمت 1)

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

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

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با منطق تجاری را می بینیم. چنین شیء الهی برای حمایت کافی عظیم و ناشیانه می شود. ما باید در هر کدی با آن سر و کله بزنیم: بسیاری از گره‌ها در سیستم به آن متکی هستند و به شدت با آن مرتبط هستند. حفظ چنین کدی روز به روز دشوارتر می شود. در چنین مواردی نیاز است که به طبقات جداگانه ای تقسیم شود که هر کدام تنها یک هدف (هدف) خواهند داشت. در این مثال، می توانید آن را به یک کلاس 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. مجرد

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

  2. Singleton یکی از اصول SOLID - Single Responsibility Principle - را نقض می کند - کلاس Singleton علاوه بر انجام مسئولیت های فوری خود، تعداد نمونه های خود را نیز کنترل می کند.

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

    وجود سینگلتون باعث کاهش تست پذیری اپلیکیشن به طور کلی و کلاس هایی می شود که از سینگلتون استفاده می کنند. اولاً نمی‌توانید یک شیء Mock را به جای تک‌تون قرار دهید و ثانیاً اگر یک تک‌تون رابطی برای تغییر حالت داشته باشد، آزمایش‌ها به یکدیگر بستگی دارند.

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

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

    بزرگترین خطر در تلاش برای ساخت کل معماری برنامه بر اساس تک‌تنها نهفته است. جایگزین های بسیار خوبی برای این رویکرد وجود دارد. مهمترین مثال 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);
   }
}
چرا ما به یک شی نیاز داریم که فقط یک واسطه باشد و کار خود را به دیگری واگذار کند؟ ما آن را حذف می کنیم و عملکرد کوچکی را که پیاده سازی می کند به اشیاء با عمر طولانی منتقل می کنیم. در مرحله بعد، به سراغ الگوهایی می رویم که بیشترین علاقه را برای ما (به عنوان توسعه دهندگان معمولی) دارند - antipatterns توسعه .

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. لنگر قایق

لنگر قایق در زمینه آنتی الگوها به معنای ذخیره بخش های استفاده نشده از یک سیستم است که پس از بهینه سازی یا بازسازی مجدد باقی مانده است. همچنین، برخی از بخش‌های کد را می‌توان «برای آینده» گذاشت، در صورتی که مجبور به استفاده مجدد از آن‌ها شوید. این اساسا کد را به سطل زباله تبدیل می کند. آنتی الگوها چیست؟  بیایید به مثال ها نگاه کنیم (قسمت 1) - 4مثال:
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 کپی پر می‌شود. هنگامی که چنین شیئی گرفته می شود، از حوضچه خارج می شود (اگر وجود نداشته باشد، ایجاد می شود و بلافاصله داده می شود) و در پایان روشی برای برگرداندن شی وجود دارد. اشیاء Resourceبه شکل زیر هستند:
public class Resource {
   private Map<String, String> patterns;
   public Resource() {
       patterns = new HashMap<>();
       patterns.put("заместитель", "https://studfile.net/preview/3676297/page:3/");
       patterns.put("мост", "https://studfile.net/preview/3676297/page:4/");
       patterns.put("фасад", "https://studfile.net/preview/3676297/page:5/");
       patterns.put("строитель", "https://studfile.net/preview/3676297/page:6/#16");
   }
   public Map<String, String> getPatterns() {
       return patterns;
   }
   public void setPatterns(Map<String, String> patterns) {
       this.patterns = patterns;
   }
}
در اینجا یک شی کوچک داریم که شامل یک نقشه با نام الگوها به عنوان کلید و پیوندهایی به آنها به عنوان مقدار و همچنین روش هایی برای دسترسی به نقشه است. بیایید نگاه بیندازیم main:
class SomeMain {
   public static void main(String[] args) {
       ReusablePool pool = ReusablePool.getInstance();

       Resource firstResource = pool.acquireResource();
       Map<String, String> firstPatterns = firstResource.getPatterns();
       // ......Howим-то образом используем нашу мапу.....
       pool.releaseResource(firstResource);

       Resource secondResource = pool.acquireResource();
       Map<String, String> secondPatterns = firstResource.getPatterns();
       // ......Howим-то образом используем нашу мапу.....
       pool.releaseResource(secondResource);

       Resource thirdResource = pool.acquireResource();
       Map<String, String> thirdPatterns = firstResource.getPatterns();
       // ......Howим-то образом используем нашу мапу.....
       pool.releaseResource(thirdResource);
   }
}
همه چیز در اینجا نیز واضح است: ما یک شی استخر را می گیریم، یک شی را با منابع از آن بیرون می آوریم، یک نقشه از آن می گیریم، کاری با آن انجام می دهیم و همه آن را برای استفاده مجدد دوباره در استخر قرار می دهیم. Voila: در اینجا شما الگوی استخر آبجکت را دارید. اما ما در مورد ضد الگوها صحبت می کردیم، اینطور نیست؟ بیایید به این مورد نگاه کنیم main:
Resource fourthResource = pool.acquireResource();
   Map<String, String> fourthPatterns = firstResource.getPatterns();
// ......Howим-то образом используем нашу мапу.....
fourthPatterns.clear();
firstPatterns.put("first","blablabla");
firstPatterns.put("second","blablabla");
firstPatterns.put("third","blablabla");
firstPatterns.put("fourth","blablabla");
pool.releaseResource(fourthResource);
در اینجا مجدداً یک شی منبع گرفته می شود، نقشه آن با الگوها گرفته می شود و کاری با آن انجام می شود، اما قبل از ذخیره مجدد در استخر آبجکت، نقشه پاک می شود و با داده های نامفهوم پر می شود که این شی Resource را برای استفاده مجدد نامناسب می کند. یکی از تفاوت‌های اصلی یک شیء جمع‌آوری این است که پس از بازگرداندن یک شی، باید به حالت مناسب برای استفاده مجدد برگردانده شود. اگر اشیا پس از بازگرداندن به استخر در وضعیت نادرست یا نامشخصی باشند، به این ساختار، آبجکت شی می گویند. ذخیره اشیایی که قابل استفاده مجدد نیستند چه فایده ای دارد؟ در این شرایط می توانید نقشه داخلی را در سازنده تغییرناپذیر کنید:
public Resource() {
   patterns = new HashMap<>();
   patterns.put("заместитель", "https://studfile.net/preview/3676297/page:3/");
   patterns.put("мост", "https://studfile.net/preview/3676297/page:4/");
   patterns.put("фасад", "https://studfile.net/preview/3676297/page:5/");
   patterns.put("строитель", "https://studfile.net/preview/3676297/page:6/#16");
   patterns = Collections.unmodifiableMap(patterns);
}
(تلاش و تمایل برای تغییر محتوا همراه با UnsupportedOperationException خواهد بود). ضد الگوها تله‌هایی هستند که توسعه‌دهندگان اغلب به دلیل کمبود شدید زمان، بی‌توجهی، بی‌تجربه بودن یا ضربه‌های مدیران به آن‌ها می‌افتند. کمبود وقت و عجله معمول می تواند مشکلات بزرگی را برای برنامه در آینده ایجاد کند، بنابراین این اشتباهات باید از قبل شناخته شده و اجتناب شود. آنتی الگوها چیست؟  بیایید به مثال ها نگاه کنیم (قسمت 1) - 6بدین ترتیب قسمت اول مقاله به پایان رسید: ادامه دارد .
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION