命令模式 (Command)
想要一个对象执行某个任务?将任务封装成命令,传递给接收者,这样任务就能灵活排队、撤销,甚至日志记录。
在日常生活中,我们常常会遇到需要委托别人执行某项任务的场景。比如,老板把一个任务委托给下属,下属会根据任务内容去执行。不同的是,在命令模式中,我们将任务封装为一个对象,并将这个对象传递给接收者,接收者根据命令执行相应操作。这种方式有很多好处,比如:你可以灵活地排队执行任务、撤销操作,甚至把执行的日志都记录下来。
趣味解读:遥控器指挥家!
想象一下,你坐在沙发上,手里拿着一个遥控器,而电视、音响、灯光等家电都被你远程操控。遥控器上的每个按钮都对应着一个任务,比如打开电视、调高音量、调整灯光亮度等。每按一个按钮,遥控器就会发送一个指令给相应的设备。设备接收到命令后,执行相应的操作。
这就是命令模式:遥控器相当于命令对象,家电设备就是命令的接收者。你只需要通过遥控器发出命令,而不需要关心家电内部是如何实现的。命令可以排队执行、撤销或者记录日志,极大地方便了操作和管理。
Java代码案例:命令模式 - 电视遥控器
假设我们正在设计一个简单的电视遥控器系统,用户可以通过遥控器开关电视、调节音量等。每个操作被封装为一个命令对象,遥控器通过调用这些命令来执行任务。
// 1. 命令接口
interface Command {
void execute(); // 执行命令
void undo(); // 撤销命令
}
// 2. 具体命令 - 开电视
class TurnOnTVCommand implements Command {
private TV tv;
public TurnOnTVCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.turnOn();
}
@Override
public void undo() {
tv.turnOff();
}
}
// 3. 具体命令 - 关电视
class TurnOffTVCommand implements Command {
private TV tv;
public TurnOffTVCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.turnOff();
}
@Override
public void undo() {
tv.turnOn();
}
}
// 4. 具体命令 - 调节音量
class SetVolumeCommand implements Command {
private TV tv;
private int volume;
public SetVolumeCommand(TV tv, int volume) {
this.tv = tv;
this.volume = volume;
}
@Override
public void execute() {
tv.setVolume(volume);
}
@Override
public void undo() {
tv.setVolume(10); // 恢复音量为默认值
}
}
// 5. 接收者 - TV(电视机)
class TV {
public void turnOn() {
System.out.println("电视机打开了");
}
public void turnOff() {
System.out.println("电视机关了");
}
public void setVolume(int volume) {
System.out.println("电视音量设置为: " + volume);
}
}
// 6. 请求者 - 遥控器
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
public void pressUndo() {
command.undo();
}
}
// 7. 客户端代码
public class Main {
public static void main(String[] args) {
TV tv = new TV();
Command turnOnTV = new TurnOnTVCommand(tv);
Command turnOffTV = new TurnOffTVCommand(tv);
Command setVolume = new SetVolumeCommand(tv, 20);
RemoteControl remote = new RemoteControl();
// 开电视
remote.setCommand(turnOnTV);
remote.pressButton();
// 调节音量
remote.setCommand(setVolume);
remote.pressButton();
// 关电视
remote.setCommand(turnOffTV);
remote.pressButton();
// 撤销上一步操作
remote.pressUndo();
// 撤销上一步操作
remote.pressUndo();
}
}
解析:
命令接口:
Command
定义了两个方法:execute()
用来执行命令,undo()
用来撤销命令。具体命令:每个具体的命令类(如
TurnOnTVCommand
、TurnOffTVCommand
、SetVolumeCommand
)都实现了Command
接口,并在execute()
中执行相应操作,在undo()
中执行撤销操作。接收者:
TV
类代表电视机,它有控制开关、调节音量等功能。请求者:
RemoteControl
类代表遥控器,它持有一个命令对象并触发命令的执行。
运行结果:
电视机打开了
电视音量设置为: 20
电视机关了
电视机打开了
电视机关了
实际应用场景:
GUI操作:在图形用户界面应用中,用户的每一个操作(比如点击按钮、选择菜单项)都可以视为一个命令。通过命令模式,我们可以把这些操作封装为对象,从而实现任务的排队、撤销或日志记录等功能。
撤销与重做:命令模式非常适合实现撤销和重做功能。每个操作都被封装成一个命令对象,执行撤销时,命令对象只需调用
undo()
方法即可撤销操作。日志记录与事务管理:命令模式可以帮助我们记录操作日志,将每个命令的执行都记录下来。系统可以根据日志回放或者重新执行操作,便于事务管理。
宏命令:通过将多个命令对象组合成一个复合命令(宏命令),我们可以批量执行多个操作。例如,在自动化测试中,我们可以将多个测试步骤封装为一个命令对象,依次执行每个步骤。
总结:
命令模式通过将请求封装成命令对象,使得请求的发送者与接收者解耦。命令对象不仅可以灵活地排队执行,还可以支持撤销和重做等功能。在复杂的系统中,命令模式帮助我们更好地管理请求,提高了代码的灵活性和可扩展性。