0
0
Lập trình
Harry Tran
Harry Tran106580903228332612117

Khám Phá Mẫu Thiết Kế: Cách Adapter Giúp Mã Nguồn Thanh Lịch Hơn

Đăng vào 5 tháng trước

• 6 phút đọc

Khám Phá Mẫu Thiết Kế: Cách Adapter Giúp Mã Nguồn Thanh Lịch Hơn

Bạn đã bao giờ gặp phải tình huống này chưa? Bạn mua một chiếc máy tính mới nhưng cổng kết nối không phù hợp với màn hình cũ của bạn. Hoặc bạn đi du lịch nước ngoài và sạc điện thoại của bạn không phù hợp với ổ điện địa phương. Trong những lúc như thế này, một adapter có thể giải quyết mọi vấn đề.

Trong thế giới phát triển phần mềm, chúng ta thường đối mặt với những vấn đề tương tự: bạn có một lớp mạnh mẽ nhưng hệ thống mới của bạn không thể gọi trực tiếp vì các giao diện của chúng "không nói cùng một ngôn ngữ". Liệu bạn có nên sửa đổi mã cũ hay làm giảm chất lượng hệ thống mới?

Đừng lo lắng — câu trả lời nằm ở một mẫu thiết kế được gọi là Mẫu Adapter, thường được ví như "của cắm" trong thế giới mã. Nó cho phép hai giao diện không tương thích làm việc cùng nhau một cách liền mạch — tất cả mà không cần thay đổi mã nguồn hiện có.


Mẫu Adapter Là Gì?

Nói một cách đơn giản, Mẫu Adapter chuyển đổi giao diện của một lớp thành một giao diện khác mà các khách hàng mong đợi, cho phép các lớp mà nếu không thì không thể làm việc cùng nhau do các giao diện không tương thích có thể hợp tác một cách trơn tru.

Nó bao gồm ba vai trò chính:

  1. Giao diện mục tiêu (Target Interface): Giao diện mà bạn muốn sử dụng, tiêu chuẩn mà bạn muốn “thích ứng” với.
  2. Adaptee: Lớp không tương thích mà bạn muốn tái sử dụng nhưng không thể sử dụng trực tiếp.
  3. Adapter: "Của cắm" mà thực hiện giao diện mục tiêu trong khi giữ một thể hiện của adaptee, dịch các cuộc gọi từ khách hàng thành các cuộc gọi đến adaptee.

Sơ đồ Lớp

Để hiểu rõ hơn về các mối quan hệ, hãy hình dung chúng với một sơ đồ lớp.

Phân Tích Mối Quan Hệ:

  • WavAdapter thực hiện giao diện MediaPlayer, có nghĩa là nó có thể được coi là một MediaPlayer.
  • WavAdapter giữ một thể hiện WavPlayer (mối quan hệ "có một"). Đây là cốt lõi của mẫu adapter: adapter nội bộ ủy thác các cuộc gọi đến adaptee.
  • AudioPlayer sử dụng giao diện MediaPlayer. Nó tương tác chỉ với MediaPlayer, không biết liệu triển khai thực tế là WavAdapter hay một trình phát khác.

Mẫu Adapter Trong Các Framework Phổ Biến

Tất cả điều này nghe có vẻ hay trong lý thuyết, nhưng Mẫu Adapter giúp ích như thế nào trong phát triển hàng ngày? Ngoài việc tích hợp hệ thống hoặc tính tương thích dịch vụ của bên thứ ba, bạn đã sử dụng các adapter hàng ngày trong các framework như Spring.

Một ví dụ điển hình là HandlerAdapter trong Spring MVC.

Cốt lõi của Spring MVC là DispatcherServlet. Khi một yêu cầu HTTP đến, nó phải tìm ra controller phù hợp để xử lý. Nhưng các nhà phát triển viết controller theo nhiều phong cách khác nhau:

  • Một số thực hiện một giao diện cụ thể như Controller.
  • Một số sử dụng chú thích như @Controller@RequestMapping.
  • Những người khác có thể sử dụng các loại xử lý tùy chỉnh.

Nếu DispatcherServlet phải xử lý tất cả các biến thể này trực tiếp, nó sẽ trở nên cồng kềnh và lộn xộn. Mỗi khi một loại controller mới xuất hiện, bạn sẽ phải sửa đổi mã cốt lõi của nó.

Giải pháp của Spring: HandlerAdapter.

  • Giao diện mục tiêu: Giao diện HandlerAdapter, định nghĩa phương thức handle(). DispatcherServlet chỉ nói chuyện với giao diện này.
  • Adaptee: Các triển khai controller khác nhau mà chúng tôi viết.
  • Adapter: Các triển khai khác nhau của HandlerAdapter trong Spring. Ví dụ, SimpleControllerHandlerAdapter xử lý các controller thực hiện Controller, trong khi RequestMappingHandlerAdapter hỗ trợ các controller được chú thích bằng @RequestMapping.

Khi một yêu cầu đến, DispatcherServlet hỏi từng HandlerAdapter: “Bạn có thể xử lý controller này không?” Khi tìm thấy một sự khớp, nó ủy thác cuộc gọi.

Đây là bản chất của mẫu adapter: biến các giao diện không tương thích (các controller khác nhau) thành một giao diện thống nhất (HandlerAdapter). Thiết kế này giữ cho DispatcherServlet đơn giản, có thể mở rộng và mở cho các loại xử lý mới mà không cần thay đổi nội bộ.


Ví Dụ Về Java

Giả sử chúng ta đang xây dựng một trình phát audio hiện chỉ hỗ trợ MP3. Chúng ta định nghĩa một giao diện MediaPlayer như là mục tiêu của chúng ta:

java Copy
// Giao diện mục tiêu
public interface MediaPlayer {
    void play(String audioType, String fileName);
}

Bây giờ chúng ta nhận được một yêu cầu mới: hỗ trợ tệp WAV. Chúng ta đã có một lớp WavPlayer, nhưng giao diện của nó không tương thích.

java Copy
// Adaptee
public class WavPlayer {
    public void playWav(String fileName) {
        System.out.println("Đang phát tệp WAV: " + fileName);
    }
}

Chúng ta không thể sửa đổi MediaPlayer hoặc WavPlayer. Giải pháp tốt nhất là tạo một adapter: WavAdapter.

java Copy
// Adapter
public class WavAdapter implements MediaPlayer {

    private WavPlayer wavPlayer;

    public WavAdapter(WavPlayer wavPlayer) {
        this.wavPlayer = wavPlayer;
    }

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("wav")) {
            this.wavPlayer.playWav(fileName);
        } else {
            System.out.println("Loại âm thanh không hợp lệ: " + audioType);
        }
    }
}

Bây giờ, AudioPlayer chính có thể sử dụng nó mà không cần quan tâm đến chi tiết:

java Copy
public class AudioPlayer {
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Đang phát tệp MP3: " + fileName);
        } else if (audioType.equalsIgnoreCase("wav")) {
            MediaPlayer mediaPlayer = new WavAdapter(new WavPlayer());
            mediaPlayer.play(audioType, fileName);
        } else {
            System.out.println("Loại âm thanh không hợp lệ: " + audioType);
        }
    }

    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();
        audioPlayer.play("mp3", "beyond_the_horizon.mp3");
        audioPlayer.play("wav", "my_new_song.wav");
    }
}

Giá Trị Thực Sự Của Mẫu Adapter

Sức mạnh thực sự của mẫu adapter nằm ở giải pháp thanh lịch của nó cho sự không tương thích của giao diện:

  • Nguyên tắc Mở/Đóng: Để hỗ trợ một dịch vụ mới, chỉ cần thêm một adapter mới — không cần thay đổi mã hiện có.
  • Phân Tách Mối Quan Hệ: Mỗi lớp làm công việc của riêng mình. WavPlayer phát WAV, AudioPlayer gọi giao diện MediaPlayer, và adapter chỉ dịch giữa chúng. Sạch sẽ và dễ bảo trì.
  • Tích Hợp Liền Mạch: Hệ thống cũ và các module mới có thể hoạt động cùng nhau một cách trơn tru.

Vì vậy, lần tới khi bạn gặp hai giao diện không tương thích, hãy dừng lại và tự hỏi: liệu tôi chỉ cần một “adapter” để cho chúng bắt tay không?

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào