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ứaExcuse
các phương thức generateExcuse()
và 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 Excuse
và một thư viện có một lớp SuperStudentExcuse
triể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: 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: Để 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ờ đó, Middleware
nó có thể gọi các phương thức của đối tượng đầu tiên. Middleware
nhậ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 generateExcuse
và 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 generateExcuse
chỉ đơ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 dislikeExcuse
yê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 likeExcuse
nằm trong giao diện Excuse
như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
-
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.
-
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
-
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.
-
Xác định giao diện máy khách thay mặt cho lớp khác sẽ được sử dụng.
-
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.
-
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.
-
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.
-
-
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.
GO TO FULL VERSION