1. 项目结构
bash
command-pattern-demo/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── demo/
│ │ │ └── command/
│ │ │ ├── Command.java
│ │ │ ├── Light.java
│ │ │ ├── LightOnCommand.java
│ │ │ ├── LightOffCommand.java
│ │ │ ├── CeilingFan.java
│ │ │ ├── CeilingFanHighCommand.java
│ │ │ ├── CeilingFanOffCommand.java
│ │ │ ├── Stereo.java
│ │ │ ├── StereoOnWithCDCommand.java
│ │ │ ├── StereoOffCommand.java
│ │ │ ├── MacroCommand.java
│ │ │ ├── RemoteControl.java
│ │ │ ├── NoCommand.java
│ │ │ └── Client.java
│ │ └── resources/
└── test/
Maven配置文件 (pom.xml)
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>command-pattern-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 代码实现
-
命令接口 (Command.java)
javapackage com.demo.command; /** * 命令接口:所有命令都需要实现这个接口 */ @FunctionalInterface public interface Command { void execute(); // Java 8 支持默认方法,可以添加默认实现 default void undo() { System.out.println("撤销操作未实现"); } } -
接收者类 - 电灯 (Light.java)
javapackage com.demo.command; import lombok.Getter; /** * 接收者类:知道如何执行具体的操作 */ @Getter public class Light { private String location; private boolean isOn = false; public Light(String location) { this.location = location; } public void on() { this.isOn = true; System.out.println(location + " 电灯已打开"); } public void off() { this.isOn = false; System.out.println(location + " 电灯已关闭"); } } -
具体命令类 - 打开电灯 (LightOnCommand.java)
javapackage com.demo.command; /** * 具体命令类:实现打开电灯的命令 */ public class LightOnCommand implements Command { private Light light; public LightOnCommand(Light light) { this.light = light; } @Override public void execute() { light.on(); } @Override public void undo() { light.off(); } @Override public String toString() { return "打开 " + light.getLocation() + " 的电灯"; } } -
具体命令类 - 关闭电灯 (LightOffCommand.java)
javapackage com.demo.command; /** * 具体命令类:实现关闭电灯的命令 */ public class LightOffCommand implements Command { private Light light; public LightOffCommand(Light light) { this.light = light; } @Override public void execute() { light.off(); } @Override public void undo() { light.on(); } @Override public String toString() { return "关闭 " + light.getLocation() + " 的电灯"; } } -
接收者类 - 吊扇 (CeilingFan.java)
javapackage com.demo.command; import lombok.Getter; /** * 接收者类:吊扇 */ @Getter public class CeilingFan { public static final int HIGH = 3; public static final int MEDIUM = 2; public static final int LOW = 1; public static final int OFF = 0; private String location; private int speed; public CeilingFan(String location) { this.location = location; this.speed = OFF; } public void high() { this.speed = HIGH; System.out.println(location + " 吊扇设置为高速"); } public void medium() { this.speed = MEDIUM; System.out.println(location + " 吊扇设置为中速"); } public void low() { this.speed = LOW; System.out.println(location + " 吊扇设置为低速"); } public void off() { this.speed = OFF; System.out.println(location + " 吊扇已关闭"); } } -
具体命令类 - 吊扇高速命令 (CeilingFanHighCommand.java)
javapackage com.demo.command; /** * 具体命令类:设置吊扇为高速 */ public class CeilingFanHighCommand implements Command { private CeilingFan ceilingFan; private int previousSpeed; public CeilingFanHighCommand(CeilingFan ceilingFan) { this.ceilingFan = ceilingFan; } @Override public void execute() { previousSpeed = ceilingFan.getSpeed(); ceilingFan.high(); } @Override public void undo() { if (previousSpeed == CeilingFan.HIGH) { ceilingFan.high(); } else if (previousSpeed == CeilingFan.MEDIUM) { ceilingFan.medium(); } else if (previousSpeed == CeilingFan.LOW) { ceilingFan.low(); } else { ceilingFan.off(); } } @Override public String toString() { return "设置 " + ceilingFan.getLocation() + " 的吊扇为高速"; } } -
具体命令类 - 关闭吊扇命令 (CeilingFanOffCommand.java)
javapackage com.demo.command; /** * 具体命令类:关闭吊扇 */ public class CeilingFanOffCommand implements Command { private CeilingFan ceilingFan; private int previousSpeed; public CeilingFanOffCommand(CeilingFan ceilingFan) { this.ceilingFan = ceilingFan; } @Override public void execute() { previousSpeed = ceilingFan.getSpeed(); ceilingFan.off(); } @Override public void undo() { if (previousSpeed == CeilingFan.HIGH) { ceilingFan.high(); } else if (previousSpeed == CeilingFan.MEDIUM) { ceilingFan.medium(); } else if (previousSpeed == CeilingFan.LOW) { ceilingFan.low(); } else { ceilingFan.off(); } } @Override public String toString() { return "关闭 " + ceilingFan.getLocation() + " 的吊扇"; } } -
接收者类 - 音响 (Stereo.java)
javapackage com.demo.command; import lombok.Getter; /** * 接收者类:音响系统 */ @Getter public class Stereo { private String location; private int volume = 5; private boolean isOn = false; public Stereo(String location) { this.location = location; } public void on() { this.isOn = true; System.out.println(location + " 音响已打开"); } public void off() { this.isOn = false; System.out.println(location + " 音响已关闭"); } public void setCD() { System.out.println(location + " 音响设置为CD模式"); } public void setVolume(int volume) { this.volume = volume; System.out.println(location + " 音响音量设置为: " + volume); } } -
具体命令类 - 打开音响并播放CD (StereoOnWithCDCommand.java)
javapackage com.demo.command; /** * 具体命令类:打开音响并播放CD */ public class StereoOnWithCDCommand implements Command { private Stereo stereo; public StereoOnWithCDCommand(Stereo stereo) { this.stereo = stereo; } @Override public void execute() { stereo.on(); stereo.setCD(); stereo.setVolume(11); } @Override public void undo() { stereo.off(); } @Override public String toString() { return "打开 " + stereo.getLocation() + " 的音响并播放CD"; } } -
具体命令类 - 关闭音响 (StereoOffCommand.java)
javapackage com.demo.command; /** * 具体命令类:关闭音响 */ public class StereoOffCommand implements Command { private Stereo stereo; public StereoOffCommand(Stereo stereo) { this.stereo = stereo; } @Override public void execute() { stereo.off(); } @Override public void undo() { stereo.on(); stereo.setCD(); stereo.setVolume(11); } @Override public String toString() { return "关闭 " + stereo.getLocation() + " 的音响"; } } -
宏命令类 - 一次执行多个命令 (MacroCommand.java)
javapackage com.demo.command; import java.util.ArrayList; import java.util.List; /** * 宏命令:一次执行多个命令 */ public class MacroCommand implements Command { private List<Command> commands; public MacroCommand(List<Command> commands) { this.commands = new ArrayList<>(commands); } @Override public void execute() { System.out.println("=== 开始执行宏命令 ==="); commands.forEach(Command::execute); System.out.println("=== 宏命令执行完成 ==="); } @Override public void undo() { System.out.println("=== 开始撤销宏命令 ==="); // 反向执行撤销操作 for (int i = commands.size() - 1; i >= 0; i--) { commands.get(i).undo(); } System.out.println("=== 宏命令撤销完成 ==="); } public void addCommand(Command command) { commands.add(command); } @Override public String toString() { return "宏命令,包含 " + commands.size() + " 个子命令"; } } -
空命令类 - 默认空实现 (NoCommand.java)
javapackage com.demo.command; /** * 空命令:用于初始化遥控器按钮,避免空指针检查 * 这是空对象模式的应用 */ public class NoCommand implements Command { @Override public void execute() { // 什么都不做 } @Override public void undo() { // 什么都不做 } @Override public String toString() { return "空命令"; } } -
调用者类 - 遥控器 (RemoteControl.java)
javapackage com.demo.command; import java.util.Stack; /** * 调用者类:遥控器,持有命令对象并触发命令执行 */ public class RemoteControl { private Command[] onCommands; private Command[] offCommands; private Stack<Command> undoStack; public RemoteControl() { onCommands = new Command[7]; offCommands = new Command[7]; undoStack = new Stack<>(); Command noCommand = new NoCommand(); for (int i = 0; i < 7; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } } public void setCommand(int slot, Command onCommand, Command offCommand) { if (slot < 0 || slot >= onCommands.length) { throw new IllegalArgumentException("无效的插槽: " + slot); } onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void onButtonWasPushed(int slot) { if (slot < 0 || slot >= onCommands.length) { throw new IllegalArgumentException("无效的插槽: " + slot); } onCommands[slot].execute(); undoStack.push(onCommands[slot]); } public void offButtonWasPushed(int slot) { if (slot < 0 || slot >= offCommands.length) { throw new IllegalArgumentException("无效的插槽: " + slot); } offCommands[slot].execute(); undoStack.push(offCommands[slot]); } public void undoButtonWasPushed() { if (!undoStack.isEmpty()) { Command lastCommand = undoStack.pop(); System.out.println("=== 执行撤销操作 ==="); lastCommand.undo(); } else { System.out.println("没有可撤销的操作"); } } public void showCommands() { System.out.println("\n======= 遥控器配置 ======="); for (int i = 0; i < onCommands.length; i++) { System.out.println("[插槽 " + i + "] " + onCommands[i] + " / " + offCommands[i]); } System.out.println("=========================\n"); } // 使用Java 8的Lambda表达式创建命令(函数式接口) public void setLambdaCommand(int slot, Runnable onAction, Runnable offAction) { setCommand(slot, () -> onAction.run(), () -> offAction.run()); } } -
客户端类 - 演示使用 (Client.java)
javapackage com.demo.command; import java.util.Arrays; /** * 客户端:演示命令模式的使用 */ public class Client { public static void main(String[] args) { System.out.println("=== 命令模式演示:智能家居控制系统 ===\n"); // 1. 创建接收者(设备) Light livingRoomLight = new Light("客厅"); Light kitchenLight = new Light("厨房"); CeilingFan ceilingFan = new CeilingFan("卧室"); Stereo stereo = new Stereo("客厅"); // 2. 创建具体命令 Command livingRoomLightOn = new LightOnCommand(livingRoomLight); Command livingRoomLightOff = new LightOffCommand(livingRoomLight); Command kitchenLightOn = new LightOnCommand(kitchenLight); Command kitchenLightOff = new LightOffCommand(kitchenLight); Command ceilingFanHigh = new CeilingFanHighCommand(ceilingFan); Command ceilingFanOff = new CeilingFanOffCommand(ceilingFan); Command stereoOnWithCD = new StereoOnWithCDCommand(stereo); Command stereoOff = new StereoOffCommand(stereo); // 3. 创建调用者(遥控器) RemoteControl remoteControl = new RemoteControl(); // 4. 设置遥控器按钮 remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff); remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff); remoteControl.setCommand(2, ceilingFanHigh, ceilingFanOff); remoteControl.setCommand(3, stereoOnWithCD, stereoOff); // 5. 显示遥控器配置 remoteControl.showCommands(); // 6. 测试遥控器功能 System.out.println("=== 测试遥控器功能 ==="); remoteControl.onButtonWasPushed(0); // 打开客厅灯 remoteControl.offButtonWasPushed(0); // 关闭客厅灯 remoteControl.undoButtonWasPushed(); // 撤销 remoteControl.onButtonWasPushed(1); // 打开厨房灯 remoteControl.onButtonWasPushed(2); // 打开吊扇高速 remoteControl.onButtonWasPushed(3); // 打开音响 System.out.println("\n=== 测试撤销功能 ==="); remoteControl.undoButtonWasPushed(); // 撤销音响 remoteControl.undoButtonWasPushed(); // 撤销吊扇 remoteControl.undoButtonWasPushed(); // 撤销厨房灯 // 7. 创建宏命令 System.out.println("\n=== 测试宏命令 ==="); Command partyOnMacro = new MacroCommand( Arrays.asList(livingRoomLightOn, stereoOnWithCD, ceilingFanHigh) ); Command partyOffMacro = new MacroCommand( Arrays.asList(livingRoomLightOff, stereoOff, ceilingFanOff) ); remoteControl.setCommand(4, partyOnMacro, partyOffMacro); remoteControl.showCommands(); System.out.println("=== 执行派对模式 ==="); remoteControl.onButtonWasPushed(4); // 执行宏命令 System.out.println("\n=== 结束派对模式 ==="); remoteControl.offButtonWasPushed(4); // 执行关闭宏命令 remoteControl.undoButtonWasPushed(); // 撤销 // 8. 使用Java 8 Lambda表达式(函数式接口) System.out.println("\n=== 使用Lambda表达式 ==="); Light garageLight = new Light("车库"); // 使用Lambda表达式创建命令 remoteControl.setLambdaCommand(5, garageLight::on, // 方法引用 garageLight::off // 方法引用 ); remoteControl.onButtonWasPushed(5); remoteControl.offButtonWasPushed(5); // 9. 演示空命令模式 System.out.println("\n=== 测试空命令 ==="); remoteControl.onButtonWasPushed(6); // 未设置的插槽,使用空命令 remoteControl.undoButtonWasPushed(); // 没有可撤销的操作 } } -
扩展示例:使用命令队列
javapackage com.demo.command; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; /** * 命令队列:支持异步执行命令 */ public class CommandQueue { private BlockingQueue<Command> queue; private volatile boolean running = true; public CommandQueue() { queue = new LinkedBlockingQueue<>(); startWorker(); } private void startWorker() { Thread worker = new Thread(() -> { while (running) { try { Command command = queue.take(); System.out.println("[队列执行] 开始执行命令"); command.execute(); System.out.println("[队列执行] 命令执行完成"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); worker.setDaemon(true); worker.start(); } public void addCommand(Command command) { queue.offer(command); } public void shutdown() { running = false; } }
3. 构建和运行
-
使用Maven编译项目:
bashmvn clean compile -
运行主程序:
bashmvn exec:java -Dexec.mainClass="com.demo.command.Client" -
直接运行:
bashjava -cp target/classes com.demo.command.Client
4. 核心概念
-
命令模式将"请求"封装成对象,使得可以参数化其他对象,并支持请求排队、记录日志、撤销等操作。
-
主要角色:
-
Command(命令接口):定义执行操作的接口
-
ConcreteCommand(具体命令):实现命令接口,绑定接收者和动作
-
Receiver(接收者):知道如何执行具体的操作
-
Invoker(调用者):持有命令对象并触发命令执行
-
Client(客户端):创建具体命令并设置接收者
-
-
优点:
-
解耦调用者和接收者
-
支持撤销和重做
-
支持命令队列和日志
-
容易扩展新的命令
-