JavaRush /Blog Java /Random-VI /Mẫu thiết kế Adaptor giải quyết được những vấn đề gì?

Mẫu thiết kế Adaptor giải quyết được những vấn đề gì?

Xuất bản trong nhóm
Việc phát triển phần mềm thường phức tạp do sự không tương thích giữa các thành phần hoạt động với nhau. Ví dụ: nếu bạn cần tích hợp một thư viện mới với nền tảng cũ được viết bằng các phiên bản Java trước đó, bạn có thể gặp phải sự không tương thích của các đối tượng, hay chính xác hơn là các giao diện. Phải làm gì trong trường hợp này? Viết lại mã? Nhưng điều này là không thể: việc phân tích hệ thống sẽ mất rất nhiều thời gian nếu không logic bên trong của công việc sẽ bị phá vỡ. Mẫu thiết kế Adaptor giải quyết được những vấn đề gì - 1Để giải quyết vấn đề này, họ đã nghĩ ra mẫu Adaptor, giúp các đối tượng có giao diện không tương thích hoạt động cùng nhau. Hãy xem cách sử dụng nó!

Thêm chi tiết về vấn đề

Đầu tiên, hãy mô phỏng hành vi của hệ thống cũ. Giả sử nó tạo ra lý do đi làm hoặc đi học muộn. Để làm điều này, chúng ta có một giao diện chứa Excusecác phương thức generateExcuse()likeExcuse().dislikeExcuse()
public interface Excuse {
   String generateExcuse();
   void likeExcuse(String excuse);
   void dislikeExcuse(String excuse);
}
Giao diện này được thực hiện bởi lớp WorkExcuse:
public class WorkExcuse implements Excuse {
   private String[] reasonOptions = {"по невероятному стечению обстоятельств у нас в доме закончилась горячая вода и я ждал, пока солнечный свет, сконцентрированный через лупу, нагреет кружку воды, чтобы я мог умыться.",
   "искусственный интеллект в моем будильнике подвел меня и разбудил на час раньше обычного. Поскольку сейчас зима, я думал что еще ночь и уснул. Дальше все How в тумане.",
   "предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице."};
   private String[] sorryOptions = {"Это, конечно, не повторится, мне очень жаль.", "Прошу меня извинить за непрофессиональное поведение.", "Нет оправдания моему поступку. Я недостоин этой должности."};

   @Override
   public String generateExcuse() { // Случайно выбираем отговорку из массива
       String result = "Я сегодня опоздал, потому что " + reasonOptions[(int) Math.round(Math.random() + 1)] + "\n" +
               sorryOptions[(int) Math.round(Math.random() + 1)];
       return result;
   }

   @Override
   public void likeExcuse(String excuse) {
       // Дублируем элемент в массиве, чтобы шанс его выпадения был выше
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Удаляем элемент из массива
   }
}
Hãy kiểm tra ví dụ:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
Phần kết luận:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице. 
Прошу меня извинить за непрофессиональное поведение.
Bây giờ hãy tưởng tượng rằng bạn đã triển khai dịch vụ, thu thập số liệu thống kê và nhận thấy rằng phần lớn người dùng dịch vụ là sinh viên đại học. Để cải thiện nó nhằm đáp ứng nhu cầu của nhóm này, bạn đã đặt hàng một hệ thống tạo lý do từ một nhà phát triển khác dành riêng cho nhóm này. Nhóm phát triển đã tiến hành nghiên cứu, tổng hợp xếp hạng, kết nối trí tuệ nhân tạo, bổ sung tích hợp với ùn tắc giao thông, thời tiết, v.v. Bây giờ bạn có một thư viện để tạo cớ cho học sinh, nhưng giao diện để tương tác với nó thì khác - StudentExcuse:
public interface StudentExcuse {
   String generateExcuse();
   void dislikeExcuse(String excuse);
}
Giao diện có hai phương thức: generateExcuse, tạo ra lý do và dislikeExcuse, chặn lý do đó để nó không xuất hiện trong tương lai. Thư viện của bên thứ ba đã bị đóng để chỉnh sửa - bạn không thể thay đổi mã nguồn của nó. Kết quả là, trong hệ thống của bạn có hai lớp triển khai giao diện Excusevà một thư viện có một lớp SuperStudentExcusetriển khai giao diện StudentExcuse:
public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Логика нового функционала
       return "Невероятная отговорка, адаптированная под текущее состояние погоды, пробки or сбои в расписании общественного транспорта.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Добавляет причину в черный список
   }
}
Mã không thể thay đổi. Sơ đồ hiện tại sẽ như sau: Mẫu thiết kế Adaptor - 2 giải quyết được những vấn đề gì?Phiên bản hệ thống này chỉ hoạt động với giao diện Excuse. Bạn không thể viết lại mã: trong một ứng dụng lớn, những thay đổi như vậy có thể mất nhiều thời gian hoặc phá vỡ logic ứng dụng. Bạn có thể gợi ý giới thiệu giao diện chính và tăng thứ bậc: Mẫu thiết kế Adaptor giải quyết được những vấn đề gì - 3Để làm được điều này, bạn cần đổi tên giao diện Excuse. Nhưng hệ thống phân cấp bổ sung là điều không mong muốn trong các ứng dụng nghiêm túc: việc đưa vào một phần tử gốc chung sẽ phá vỡ kiến ​​trúc. Một lớp trung gian nên được triển khai để cho phép sử dụng chức năng mới và cũ với tổn thất tối thiểu. Tóm lại, bạn cần một bộ chuyển đổi .

Cách hoạt động của mẫu Bộ điều hợp

Bộ điều hợp là một đối tượng trung gian thực hiện các lệnh gọi đến các phương thức trên một đối tượng có thể hiểu được đối tượng khác. Hãy triển khai một bộ chuyển đổi cho ví dụ của chúng ta và gọi nó là Middleware. Bộ điều hợp của chúng tôi phải triển khai giao diện tương thích với một trong các đối tượng. Để cho nó được Excuse. Nhờ đó, Middlewarenó có thể gọi các phương thức của đối tượng đầu tiên. Middlewarenhận cuộc gọi và chuyển chúng đến đối tượng thứ hai ở định dạng tương thích. MiddlewareĐây là cách triển khai một phương thức với các phương thức generateExcusevà trông như sau dislikeExcuse:
public class Middleware implements Excuse { // 1. Middleware становится совместимым с an objectом WorkExcuse через интерфейс Excuse

   private StudentExcuse superStudentExcuse;

   public Middleware(StudentExcuse excuse) { // 2. Получаем ссылку на адаптируемый an object
       this.superStudentExcuse = excuse;
   }

   @Override
   public String generateExcuse() {
       return superStudentExcuse.generateExcuse(); // 3. Адаптер реализовывает метод интерфейса
   }

    @Override
    public void dislikeExcuse(String excuse) {
        // Метод предварительно помещает отговорку в черный список БД,
        // Затем передает ее в метод dislikeExcuse an object superStudentExcuse.
    }
   // Метод likeExcuse появятся позже
}
Kiểm tra (trong mã máy khách):
public class Test {
   public static void main(String[] args) {
       Excuse excuse = new WorkExcuse(); // Создаются an objectы классов,
       StudentExcuse newExcuse = new SuperStudentExcuse(); // Которые должны быть совмещены.
       System.out.println("Обычная причина для работника:");
       System.out.println(excuse.generateExcuse());
       System.out.println("\n");
       Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Оборачиваем новый функционал в an object-адаптер
       System.out.println("Использование нового функционала с помощью адаптера:");
       System.out.println(adaptedStudentExcuse.generateExcuse()); // Адаптер вызывает адаптированный метод
   }
}
Phần kết luận:
Обычная причина для работника:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице.
Нет оправдания моему поступку. Я недостоин этой должности. Использование нового функционала с помощью адаптера
Một lý do đáng kinh ngạc, phù hợp với điều kiện thời tiết hiện tại, tình trạng ùn tắc giao thông hoặc sự gián đoạn trong lịch trình giao thông công cộng. Phương thức này generateExcusechỉ đơn giản chuyển cuộc gọi sang đối tượng khác mà không cần chuyển đổi bổ sung. Phương pháp này dislikeExcuseyêu cầu trước tiên là đưa lý do vào danh sách đen cơ sở dữ liệu. Việc xử lý dữ liệu trung gian bổ sung là lý do khiến mẫu Adaptor được yêu thích. Nhưng còn một phương thức likeExcusenằm trong giao diện Excusenhưng không có trong thì sao StudentExcuse? Thao tác này không được hỗ trợ trong chức năng mới. Đối với trường hợp như vậy, họ đã đưa ra một ngoại lệ UnsupportedOperationException: nó sẽ bị loại bỏ nếu thao tác được yêu cầu không được hỗ trợ. Hãy sử dụng cái này. Đây là cách triển khai lớp mới Middleware:
public class Middleware implements Excuse {

   private StudentExcuse superStudentExcuse;

   public Middleware(StudentExcuse excuse) {
       this.superStudentExcuse = excuse;
   }

   @Override
   public String generateExcuse() {
       return superStudentExcuse.generateExcuse();
   }

   @Override
   public void likeExcuse(String excuse) {
       throw new UnsupportedOperationException("Метод likeExcuse не поддерживается в новом функционале");
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Метод обращается за дополнительной информацией к БД,
       // Затем передает ее в метод dislikeExcuse an object superStudentExcuse.
   }
}
Thoạt nhìn, giải pháp này có vẻ không thành công nhưng việc mô phỏng chức năng có thể dẫn đến tình huống phức tạp hơn. Nếu khách hàng chú ý và bộ chuyển đổi được ghi chép đầy đủ thì giải pháp này có thể chấp nhận được.

Khi nào nên sử dụng Bộ chuyển đổi

  1. Nếu bạn cần sử dụng lớp của bên thứ ba nhưng giao diện của nó không tương thích với ứng dụng chính. Ví dụ trên cho thấy cách tạo một đối tượng shim để bao bọc các cuộc gọi theo định dạng mà đối tượng đích có thể hiểu được.

  2. Khi một số lớp con hiện có phải có chức năng chung. Thay vì các lớp con bổ sung (việc tạo ra chúng sẽ dẫn đến sao chép mã), tốt hơn là bạn nên sử dụng một bộ chuyển đổi.

Ưu điểm và nhược điểm

Ưu điểm: Bộ điều hợp ẩn khỏi máy khách các chi tiết xử lý yêu cầu từ đối tượng này sang đối tượng khác. Mã máy khách không nghĩ đến việc định dạng dữ liệu hoặc xử lý các cuộc gọi đến phương thức đích. Nó quá phức tạp và các lập trình viên thì lười biếng :) Nhược điểm: Cơ sở mã của dự án phức tạp bởi các lớp bổ sung và nếu có một số lượng lớn các điểm không tương thích, số lượng của chúng có thể tăng lên đến kích thước không thể kiểm soát được.

Đừng nhầm lẫn với Mặt tiền và Trang trí

Khi xem xét bề ngoài, Adaptor có thể bị nhầm lẫn với các mẫu Façade và Decorator. Sự khác biệt giữa Adaptor và Facade là Facade giới thiệu một giao diện mới và bao bọc toàn bộ hệ thống con. Chà, Decorator, không giống như Adaptor, thay đổi chính đối tượng chứ không phải giao diện.

Thuật toán thực hiện từng bước

  1. Trước tiên, hãy đảm bảo rằng có một vấn đề mà mô hình này có thể giải quyết được.

  2. Xác định giao diện máy khách thay mặt cho lớp khác sẽ được sử dụng.

  3. Triển khai lớp bộ điều hợp dựa trên giao diện được xác định ở bước trước.

  4. Trong lớp bộ điều hợp, tạo một trường lưu trữ tham chiếu đến đối tượng. Tham chiếu này được truyền vào hàm tạo.

  5. Triển khai tất cả các phương thức giao diện máy khách trong bộ chuyển đổi. Phương pháp này có thể:

    • Chuyển cuộc gọi mà không sửa đổi;

    • Thay đổi dữ liệu, tăng/giảm số lượng lệnh gọi đến phương thức đích, mở rộng hơn nữa thành phần dữ liệu, v.v.

    • Phương án cuối cùng, nếu một phương thức cụ thể không tương thích, hãy ném ra một ngoại lệ UnsupportedOperationException, điều này thực sự cần phải được ghi lại.

  6. Nếu ứng dụng chỉ sử dụng bộ điều hợp thông qua giao diện máy khách (như trong ví dụ trên), điều này sẽ cho phép mở rộng bộ điều hợp một cách dễ dàng trong tương lai.

Tất nhiên, mẫu thiết kế không phải là thuốc chữa bách bệnh cho mọi bệnh tật, nhưng với sự trợ giúp của nó, bạn có thể giải quyết một cách khéo léo vấn đề không tương thích của các đối tượng với các giao diện khác nhau. Một nhà phát triển biết các mẫu cơ bản sẽ cao hơn vài bậc so với những người chỉ biết cách viết thuật toán vì chúng cần thiết để tạo ra các ứng dụng nghiêm túc. Việc sử dụng lại mã trở nên ít khó khăn hơn và việc duy trì mã là một niềm vui. Đó là tất cả cho ngày hôm nay! Nhưng chúng ta sẽ sớm tiếp tục làm quen với các mẫu thiết kế khác nhau :)
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION