命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
结构
命令模式的主要角色有:
- 命令(Command):为所有命令声明了一个接口。调用命令对象的 execute()方法,就可以让接收者进行相关的操作。这个接口也具备一个 undo() 方法。
- 具体命令(ConcreteCommand):实现命令接口,定义了动作和接收者之间的绑定关系。调用者只要调用 execute() 就可以发出请求,然后由 ConcreteCommand 调用接收者的一个或多个动作。
- 接收者(Receiver):接收者知道如何进行必要的动作,实现这个请求。任何类都可以当接收者。
- 请求者(Invoker):持有一个命令对象,有一个行动方法,在某个时间点调用命令对象的 execute() 方法,将请求付诸实行。
优缺点
优点
- 降低了系统耦合度
- 新的命令可以很容易添加到系统中去
缺点
- 使用命令模式可能会导致某些系统有过多的具体命令类。
代码实现
接收者
public class Light {
public void on() {
System.out.println("灯亮了...");
}
public void off() {
System.out.println("灯暗了...");
}
}
命令
public interface Command {
// 执行命令
void execute();
// 撤销命令
void undo();
}
具体命令
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 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();
}
}
请求者
前面,我们定义了动作的接收方和联系中介 —— 命名对象。现在,我们要着手构建请求者角色了。请求者持有一个命令对象,有一个行动方法。它会在某个时间点执行行动方法,但不关心是谁具体执行了这个动作。
public class RemoteInvoker {
/**
* 开关命令数组,模拟有很多对开关数组
*/
private Command[] onCommands;
private Command[] offCommands;
/**
* 撤销(回退)命令
*/
private Command undoCommand;
public RemoteInvoker(int length) {
// 有几组开关,就设置多少数组
onCommands = new Command[length];
offCommands = new Command[length];
// 把每个命令初始化成空命令,避免空指针异常
Command noCommand = new NoCommand();
undoCommand = noCommand;
for (int i = 0; i < length; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
/**
* @param slot 遥控器的位置
* @param onCommand 开的命令
* @param offCommand 关的命令
* @return void
*/
public void setCommond(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButton(int slot) {
onCommands[slot].execute();
//为撤销(回退)按钮记录动作
undoCommand = onCommands[slot];
}
public void offButton(int slot) {
offCommands[slot].execute();
//为撤销(回退)按钮记录动作
undoCommand = offCommands[slot];
}
public void undoButton() {
undoCommand.undo();
}
}
测试
前面,我们定义好了请求者、接收者已经两者之间的联系中介 —— 命令对象。但是这几个角色对象之间都是松耦合的,还没有一个具体动作的流程,现在我们利用客户端角色把整个动作流程串联在一起。
public class RemoteClient {
public static void main(String[] args) {
// 1、创建接收者
Light light = new Light();
// 2、创建命令对象
LightOnCommand lightOnCommand = new LightOnCommand(light);
LightOffCommand lightOffCommand = new LightOffCommand(light);
// 3、创建一组开关并用命令对象装载它
RemoteInvoker invoker = new RemoteInvoker(1);
invoker.setCommond(0, lightOnCommand, lightOffCommand);
// 4、测试
invoker.onButton(0);
invoker.offButton(0);
invoker.undoButton();
}
}