CodeGym /Blog Java /Ngẫu nhiên /Mẫu thiết kế bộ điều hợp giải quyết vấn đề gì?

Mẫu thiết kế bộ điều hợp giải quyết vấn đề gì?

Xuất bản trong nhóm
Việc phát triển phần mềm trở nên khó khăn hơn bởi các thành phần không tương thích cần phải hoạt động cùng nhau. Ví dụ: nếu bạn cần tích hợp một thư viện mới với một 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 các đối tượng không tương thích hoặc đúng hơn là các giao diện không tương thích. Mẫu thiết kế bộ điều hợp giải quyết vấn đề gì?  - 1Phải làm gì trong trường hợp này? Viết lại mã? Chúng tôi không thể làm điều đó, bởi vì việc phân tích hệ thống sẽ mất rất nhiều thời gian hoặc logic bên trong của ứng dụng sẽ bị vi phạm. Để giải quyết vấn đề này, mẫu bộ điều hợp đã được tạo. Nó giúp các đối tượng có giao diện không tương thích làm việc cùng nhau. Hãy xem làm thế nào để sử dụng nó!

Thêm về vấn đề

Đầu tiên, chúng tôi sẽ mô phỏng hành vi của hệ thống cũ. Giả sử nó viện cớ cho việc đi làm hoặc đi học muộn. Để làm điều này, nó có một Excusegiao diện có generateExcuse()likeExcuse()các dislikeExcuse()phương thức.

public interface Excuse {
   String generateExcuse();
   void likeExcuse(String excuse);
   void dislikeExcuse(String excuse);
}
Lớp WorkExcusethực hiện giao diện này:

public class WorkExcuse implements Excuse {
   private String[] excuses = {"in an incredible confluence of circumstances, I ran out of hot water and had to wait until sunlight, focused using a magnifying glass, heated a mug of water so that I could wash.",
   "the artificial intelligence in my alarm clock failed me, waking me up an hour earlier than normal. Because it is winter, I thought it was still nighttime and I fell back asleep. Everything after that is a bit hazy.",
   "my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia."};
   private String [] apologies = {"This will not happen again, of course. I'm very sorry.", "I apologize for my unprofessional behavior.", "There is no excuse for my actions. I am not worthy of this position."};

   @Override
   public String generateExcuse() { // Randomly select an excuse from the array
       String result = "I was late today because " + excuses[(int) Math.round(Math.random() + 1)] + "\\n" +
               apologies[(int) Math.round(Math.random() + 1)];
       return result;
   }

   @Override
   public void likeExcuse(String excuse) {
       // Duplicate the element in the array so that its chances of being chosen are higher
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Remove the item from the array
   }
}
Hãy kiểm tra ví dụ của chúng tôi:

Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
Đầu ra:

"I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
I apologize for my unprofessional behavior.
Bây giờ, hãy tưởng tượng rằng bạn đã khởi chạy một dịch vụ tạo lý do bào chữa, thu thập số liệu thống kê và nhận thấy rằng hầu hết người dùng của bạn là sinh viên đại học. Để phục vụ nhóm này tốt hơn, bạn đã yêu cầu một nhà phát triển khác tạo một hệ thống tạo ra các lý do cụ thể cho sinh viên đại học. Nhóm phát triển đã tiến hành nghiên cứu thị trường, xếp hạng các lý do bào chữa, kết nối một số trí tuệ nhân tạo và tích hợp dịch vụ với báo cáo giao thông, báo cáo thời tiết, v.v. Bây giờ bạn có một thư viện để tạo lý do cho sinh viên đại học, nhưng nó có giao diện khác: StudentExcuse.

public interface StudentExcuse {
   String generateExcuse();
   void dislikeExcuse(String excuse);
}
Giao diện này có hai phương thức: generateExcuse, tạo ra lý do và dislikeExcuse, ngăn lý do xuất hiện lại trong tương lai. Thư viện của bên thứ ba không thể chỉnh sửa, tức là bạn không thể thay đổi mã nguồn của nó. Những gì chúng ta có bây giờ là một hệ thống có hai lớp triển khai Excusegiao diện và một thư viện có một SuperStudentExcuselớp triển khai StudentExcusegiao diện:

public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Logic for the new functionality
       return "An incredible excuse adapted to the current weather conditions, traffic jams, or delays in public transport schedules.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Adds the reason to a blacklist
   }
}
Không thể thay đổi mã. Hệ thống phân cấp lớp hiện tại trông như thế này: Mẫu thiết kế bộ điều hợp giải quyết vấn đề gì?  - 2Phiê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, việc thực hiện những thay đổi như vậy có thể trở thành một quá trình dài hoặc phá vỡ logic của ứng dụng. Chúng tôi có thể giới thiệu một giao diện cơ sở và mở rộng hệ thống phân cấp: Mẫu thiết kế bộ điều hợp giải quyết vấn đề gì?  - 3Để làm điều này, chúng tôi phải đổi tên Excusegiao diện. 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. Bạn nên triển khai một lớp trung gian cho phép chúng tôi sử dụng cả 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 .

Nguyên tắc đằng sau mẫu bộ điều hợp

Bộ điều hợp là một đối tượng trung gian cho phép các lệnh gọi phương thức của một đối tượng này được hiểu bởi một đối tượng khác. Hãy triển khai một bộ điều hợp 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. Hãy để nó được Excuse. Điều này cho phép Middlewaregọi các phương thức của đối tượng đầu tiên. Middlewarenhận cuộc gọi và chuyển tiếp chúng theo cách tương thích tới đối tượng thứ hai. Đây là cách Middlewaretriển khai với phương thức generateExcusedislikeExcuse:

public class Middleware implements Excuse { // 1. Middleware becomes compatible with WorkExcuse objects via the Excuse interface

   private StudentExcuse superStudentExcuse;

   public Middleware(StudentExcuse excuse) { // 2. Get a reference to the object being adapted
       this.superStudentExcuse = excuse;
   }

   @Override
   public String generateExcuse() {
       return superStudentExcuse.generateExcuse(); // 3. The adapter implements an interface method
   }

    @Override
    public void dislikeExcuse(String excuse) {
        // The method first adds the excuse to the blacklist,
        // Then passes it to the dislikeExcuse method of the superStudentExcuse object.
    }
   // The likeExcuse method will appear later
}
Kiểm tra (trong mã máy khách):

public class Test {
   public static void main(String[] args) {
       Excuse excuse = new WorkExcuse(); // We create objects of the classes
       StudentExcuse newExcuse = new SuperStudentExcuse(); // that must be compatible.
       System.out.println("An ordinary excuse for an employee:");
       System.out.println(excuse.generateExcuse());
       System.out.println("\n");
       Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Wrap the new functionality in the adapter object
       System.out.println("Using new functionality with the adapter:");
       System.out.println(adaptedStudentExcuse.generateExcuse()); // The adapter calls the adapted method
   }
}
Đầu ra:

An ordinary excuse for an employee:
I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
There is no excuse for my actions. I am not worthy of this position. Using new functionality with the adapter:
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ắc đường hoặc chậm trễ trong lịch trình giao thông công cộng. Phương thức này generateExcusechỉ đơn giản chuyển lời gọi đến một đối tượng khác mà không có bất kỳ thay đổi bổ sung nào. Phương pháp này dislikeExcuseyêu cầu chúng tôi đưa vào danh sách đen lý do đầu tiên. Khả năng thực hiện xử lý dữ liệu trung gian là lý do khiến mọi người yêu thích mẫu bộ điều hợp. Nhưng còn likeExcusephương thức, là một phần của Excusegiao diện nhưng không phải là một phần của StudentExcusegiao diện thì sao? Chức năng mới không hỗ trợ thao tác này. Các UnsupportedOperationExceptionđã được phát minh cho tình huống này. Nó bị ném nếu thao tác được yêu cầu không được hỗ trợ. Hãy sử dụng nó. Đây là cách Middlewaretriển khai mới của lớp:

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("The likeExcuse method is not supported by the new functionality");
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // The method accesses a database to fetch additional information,
       // and then passes it to the superStudentExcuse object's dislikeExcuse method.
   }
}
Thoạt nhìn, giải pháp này có vẻ không ổn lắm, nhưng việc bắt chước chức năng có thể làm phức tạp thêm tình hình. Nếu khách hàng chú ý và bộ điều hợp được ghi chép đầy đủ, thì giải pháp như vậy có thể chấp nhận được.

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

  1. Khi bạn cần sử dụng một lớp 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 đối tượng bộ điều hợp kết thú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ó cần một số chức năng chung. Thay vì tạo các lớp con bổ sung (sẽ dẫn đến trùng lặp mã), tốt hơn là sử dụng bộ điều hợp.

Ư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. Nếu bạn có nhiều giao diện không tương thích, số lượng các lớp bổ sung có thể trở nên không thể quản lý được.

Đừng nhầm lẫn bộ điều hợp với mặt tiền hoặc trang trí

Chỉ với một cuộc kiểm tra bề ngoài, một bộ chuyển đổi có thể bị nhầm lẫn với các mẫu trang trí và mặt tiền. Sự khác biệt giữa bộ điều hợp và mặt tiền là mặt tiền giới thiệu một giao diện mới và bao bọc toàn bộ hệ thống con. Và một trình trang trí, không giống như một bộ điều hợp, thay đổi chính đối tượng chứ không phải giao diện.

Thuật toán từng bước

  1. Đầu tiên, hãy chắc chắn rằng bạn có một vấn đề mà mẫu này có thể giải quyết.

  2. Xác định giao diện máy khách sẽ được sử dụng để tương tác gián tiếp với các đối tượng không tương thích.

  3. Làm cho lớp bộ điều hợp kế thừa giao diện được xác định trong bước trước.

  4. Trong lớp bộ điều hợp, hãy tạo một trường để lưu trữ tham chiếu đến đối tượng bộ điều hợp. Tham chiếu này được chuyển đến 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ộ điều hợp. Một phương pháp có thể:

    • Truyền cuộc gọi mà không thực hiện bất kỳ thay đổi nào

    • Sửa đổi, bổ sung dữ liệu, tăng/giảm số lần gọi phương thức đích, v.v.

    • Trong trường hợp cực đoan, nếu một phương thức cụ thể vẫn không tương thích, hãy đưa ra Ngoại lệ UnsupportedOperationException. Các hoạt động không được hỗ trợ phải được ghi lại nghiêm ngặt.

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

Tất nhiên, mẫu thiết kế này không phải là thuốc chữa bách bệnh, nhưng nó có thể giúp bạn giải quyết vấn đề không tương thích giữa các đối tượng có giao diện khác nhau một cách tinh tế. Một nhà phát triển biết các mẫu cơ bản sẽ đi trước vài bước so với những người chỉ biết viết thuật toán, bởi vì các mẫu thiết kế được yêu cầu để tạo các ứng dụng nghiêm túc. Việc sử dụng lại mã không quá khó và việc bảo trì trở nên thú vị. Đó là tất cả cho ngày hôm nay! Nhưng chúng ta sẽ sớm tiếp tục tìm hiểu 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