Menu
CoddyTech

命令模式

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 70 课,共 87 课。

命令模式是一种行为型设计模式,它会将请求转换为一个独立的对象,其中包含有关该请求的所有信息。这样,你就可以将请求作为方法参数传递,延迟或排队执行请求,并支持可撤销的操作。

可以把它想成一家餐厅:你不直接告诉厨师要做什么菜,而是把订单写在单子上。服务员(调用者)把单子(命令)送到厨房,在那里,订单可以排队、调整优先级,甚至取消。这种模式将调用操作的对象与执行操作的对象解耦。

该模式有四个关键组成部分:声明 execute 方法的 Command 接口,实现具体操作的 ConcreteCommand 类,执行实际工作的 Receiver,以及触发命令的 Invoker:

interface Command {
    void execute();
}
class Light {  // 接收者
    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 {  // 调用者
    private Command command;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
    }
}

调用者不知道将执行什么操作。它只会调用 execute():

Light light = new Light();
Command lightOn = new LightOnCommand(light);

RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();  // 输出:Light is ON

当你需要将操作加入队列、实现撤销/重做功能或安排稍后执行任务时,命令模式特别适用。每个命令都封装了执行操作所需的一切,让你的系统更加灵活、易于扩展。

challenge icon

挑战

简单

让我们使用命令模式来构建一个智能家居自动化系统!你将创建一个系统,通过命令控制各种家用设备,从而可以将操作排队、执行,甚至保存起来供以后使用,就像一个不需要了解每台设备内部工作原理的万能遥控器。

你将把代码组织在四个文件中:

  • Command.java:定义所有设备命令都将实现的 Command 接口。它应声明一个 execute() 方法,用于执行命令的操作。
  • Devices.java:创建两个表示智能家居设备的接收者类:

    Television:包含 powerOn() 方法,该方法会输出 TV is now ON;包含 powerOff() 方法,该方法会输出 TV is now OFF;还包含 setChannel(int channel) 方法,该方法会输出 TV channel set to [channel]。

    Thermostat:包含 setTemperature(int temp) 方法,该方法会输出 Thermostat set to [temp] degrees;还包含 turnOff() 方法,该方法会输出 Thermostat turned OFF。

  • Commands.java:创建封装设备操作的具体命令类:

    TVOnCommand:在构造函数中接收一个 Television,执行时调用 powerOn()。

    TVOffCommand:在构造函数中接收一个 Television,执行时调用 powerOff()。

    TVChannelCommand:在构造函数中接收一个 Television 和一个 int channel,执行时使用该频道调用 setChannel()。

    ThermostatSetCommand:在构造函数中接收一个 Thermostat 和一个 int temperature,执行时使用该温度调用 setTemperature()。

  • Main.java:创建一个 RemoteControl 类(调用者),其中包含 setCommand(Command cmd) 方法,用于存储命令;还包含 pressButton() 方法,用于执行存储的命令。

    在主方法中,你将接收两个输入:电视频道号(整数)和恒温器温度(整数)。

    创建一个 Television 和一个 Thermostat。创建一个 RemoteControl,并通过以下操作演示命令模式:

    1. 设置一个 TVOnCommand 并按下按钮
    2. 设置一个带有输入频道的 TVChannelCommand 并按下按钮
    3. 设置一个带有输入温度的 ThermostatSetCommand 并按下按钮
    4. 设置一个 TVOffCommand 并按下按钮

你将按顺序接收两个输入:电视频道(整数)和恒温器温度(整数)。

例如,输入为 5 和 72 时,输出为:

TV is now ON
TV channel set to 5
Thermostat set to 72 degrees
TV is now OFF

注意,RemoteControl 从不直接与设备交互。它只了解 Command 接口。每个命令都封装了执行其操作所需的一切,因此无需更改调用者,就能轻松添加新设备和命令!

亲手试试

import java.util.Scanner;

// TODO: 创建 RemoteControl 类(调用者),包含:
// 使用 setCommand(Command cmd) 方法存储命令
// 使用 pressButton() 方法执行存储的命令

class RemoteControl {
    // TODO: 实现 RemoteControl
}

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 读取输入
        int channel = scanner.nextInt();
        int temperature = scanner.nextInt();
        
        // TODO: 创建一个 Television 和一个 Thermostat
        
        // TODO: 创建一个 RemoteControl
        
        // TODO: 演示命令模式:
        // 1. 设置 TVOnCommand,然后按下按钮
        // 2. 使用输入的频道设置 TVChannelCommand,然后按下按钮
        // 3. 使用输入的温度设置 ThermostatSetCommand,然后按下按钮
        // 4. 设置 TVOffCommand,然后按下按钮
        
        scanner.close();
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器