Adapter Pattern là một trong các mẫu thiết kế thuộc nhóm Structural Patterns (mẫu cấu trúc). Nó cho phép các đối tượng có giao diện không tương thích làm việc với nhau bằng cách cung cấp một lớp trung gian để chuyển đổi giao diện của đối tượng này thành giao diện mà đối tượng khác mong đợi.
Mục đích của Adapter Pattern
Adapter Pattern được sử dụng khi bạn muốn tích hợp một lớp hiện có vào một hệ thống, nhưng lớp đó có giao diện khác với những gì hệ thống yêu cầu. Thay vì thay đổi lớp hiện có (có thể là không khả thi nếu lớp thuộc thư viện bên ngoài hoặc đã được triển khai từ lâu), bạn có thể tạo ra một lớp Adapter để làm “cầu nối” giữa hệ thống và lớp không tương thích.
Cách hoạt động của Adapter Pattern
Adapter Pattern hoạt động bằng cách tạo ra một lớp Adapter có nhiệm vụ chuyển đổi các phương thức và thuộc tính của lớp không tương thích thành giao diện mà hệ thống mong đợi. Lớp này sẽ triển khai giao diện hoặc kế thừa từ một lớp mà hệ thống sử dụng, và bên trong nó, sẽ chuyển tiếp các lời gọi phương thức đến đối tượng gốc với giao diện không tương thích.
Ví dụ
Giả sử chúng ta có hai lớp, một lớp mới đang sử dụng giao diện USB để sạc thiết bị, và một lớp cũ sử dụng giao diện MicroUSB. Hệ thống mới của bạn yêu cầu sử dụng USB, nhưng bạn lại có một thiết bị cũ chỉ hỗ trợ MicroUSB. Ở đây, chúng ta sẽ sử dụng Adapter Pattern để kết nối hệ thống với thiết bị cũ mà không cần phải thay đổi thiết bị đó.
Giao diện USB mà hệ thống mong muốn sử dụng:
public interface USB {
void connectWithUsbCable();
}
Lớp cũ sử dụng MicroUSB:
public class MicroUSBDevice {
public void connectWithMicroUsbCable() {
System.out.println("Connected with Micro USB cable");
}
}
Lớp Adapter:
public class MicroUsbToUsbAdapter implements USB {
private MicroUSBDevice microUSBDevice;
public MicroUsbToUsbAdapter(MicroUSBDevice microUSBDevice) {
this.microUSBDevice = microUSBDevice;
}
@Override
public void connectWithUsbCable() {
// Chuyển lời gọi phương thức USB thành lời gọi phương thức MicroUSB
microUSBDevice.connectWithMicroUsbCable();
}
}
Sử dụng Adapter:
public class Main {
public static void main(String[] args) {
// Tạo một thiết bị sử dụng MicroUSB
MicroUSBDevice microUsbDevice = new MicroUSBDevice();
// Tạo Adapter để chuyển đổi từ MicroUSB sang USB
USB adapter = new MicroUsbToUsbAdapter(microUsbDevice);
// Sử dụng giao diện USB để kết nối với thiết bị MicroUSB
adapter.connectWithUsbCable();
}
}
Output:
Connected with Micro USB cable
Trong ví dụ trên, lớp MicroUsbToUsbAdapter đã chuyển đổi giao diện USB thành giao diện MicroUSB, giúp hệ thống có thể sử dụng được thiết bị cũ mà không cần phải thay đổi gì trong lớp MicroUSBDevice.
Khi nào nên sử dụng Adapter Pattern?
- Khi bạn có một lớp hoặc một thư viện có sẵn không thể chỉnh sửa và bạn muốn tích hợp nó vào hệ thống với một giao diện khác.
- Khi bạn muốn sử dụng lại các lớp cũ mà không phải thay đổi cấu trúc hoặc giao diện của chúng.
- Khi bạn cần làm cho một lớp không tương thích làm việc với một lớp khác mà cả hai không có chung giao diện.
Các loại Adapter Pattern
Adapter Pattern có thể được chia thành hai loại:
- Object Adapter: Sử dụng thành phần đối tượng để thích nghi. Trong trường hợp này, lớp Adapter chứa một tham chiếu đến đối tượng mà nó thích nghi và gọi các phương thức của đối tượng đó.
- Class Adapter: Sử dụng kế thừa để thích nghi. Trong trường hợp này, Adapter kế thừa từ cả lớp mục tiêu (Target Class) và lớp nguồn (Adaptee Class). Tuy nhiên, do giới hạn của đa kế thừa, cách này thường không được sử dụng trong các ngôn ngữ không hỗ trợ đa kế thừa như Java.
Lợi ích của Adapter Pattern
- Tái sử dụng các lớp cũ: Adapter Pattern cho phép bạn sử dụng lại các lớp hoặc thư viện đã được triển khai trước đó mà không cần chỉnh sửa mã gốc.
- Tách biệt logic: Adapter Pattern giúp giữ mã nguồn gọn gàng và dễ bảo trì bằng cách tách biệt logic chuyển đổi giữa các giao diện.
- Giảm phụ thuộc: Bằng cách sử dụng Adapter, bạn có thể giảm sự phụ thuộc trực tiếp vào các lớp cũ, giúp dễ dàng thay đổi hoặc nâng cấp lớp cũ mà không ảnh hưởng đến các thành phần khác của hệ thống.
Kết luận
Adapter Pattern là một giải pháp hữu ích để tích hợp các lớp không tương thích với hệ thống mà không cần phải thay đổi lớp gốc. Nó giúp giữ mã nguồn dễ hiểu, tái sử dụng, và dễ bảo trì. Đây là một mẫu thiết kế lý tưởng trong các trường hợp bạn phải làm việc với các hệ thống hoặc thư viện cũ mà không thể chỉnh sửa chúng.