Mẫu Adapter
Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 71 trên 87.
Mẫu bộ chuyển đổi là một mẫu thiết kế cấu trúc giúp các giao diện không tương thích có thể hoạt động cùng nhau. Mẫu này đóng vai trò như cầu nối giữa hai lớp, chuyển đổi giao diện của một lớp thành giao diện mà phía máy khách mong đợi. Hãy hình dung mẫu này như một bộ chuyển đổi nguồn, giúp bạn cắm thiết bị châu Âu vào ổ cắm ở Mỹ.
Hãy tưởng tượng bạn có một giao diện MediaPlayer hiện có để phát các tệp âm thanh, nhưng bạn cần tích hợp một thư viện bên thứ ba sử dụng giao diện hoàn toàn khác để phát video:
// Giao diện hiện có mà code của bạn sử dụng
interface MediaPlayer {
void play(String filename);
}
// Lớp (class) của bên thứ ba có giao diện không tương thích
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}Bộ chuyển đổi bao bọc lớp không tương thích và triển khai giao diện cần thiết, chuyển đổi các lệnh gọi giữa chúng:
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}Giờ đây, code phía client của bạn có thể dùng trình phát video thông qua giao diện quen thuộc MediaPlayer:
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // Đầu ra: Đang phát video: movie.mp4Mẫu Adapter rất hữu ích khi tích hợp code cũ hoặc thư viện của bên thứ ba mà không sửa mã nguồn của chúng. Phía máy khách không nhận ra rằng nó đang làm việc với một lớp đã được điều chỉnh. Nó chỉ dùng giao diện mà nó đã biết.
Thử thách
DễHãy xây dựng một hệ thống chuyển đổi nhiệt độ bằng mẫu Adapter nhé! Bạn có một ứng dụng thời tiết hiện có, hoạt động với nhiệt độ Celsius, nhưng cần tích hợp một cảm biến nhiệt độ cũ chỉ cho kết quả theo Fahrenheit. Thay vì sửa đổi một trong hai hệ thống, bạn sẽ tạo một adapter để kết nối chúng.
Bạn sẽ tổ chức code trong bốn tệp:
TemperatureProvider.java: Định nghĩa giao diện mà ứng dụng thời tiết cần dùng. Giao diện này cần khai báo phương thứcgetTemperatureCelsius(), trả về một số thực biểu thị nhiệt độ theo Celsius.FahrenheitSensor.java: Đây là cảm biến cũ có giao diện không tương thích. Hãy tạo một lớp có trường số thực private lưu nhiệt độ theo Fahrenheit, được thiết lập qua hàm khởi tạo. Lớp này cần có phương thứcreadFahrenheit(), trả về giá trị Fahrenheit đã lưu.SensorAdapter.java: Tạo adapter giúp cảm biến Fahrenheit tương thích với ứng dụng thời tiết. Adapter của bạn cần triển khaiTemperatureProvidervà bao bọc mộtFahrenheitSensor. Khi gọigetTemperatureCelsius(), adapter cần đọc giá trị từ cảm biến rồi chuyển đổi sang Celsius theo công thức:(fahrenheit - 32) * 5 / 9.Main.java: Kết hợp mọi thứ! Bạn sẽ nhận một đầu vào: giá trị nhiệt độ theo Fahrenheit (số thực).Tạo một
FahrenheitSensorvới giá trị đầu vào. Sau đó, tạo mộtSensorAdapterbao bọc cảm biến này. Lưu adapter vào một biến kiểuTemperatureProviderđể minh họa rằng ứng dụng thời tiết có thể sử dụng adapter thông qua giao diện cần dùng.In kết quả theo định dạng này:
Temperature: [celsius] C, trong đó celsius được định dạng với một chữ số thập phân.
Bạn sẽ nhận một đầu vào: nhiệt độ Fahrenheit (số thực).
Ví dụ, với đầu vào 98.6, đầu ra sẽ là:
Temperature: 37.0 CHãy chú ý cách lớp Main chỉ làm việc với giao diện TemperatureProvider. Lớp này không biết rằng nhiệt độ thực tế đến từ một cảm biến Fahrenheit được điều chỉnh phía sau. Đây chính là nét tinh tế của mẫu Adapter!
Tự làm thử
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double fahrenheit = scanner.nextDouble();
// TODO: Tạo FahrenheitSensor với giá trị đầu vào
// TODO: Tạo SensorAdapter bao bọc cảm biến
// TODO: Lưu bộ chuyển đổi vào biến TemperatureProvider
// TODO: Lấy nhiệt độ theo độ C và in ra
// Định dạng: "Temperature: [celsius] C" với một chữ số thập phân
// Gợi ý: Dùng String.format("%.1f", value) để định dạng
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online