概述
命令模式
: 一种行为型模式,将命令的发出
与命令的执行
进行解耦。
从而使代码达到灵活的状态。
角色
Command 命令接口
: 命令的接口,通常有一个execute()
方法,表示执行操作。
ConcreteCommand 命令实现类
: 命令接口的具体实现类。
- 有一个
Receiver
对象的引用,执行具体的命令对应的逻辑。
Invoker 请求者
: 发出命令的请求者。
- 有一个
Command
对象的引用
Receiver 接收者
: 命令的具体执行者,具体的逻辑。
举个例子 :用遥控器开关灯的场景。
开灯/关灯
: 这是一个具体的命令;灯
: 命令的接收者,具体执行 开灯动作 or 关灯动作;遥控器
:命令的请求者,发出具体的 开灯命令 or 关灯命令。
类图设计
代码实现
接收者-灯
public class Light {
public void on() {
System.out.println("Light is on");
}
public void off() {
System.out.println("Light is off");
}
}
命令接口 & 命令实现
public interface ICommand {
void execute();
void undo();
}
/**
* 开灯具体命令
*/
public class LightOnCommand implements ICommand{
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
System.out.println("开灯命令 execute begin ");
light.on();
System.out.println("开灯命令 execute end ");
}
@Override
public void undo() {
System.out.println("开灯命令 undo begin ");
light.off();
System.out.println("开灯命令 undo end ");
}
}
/**
* 关灯的具体命令
*/
public class LightOffCommand implements ICommand{
private Light light;
public LightOffCommand(Light light){
this.light = light;
}
@Override
public void execute() {
System.out.println("关灯命令 execute begin ");
light.off();
System.out.println("关灯命令 execute end ");
}
@Override
public void undo() {
System.out.println("关灯命令 undo begin ");
light.on();
System.out.println("关灯命令 undo end ");
}
}
调用者-遥控器
public class RemoteControl {
/**
* 命令列表 : 可以存放多个命令
*/
private Map<String,ICommand> commandMap;
public RemoteControl(Map<String,ICommand> commandMap)
{
this.commandMap = commandMap;
}
// 设置命令
public void setCommand(String name,ICommand command)
{
commandMap.put(name, command);
}
// 执行命令
public void execute(String name)
{
ICommand command = commandMap.get(name);
command.execute();
}
// 撤销命令
public void undo(String name)
{
ICommand command = commandMap.get(name);
command.undo();
}
}
测试使用
public class CommandPatternTest {
public static void main(String[] args) {
// 创建灯对象
Light light = new Light();
// 创建开灯命令对象
ICommand lightOnCommand = new LightOnCommand(light);
// 创建关灯命令对象
ICommand lightOffCommand = new LightOffCommand(light);
// 创建遥控器对象
RemoteControl remoteControl = new RemoteControl(new HashMap<>());
remoteControl.setCommand("lightOn", lightOnCommand);
remoteControl.setCommand("lightOff", lightOffCommand);
// 遥控器执行开灯命令
remoteControl.execute("lightOn");
System.out.println("--------------------------------------------------");
// 遥控器执行撤销动作
remoteControl.undo("lightOn");
System.out.println("--------------------------------------------------");
// 遥控器执行关灯命令
remoteControl.execute("lightOff");
System.out.println("--------------------------------------------------");
// 遥控器执行撤销动作
remoteControl.undo("lightOff");
}
}
运行结果:每个命令都支持 执行 与 撤销
开灯命令 execute begin
Light is on
开灯命令 execute end
--------------------------------------------------
开灯命令 undo begin
Light is off
开灯命令 undo end
--------------------------------------------------
关灯命令 execute begin
Light is off
关灯命令 execute end
--------------------------------------------------
关灯命令 undo begin
Light is on
关灯命令 undo end
优点
解耦 (Decoupling)
: 这是最主要的优点。调用者(Invoker)和接收者(Receiver)之间完全解耦。调用者不需要知道请求的具体细节,只需要知道命令接口。这使得系统组件可以独立变化和复用。
可扩展性 (Extensibility)
: 添加新的命令非常容易,只需要实现 Command 接口并创建一个新的 ConcreteCommand 类,而无需修改现有的 Invoker 或 Client 代码(符合开闭原则)。
支持撤销/重做 (Undo/Redo)
: 通过在 Command 接口中定义 undo() 方法,并在 ConcreteCommand 中实现相应的撤销逻辑,可以轻松地实现撤销和重做功能。Invoker 可以维护一个命令历史列表。
支持宏命令 (Macro Command)
: 可以创建一个 MacroCommand 类,它本身也是一个 Command,但内部包含一个 Command 对象列表。执行 MacroCommand 的 execute() 会遍历并执行其内部的所有命令。撤销操作则按逆序执行每个命令的 undo()。
支持请求队列 (Request Queuing)
: 可以将命令对象放入队列中,在不同的线程中异步执行,或者延迟执行。因为命令对象包含了所有执行所需的信息。
日志请求 (Logging Requests)
: 命令对象可以很容易地被序列化,从而可以将请求记录到日志文件中,用于系统崩溃后的恢复(事务型命令)。
缺点
类膨胀 (Class Proliferation)
: 每个不同的请求都需要一个具体的 ConcreteCommand 类,这可能会导致系统中类的数量急剧增加。
增加复杂性 (Increased Complexity)
: 对于简单的操作,引入命令模式可能显得过于复杂和笨重。
适用场景(了解)
【实现撤销/重做功能】: 如文本编辑器、图形编辑器。
【实现宏命令】: 用户可以录制一系列操作,然后一次性执行。
【构建基于命令的系统】: 如任务调度系统、工作流引擎。
【请求需要排队、记录日志或远程执行】: 命令对象可以轻松地在网络上传输、存储或放入队列。
【参数化对象】: 需要将操作参数化,例如 GUI 中的按钮、菜单项,它们的行为由关联的命令决定。
【高阶操作】: 需要在不同的时间点执行请求,或者需要将操作对象作为参数传递。