Menu
CoddyTech

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 ON

Mẫ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.

challenge icon

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ện Command mà 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ức powerOn() để in ra TV is now ON, powerOff() để in ra TV is now OFF và setChannel(int channel) để in ra TV channel set to [channel].

    Thermostat: có các phương thức setTemperature(int temp) để in ra Thermostat set to [temp] degrees và turnOff() để in ra Thermostat 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ột Television trong hàm khởi tạo và gọi powerOn() khi được thực thi.

    TVOffCommand: nhận một Television trong hàm khởi tạo và gọi powerOff() khi được thực thi.

    TVChannelCommand: nhận một Television và một int channel trong hàm khởi tạo, rồi gọi setChannel() với kênh đó khi được thực thi.

    ThermostatSetCommand: nhận một Thermostat và một int temperature trong hàm khởi tạo, rồi gọi setTemperature() với giá trị đó khi được thực thi.

  • Main.java: Tạo lớp RemoteControl (bộ gọi lệnh) có phương thức setCommand(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 Television và một Thermostat. Tạo một RemoteControl và minh họa Mẫu lệnh bằng cách:

    1. Đặt lệnh TVOnCommand rồi nhấn nút
    2. Đặt lệnh TVChannelCommand với kênh đầu vào rồi nhấn nút
    3. Đặt lệnh ThermostatSetCommand với nhiệt độ đầu vào rồi nhấn nút
    4. Đặt lệnh TVOffCommand rồi nhấn nút

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 OFF

Lư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();
    }
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch Java online