Mẫu Command
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 70 trên 87.
Mẫu lệnh là một mẫu thiết kế hành vi, biến một yêu cầu thành một đối tượng độc lập chứa toàn bộ thông tin về yêu cầu đó. Nhờ vậy, bạn có thể truyền yêu cầu làm đối số cho phương thức, trì hoãn hoặc xếp hàng chờ thực thi yêu cầu, đồng thời hỗ trợ các thao tác có thể hoàn tác.
Hãy hình dung như ở nhà hàng: thay vì trực tiếp bảo đầu bếp nấu món gì, bạn viết món mình gọi vào phiếu. Người phục vụ (invoker) mang phiếu gọi món (command) vào bếp, nơi phiếu có thể được xếp hàng, ưu tiên hoặc thậm chí bị hủy. Mẫu này tách biệt đối tượng gọi thao tác khỏi đối tượng thực hiện thao tác đó.
Mẫu này có bốn thành phần chính: giao diện Command khai báo phương thức execute, các lớp ConcreteCommand triển khai những hành động cụ thể, một Receiver thực hiện công việc thực tế và một Invoker kích hoạt các lệnh:
interface Command {
void execute();
}class Light { // Bộ nhận
public void turnOn() {
System.out.println("Light is ON");
}
public void turnOff() {
System.out.println("Light is OFF");
}
}class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}class RemoteControl { // Bộ gọi lệnh
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}Bên gọi không biết hành động nào sẽ được thực hiện. Nó chỉ gọi execute():
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // Đầu ra: Light is ONMẫu lệnh (Command Pattern) phát huy hiệu quả khi bạn cần xếp hàng các thao tác, triển khai chức năng hoàn tác/làm lại hoặc lên lịch thực hiện tác vụ sau này. Mỗi lệnh đóng gói mọi thứ cần thiết để thực hiện một hành động, giúp hệ thống linh hoạt và dễ mở rộng hơn.
Thử thách
DễHãy xây dựng một hệ thống tự động hóa nhà thông minh bằng Mẫu lệnh (Command Pattern)! Bạn sẽ tạo một hệ thống cho phép điều khiển các thiết bị trong nhà bằng các lệnh, nhờ đó có thể xếp hàng đợi, thực thi và thậm chí lưu lại các thao tác để dùng sau, giống như một điều khiển từ xa đa năng không cần biết cách hoạt động bên trong của từng thiết bị.
Bạn sẽ sắp xếp code trong bốn tệp:
Command.java: Định nghĩa giao diệnCommandmà tất cả lệnh điều khiển thiết bị sẽ triển khai. Giao diện này khai báo một phương thức duy nhất làexecute()để thực hiện thao tác của lệnh.Devices.java: Tạo hai lớp nhận lệnh, đại diện cho các thiết bị nhà thông minh:Television: có các phương thứcpowerOn()để in raTV is now ON,powerOff()để in raTV is now OFFvàsetChannel(int channel)để in raTV channel set to [channel].Thermostat: có các phương thứcsetTemperature(int temp)để in raThermostat set to [temp] degreesvàturnOff()để in raThermostat turned OFF.Commands.java: Tạo các lớp lệnh cụ thể để đóng gói các thao tác của thiết bị:TVOnCommand: nhận mộtTelevisiontrong hàm khởi tạo và gọipowerOn()khi được thực thi.TVOffCommand: nhận mộtTelevisiontrong hàm khởi tạo và gọipowerOff()khi được thực thi.TVChannelCommand: nhận mộtTelevisionvà mộtint channeltrong hàm khởi tạo, rồi gọisetChannel()với kênh đó khi được thực thi.ThermostatSetCommand: nhận mộtThermostatvà mộtint temperaturetrong hàm khởi tạo, rồi gọisetTemperature()với giá trị đó khi được thực thi.Main.java: Tạo lớpRemoteControl(bộ gọi lệnh) có phương thứcsetCommand(Command cmd)để lưu một lệnh vàpressButton()để thực thi lệnh đã lưu.Trong phương thức main, bạn sẽ nhận hai đầu vào: số kênh TV (số nguyên) và nhiệt độ của bộ điều nhiệt (số nguyên).
Tạo một
Televisionvà mộtThermostat. Tạo mộtRemoteControlvà minh họa Mẫu lệnh bằng cách:- Đặt lệnh
TVOnCommandrồi nhấn nút - Đặt lệnh
TVChannelCommandvới kênh đầu vào rồi nhấn nút - Đặt lệnh
ThermostatSetCommandvới nhiệt độ đầu vào rồi nhấn nút - Đặt lệnh
TVOffCommandrồi nhấn nút
- Đặt lệnh
Bạn sẽ nhận hai đầu vào theo thứ tự: kênh TV (số nguyên) và nhiệt độ của bộ điều nhiệt (số nguyên).
Ví dụ, với đầu vào 5 và 72, đầu ra sẽ là:
TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFFLưu ý rằng RemoteControl không bao giờ tương tác trực tiếp với các thiết bị. Nó chỉ biết giao diện Command. Mỗi lệnh đóng gói mọi thứ cần thiết để thực hiện thao tác, giúp bạn dễ dàng thêm thiết bị và lệnh mới mà không cần thay đổi bộ gọi lệnh!
Tự làm thử
import java.util.Scanner;
// TODO: Tạo lớp RemoteControl (bộ gọi lệnh) với:
// - Phương thức setCommand(Command cmd) để lưu một lệnh
// - Phương thức pressButton() để thực thi lệnh đã lưu
class RemoteControl {
// TODO: Triển khai RemoteControl
}
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Đọc dữ liệu đầu vào
int channel = scanner.nextInt();
int temperature = scanner.nextInt();
// TODO: Tạo một Television và một Thermostat
// TODO: Tạo một RemoteControl
// TODO: Minh họa mẫu thiết kế Command:
// 1. Đặt TVOnCommand rồi nhấn nút
// 2. Đặt TVChannelCommand với kênh đầu vào rồi nhấn nút
// 3. Đặt ThermostatSetCommand với nhiệt độ đầu vào rồi nhấn nút
// 4. Đặt TVOffCommand rồi nhấn nút
scanner.close();
}
}
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