Java案例中的命令模式怎么用?——从原理到实战的完整指南
📑 目录导读
- 命令模式的核心概念与适用场景
- Java实现命令模式的四大核心角色
- 经典案例:智能家居遥控系统(附完整代码)
- 命令模式的高级技巧:撤销、队列与宏命令
- 常见面试问答(Q&A)
- 何时选择命令模式?
命令模式的核心概念与适用场景
命令模式(Command Pattern)属于行为型设计模式,它将请求封装为独立的对象,从而允许用户用不同的请求对客户端进行参数化、对请求排队或记录请求日志,以及支持可撤销的操作。

核心思想:将“调用操作的对象”与“知道如何执行操作的对象”解耦,在Java开发中,当遇到以下场景时,命令模式尤为适用:
- 需要将请求发送者和接收者解耦(如GUI按钮与业务逻辑分离)
- 支持操作的撤销/重做(如文本编辑器的Ctrl+Z)
- 需要实现请求的排队、日志记录或异步执行(如任务调度系统)
- 需要组合多个操作为宏命令(如智能家居的场景模式)
Java实现命令模式的四大核心角色
在Java中实现命令模式,需要清晰理解以下四个角色:
| 角色 | 作用 | 类比 |
|---|---|---|
| Command(抽象命令) | 声明执行操作的接口 | 遥控器上的“按钮” |
| ConcreteCommand(具体命令) | 绑定Receiver与动作 | 按下“开灯”按钮的实际电路 |
| Invoker(调用者/请求者) | 持有命令对象并触发执行 | 遥控器本体 |
| Receiver(接收者) | 真正执行具体业务逻辑 | 灯泡、空调等设备 |
关键区别:与传统直接调用的不同在于——调用者(Invoker)不知道接收者(Receiver)是谁,它只知道调用 command.execute() 方法。
经典案例:智能家居遥控系统(完整Java代码)
场景描述
我们需要为一套智能家居设计遥控器,支持开灯、关灯、打开空调、关闭空调,并且要求未来能轻松添加新设备(如电视、窗帘)而不修改现有代码。
定义Receiver(设备类)
// 灯
public class Light {
public void on() { System.out.println("灯已打开"); }
public void off() { System.out.println("灯已关闭"); }
}
// 空调
public class AirConditioner {
public void start() { System.out.println("空调启动"); }
public void stop() { System.out.println("空调关闭"); }
}
定义Command接口
public interface Command {
void execute();
// 可以增加undo()方法支持撤销
void undo();
}
实现ConcreteCommand(具体命令)
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();
}
}
public class AirConditionerStartCommand implements Command {
private AirConditioner ac;
public AirConditionerStartCommand(AirConditioner ac) {
this.ac = ac;
}
@Override
public void execute() {
ac.start();
}
@Override
public void undo() {
ac.stop();
}
}
实现Invoker(遥控器)
public class RemoteControl {
private Command[] onCommands;
private Command[] offCommands;
private Command lastCommand; // 用于撤销
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
// 初始化空命令(避免判空)
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
lastCommand = noCommand;
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
// 按下“开”按钮
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
lastCommand = onCommands[slot];
}
// 按下“关”按钮
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
lastCommand = offCommands[slot];
}
// 撤销操作
public void undoButtonWasPushed() {
lastCommand.undo();
}
}
客户端使用
public class Client {
public static void main(String[] args) {
// 创建接收者
Light livingRoomLight = new Light();
AirConditioner bedroomAC = new AirConditioner();
// 创建命令
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
ACStartCommand bedroomACStart = new ACStartCommand(bedroomAC);
ACStopCommand bedroomACStop = new ACStopCommand(bedroomAC);
// 设置遥控器
RemoteControl remote = new RemoteControl();
remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remote.setCommand(1, bedroomACStart, bedroomACStop);
// 执行操作
remote.onButtonWasPushed(0); // 开灯
remote.onButtonWasPushed(1); // 启动空调
remote.offButtonWasPushed(0); // 关灯
remote.undoButtonWasPushed(); // 撤销关灯(灯重新打开)
}
}
命令模式的高级技巧
1 支持撤销(Undo)操作
关键在于每个ConcreteCommand保存执行前的状态,并在undo()中恢复,对于状态复杂的操作,可以使用备忘录模式配合。
2 实现命令队列
使用Queue<Command>存放命令,由异步线程池逐条执行,实现批处理或任务调度:
Queue<Command> commandQueue = new LinkedList<>();
commandQueue.add(new LightOnCommand(livingRoomLight));
commandQueue.add(new ACStartCommand(bedroomAC));
while (!commandQueue.isEmpty()) {
commandQueue.poll().execute();
}
3 宏命令(复合命令)
组合多个命令为一个宏命令:
public class MacroCommand implements Command {
private Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
for (Command cmd : commands) {
cmd.execute();
}
}
@Override
public void undo() {
for (int i = commands.length - 1; i >= 0; i--) {
commands[i].undo();
}
}
}
常见面试问答(Q&A)
Q1:命令模式与策略模式的区别是什么?
A:两者都涉及接口和实现分离,但意图不同:策略模式用于封装算法族(如不同的加密算法),强调“怎么做”;命令模式用于封装请求(如按钮点击),强调“做什么”,且命令模式通常包含Receiver对象,而策略模式不强制。
Q2:命令模式如何处理线程安全问题?
A:如果多个线程共享Invoker,需确保Command对象是无状态的,或使用同步块,更常见的做法是为每个线程分配独立的Invoker实例(如每个HTTP请求对应的Servlet)。
Q3:Java中是否有现成的命令模式实现?
A:java.lang.Runnable 可视为命令模式的变体(Runnable充当Command,execute为run())。javax.swing.Action 及 UndoManager 也是官方实现例子。
Q4:命令模式可以替代Switch语句吗?
A:完全可以!将每个case分支封装为具体命令对象,通过Map<条件, Command>将条件与命令映射,消除复杂的switch,符合开闭原则。
何时选择命令模式?
| 适合场景 | 不适合场景 |
|---|---|
| 需要将调用者和实现者解耦 | 只有简单的直接调用(过度设计) |
| 需要记录、撤销、重做操作 | 对响应时间要求极高(增加对象开销) |
| 需要组合操作为宏命令 | 命令对象本身非常复杂且状态多变 |
| 需要排队执行或异步调用 | 项目规模很小且不会扩展 |
最佳实践建议:在业务中,优先考虑是否真的需要“撤销”功能;如果仅是简单的请求封装,使用Java 8的Function或Runnable即可完成任务,无需引入命令模式增加复杂度。
本文基于实际Java项目案例编写,参考了《Head First设计模式》与多个开源项目代码逻辑,遵循SEO优化原则,确保内容原创性与实用性,任何转载请联系作者。