CodeGym /Blog Java /Ngẫu nhiên /Chống mẫu là gì? Cùng xem một số ví dụ (Phần 1)
John Squirrels
Mức độ
San Francisco

Chống mẫu là gì? Cùng xem một số ví dụ (Phần 1)

Xuất bản trong nhóm
Chúc một ngày tốt lành! Một ngày nọ, tôi có một cuộc phỏng vấn xin việc và tôi đã được hỏi một số câu hỏi về phản mẫu: chúng là gì, có những loại nào và có những ví dụ thực tế nào. Tất nhiên, tôi đã trả lời câu hỏi, nhưng rất hời hợt, vì trước đó tôi chưa đi sâu vào chủ đề này. Sau cuộc phỏng vấn, tôi bắt đầu lùng sục Internet và ngày càng đắm mình hơn vào chủ đề này. Chống mẫu là gì?  Cùng xem một số ví dụ (Phần 1) - 1 Hôm nay tôi muốn cung cấp một cái nhìn tổng quan ngắn gọn về các anti-pattern phổ biến nhất và xem xét một số ví dụ. Tôi hy vọng rằng việc đọc này sẽ cung cấp cho bạn những kiến ​​thức bạn cần trong lĩnh vực này. Bắt đầu nào! Trước khi thảo luận về anti-pattern là gì, hãy nhớ lại design pattern là gì. Một mẫu thiết kếlà một giải pháp kiến ​​trúc có thể lặp lại cho các vấn đề hoặc tình huống phổ biến phát sinh khi thiết kế một ứng dụng. Nhưng hôm nay chúng ta không nói về chúng, mà là những mặt đối lập của chúng - phản khuôn mẫu. Anti -pattern là một cách tiếp cận phổ biến nhưng không hiệu quả, rủi ro và/hoặc không hiệu quả để giải quyết một loại vấn đề phổ biến. Nói cách khác, đây là một dạng sai lầm (đôi khi còn được gọi là một cái bẫy). Theo quy định, các mẫu chống được chia thành các loại sau:
  1. Các phản mẫu kiến ​​trúc — Những phản mẫu này phát sinh khi cấu trúc của một hệ thống được thiết kế (thường là bởi một kiến ​​trúc sư).
  2. Anti-patterns trong quản lý/tổ chức — Đây là những anti-patterns trong quản lý dự án, thường gặp phải bởi nhiều người quản lý (hoặc nhóm người quản lý).
  3. Các phản mẫu phát triển — Những phản mẫu này phát sinh khi một hệ thống được triển khai bởi các lập trình viên bình thường.
Phạm vi đầy đủ của các mẫu chống đối kỳ lạ hơn nhiều, nhưng chúng ta sẽ không xem xét tất cả chúng ngày hôm nay. Đối với các nhà phát triển bình thường, điều đó sẽ là quá nhiều. Đối với người mới bắt đầu, hãy xem xét một mô hình chống quản lý làm ví dụ.

1. Tê liệt phân tích

Tê liệt phân tíchđược coi là một mô hình chống quản lý cổ điển. Nó liên quan đến việc phân tích quá mức tình hình trong quá trình lập kế hoạch, do đó không có quyết định hoặc hành động nào được đưa ra, về cơ bản là làm tê liệt quá trình phát triển. Điều này thường xảy ra khi mục tiêu là đạt được sự hoàn hảo và xem xét tuyệt đối mọi thứ trong giai đoạn phân tích. Phản mẫu này có đặc điểm là đi theo vòng tròn (một vòng lặp khép kín chạy bình thường), sửa đổi và tạo các mô hình chi tiết, từ đó cản trở quy trình làm việc. Ví dụ: bạn đang cố gắng dự đoán mọi thứ ở mức độ: nhưng điều gì sẽ xảy ra nếu người dùng đột nhiên muốn tạo danh sách nhân viên dựa trên chữ cái thứ tư và thứ năm trong tên của họ, bao gồm danh sách các dự án mà họ dành nhiều giờ làm việc nhất giữa Tết Dương lịch và Quốc tế Phụ nữ trong bốn năm qua? Về bản chất, nó' Có quá nhiều phân tích. Dưới đây là một số mẹo để chống tê liệt phân tích:
  1. Bạn cần xác định mục tiêu dài hạn như một ngọn hải đăng cho việc ra quyết định, để mỗi quyết định của bạn đưa bạn đến gần mục tiêu hơn là khiến bạn trì trệ.
  2. Đừng tập trung vào những chuyện vặt vãnh (tại sao lại đưa ra quyết định về một chi tiết không đáng kể như thể đó là quyết định quan trọng nhất của cuộc đời bạn?)
  3. Đặt thời hạn cho một quyết định.
  4. Đừng cố gắng hoàn thành một nhiệm vụ một cách hoàn hảo - tốt hơn hết là hãy làm thật tốt.
Không cần phải đi quá sâu ở đây, vì vậy chúng tôi sẽ không xem xét các mô hình chống quản lý khác. Do đó, không có phần giới thiệu nào, chúng ta sẽ chuyển sang một số kiến ​​trúc phản mẫu, bởi vì bài viết này rất có thể được đọc bởi các nhà phát triển tương lai hơn là các nhà quản lý.

2. Đối tượng của Chúa

Đối tượng God là một đối tượng mô tả sự tập trung quá mức của tất cả các loại chức năng và lượng lớn dữ liệu khác nhau (một đối tượng mà ứng dụng xoay quanh). Lấy một ví dụ nhỏ:

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();
   }
}
Ở đây chúng ta thấy một lớp lớn làm mọi thứ. Nó chứa các truy vấn cơ sở dữ liệu cũng như một số dữ liệu. Chúng ta cũng thấy phương thức mặt tiền findAllWithoutPageEn, bao gồm logic nghiệp vụ. Một đối tượng như vậy của Chúa trở nên to lớn và khó bảo trì đúng cách. Chúng ta phải loay hoay với nó trong từng đoạn mã. Nhiều thành phần hệ thống dựa vào nó và được liên kết chặt chẽ với nó. Việc duy trì mã như vậy ngày càng trở nên khó khăn hơn. Trong những trường hợp như vậy, mã nên được chia thành các lớp riêng biệt, mỗi lớp sẽ chỉ có một mục đích. Trong ví dụ này, chúng ta có thể chia đối tượng God thành một lớp 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());
   }
  
                               ........
}
Một lớp chứa dữ liệu và các phương thức để truy cập dữ liệu:

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;
   }
                    ....
Và sẽ phù hợp hơn nếu chuyển phương thức có logic nghiệp vụ sang một dịch vụ:

private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
   switch (type) {
       case USERS:
           return findAllEnUsers(permissionId);
       case CUSTOMERS:
           return findAllEnCustomers(permissionId);
       default:
           return findAllEn();
   }
}

3. Độc thân

Một singleton là mẫu đơn giản nhất. Nó đảm bảo rằng trong một ứng dụng đơn luồng sẽ có một thể hiện duy nhất của một lớp và nó cung cấp một điểm truy cập toàn cục cho đối tượng này. Nhưng nó là một khuôn mẫu hay phản khuôn mẫu? Hãy xem xét những nhược điểm của mô hình này:
  1. Trạng thái toàn cục Khi chúng ta truy cập thể hiện của lớp, chúng ta không biết trạng thái hiện tại của lớp này. Chúng tôi không biết ai đã thay đổi nó hoặc khi nào. Trạng thái có thể không giống như những gì chúng ta mong đợi. Nói cách khác, tính chính xác của việc làm việc với một singleton phụ thuộc vào thứ tự truy cập vào nó. Điều này có nghĩa là các hệ thống con phụ thuộc vào nhau và kết quả là thiết kế trở nên phức tạp hơn.

  2. Một singleton vi phạm các nguyên tắc SOLID - nguyên tắc trách nhiệm duy nhất: ngoài các nhiệm vụ trực tiếp của nó, lớp singleton còn kiểm soát số lượng các phiên bản.

  3. Sự phụ thuộc của một lớp bình thường vào một singleton không hiển thị trong giao diện của lớp. Bởi vì một thể hiện singleton thường không được truyền dưới dạng đối số phương thức, mà thay vào đó được lấy trực tiếp thông qua getInstance(), bạn cần tìm hiểu cách triển khai của từng phương thức để xác định sự phụ thuộc của lớp vào singleton — chỉ cần nhìn vào công khai của một lớp hợp đồng là không đủ.

    Sự hiện diện của một singleton làm giảm khả năng kiểm tra của ứng dụng nói chung và các lớp sử dụng singleton nói riêng. Trước hết, bạn không thể thay thế singleton bằng một đối tượng giả. Thứ hai, nếu một singleton có giao diện để thay đổi trạng thái của nó, thì các bài kiểm tra sẽ phụ thuộc vào nhau.

    Nói cách khác, một singleton làm tăng khả năng ghép nối và mọi thứ được đề cập ở trên không gì khác hơn là hệ quả của việc tăng khả năng ghép nối.

    Và nếu bạn nghĩ về nó, bạn có thể tránh sử dụng một singleton. Ví dụ, hoàn toàn có thể (và thực sự cần thiết) sử dụng nhiều loại nhà máy khác nhau để kiểm soát số lượng phiên bản của một đối tượng.

    Mối nguy hiểm lớn nhất nằm ở nỗ lực xây dựng toàn bộ kiến ​​trúc ứng dụng dựa trên các ứng dụng đơn lẻ. Có rất nhiều lựa chọn thay thế tuyệt vời cho phương pháp này. Ví dụ quan trọng nhất là Spring, cụ thể là các thùng chứa IoC của nó: chúng là một giải pháp tự nhiên cho vấn đề kiểm soát việc tạo dịch vụ, vì chúng thực sự là "nhà máy sản xuất steroid".

    Nhiều cuộc tranh luận không hồi kết và không thể hòa giải hiện đang nổ ra về chủ đề này. Việc quyết định xem một singleton là một mẫu hay phản mẫu là tùy thuộc vào bạn.

    Chúng tôi sẽ không nán lại trên đó. Thay vào đó, chúng ta sẽ chuyển sang mẫu thiết kế cuối cùng cho ngày hôm nay - yêu tinh.

4. Yêu tinh

Poltergeist là một anti-pattern liên quan đến một lớp vô nghĩa được sử dụng để gọi các phương thức của một lớp khác hoặc chỉ đơn giản là thêm một lớp trừu tượng không cần thiết . Phản mẫu này biểu hiện dưới dạng các đối tượng tồn tại trong thời gian ngắn, không có trạng thái. Các đối tượng này thường được sử dụng để khởi tạo các đối tượng khác, lâu dài hơn.

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);
   }
}
Tại sao chúng ta cần một đối tượng chỉ là trung gian và ủy thác công việc của nó cho người khác? Chúng tôi loại bỏ nó và chuyển chức năng ít ỏi mà nó có sang các đối tượng tồn tại lâu dài. Tiếp theo, chúng tôi chuyển sang các mẫu mà chúng tôi quan tâm nhất (với tư cách là nhà phát triển bình thường), tức là phát triển chống mẫu .

5. Mã hóa cứng

Vì vậy, chúng tôi đã đi đến từ khủng khiếp này: mã hóa cứng. Bản chất của kiểu chống mẫu này là mã được liên kết chặt chẽ với một cấu hình phần cứng và/hoặc môi trường hệ thống cụ thể. Điều này rất phức tạp khi chuyển mã sang các cấu hình khác. Phản mẫu này được liên kết chặt chẽ với các số ma thuật (các phản mẫu này thường đan xen với nhau). Ví dụ:

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;
}
Đau, phải không? Ở đây chúng tôi mã hóa cài đặt kết nối của chúng tôi. Do đó, mã sẽ chỉ hoạt động chính xác với MySQL. Để thay đổi cơ sở dữ liệu, chúng ta sẽ cần đi sâu vào mã và thay đổi mọi thứ theo cách thủ công. Một giải pháp tốt là đặt cấu hình trong một tệp riêng:

spring:
  datasource:
    jdbc-url:jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username:  user01
    password:  12345qwert
Một tùy chọn khác là sử dụng hằng số.

6. Neo thuyền

Trong bối cảnh chống mẫu, neo thuyền có nghĩa là giữ các phần của hệ thống không còn được sử dụng sau khi thực hiện một số tối ưu hóa hoặc tái cấu trúc. Ngoài ra, một số phần của mã có thể được giữ lại "để sử dụng trong tương lai" trong trường hợp bạn đột nhiên cần đến chúng. Về cơ bản, điều này biến mã của bạn thành một thùng rác. Ví dụ:

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());
}
Chúng tôi có một phương thức cập nhật sử dụng một phương thức riêng để hợp nhất dữ liệu người dùng từ cơ sở dữ liệu với dữ liệu người dùng được truyền cho phương thức (nếu người dùng được truyền cho phương thức cập nhật có trường null, thì giá trị trường cũ được lấy từ cơ sở dữ liệu) . Sau đó, giả sử có một yêu cầu mới rằng các bản ghi không được hợp nhất với các bản ghi cũ, mà thay vào đó, ngay cả khi có các trường null, chúng vẫn được sử dụng để ghi đè lên các bản ghi cũ:

public User update(Long id, User request) {
   return userDAO.update(user);
}
Điều này có nghĩa là mergeUser không còn được sử dụng nữa, nhưng sẽ rất đáng tiếc nếu xóa nó — điều gì sẽ xảy ra nếu phương pháp này (hoặc ý tưởng của phương pháp này) có thể hữu ích vào một ngày nào đó? Mã như vậy chỉ làm phức tạp hệ thống và gây nhầm lẫn, về cơ bản không có giá trị thực tế. Chúng ta không được quên rằng mã với "mảnh chết" như vậy sẽ khó chuyển cho đồng nghiệp khi bạn rời đi cho một dự án khác. Cách tốt nhất để đối phó với neo thuyền là cấu trúc lại mã, tức là xóa các đoạn mã (đau lòng lắm, tôi biết). Ngoài ra, khi chuẩn bị lịch trình phát triển, cần phải tính đến các mỏ neo đó (để phân bổ thời gian thu dọn).

7. Đối tượng cesspool

Để mô tả mẫu chống đối này, trước tiên bạn cần làm quen với mẫu nhóm đối tượng . Nhóm đối tượng (nhóm tài nguyên) là một mẫu thiết kế sáng tạo , một tập hợp các đối tượng đã khởi tạo và sẵn sàng sử dụng. Khi một ứng dụng cần một đối tượng, nó sẽ được lấy từ nhóm này thay vì được tạo lại. Khi một đối tượng không còn cần thiết, nó không bị hủy. Thay vào đó, nó được trả lại cho hồ bơi. Mẫu này thường được sử dụng cho các đối tượng nặng tốn nhiều thời gian để tạo mỗi khi cần, chẳng hạn như khi kết nối với cơ sở dữ liệu. Hãy xem xét một ví dụ nhỏ và đơn giản. Đây là một lớp đại diện cho mẫu này:

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);
   }
}
Lớp này được trình bày dưới dạng singleton pattern/anti-pattern ở trên, tức là chỉ có thể có một đối tượng thuộc loại này. Nó sử dụng Resourcecác đối tượng nhất định. Theo mặc định, hàm tạo sẽ lấp đầy nhóm với 4 phiên bản. Khi bạn nhận được một đối tượng, nó sẽ bị xóa khỏi nhóm (nếu không có đối tượng khả dụng, một đối tượng sẽ được tạo và trả về ngay lập tức). Và cuối cùng, chúng ta có một phương thức để đặt đối tượng trở lại. Các đối tượng tài nguyên trông như thế này:

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;
   }
}
Ở đây chúng ta có một đối tượng nhỏ chứa bản đồ với các tên mẫu thiết kế làm khóa và các liên kết Wikipedia tương ứng làm giá trị, cũng như các phương thức để truy cập bản đồ. Chúng ta hãy nhìn vào chính:

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);
   }
}
Mọi thứ ở đây đủ rõ ràng: chúng ta lấy một đối tượng nhóm, lấy một đối tượng có tài nguyên từ nhóm, lấy bản đồ từ đối tượng Tài nguyên, làm gì đó với nó và đặt tất cả những thứ này vào vị trí của nó trong nhóm để tái sử dụng thêm. Thì đấy, đây là mẫu thiết kế nhóm đối tượng. Nhưng chúng ta đang nói về anti-pattern, phải không? Hãy xem xét trường hợp sau trong phương thức chính:

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);
Ở đây, một lần nữa, chúng ta lấy một đối tượng Tài nguyên, chúng ta lấy bản đồ các mẫu của nó và chúng ta làm gì đó với bản đồ. Nhưng trước khi lưu bản đồ trở lại nhóm đối tượng, nó sẽ bị xóa và sau đó điền dữ liệu bị hỏng, khiến đối tượng Tài nguyên không phù hợp để sử dụng lại. Một trong những chi tiết chính của nhóm đối tượng là khi một đối tượng được trả về, nó phải khôi phục về trạng thái phù hợp để sử dụng lại sau này. Nếu các đối tượng được trả về nhóm vẫn ở trạng thái không chính xác hoặc không xác định, thì thiết kế của chúng tôi được gọi là một cesspool đối tượng. Có ý nghĩa gì khi lưu trữ các đối tượng không phù hợp để sử dụng lại không? Trong tình huống này, chúng ta có thể làm cho bản đồ bên trong trở nên bất biến trong hàm tạo:

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);
}
Những nỗ lực và mong muốn thay đổi nội dung của bản đồ sẽ biến mất nhờ Ngoại lệ UnsupportedOperationException mà chúng sẽ tạo ra. Anti-patterns là những cái bẫy mà các nhà phát triển thường xuyên gặp phải do thiếu thời gian, bất cẩn, thiếu kinh nghiệm hoặc áp lực từ các nhà quản lý dự án. Việc vội vàng, vốn phổ biến, có thể dẫn đến các vấn đề lớn cho ứng dụng trong tương lai, vì vậy bạn cần biết về những lỗi này và tránh chúng trước. Điều này kết thúc phần đầu tiên của bài viết. Còn tiếp...
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION