1.定义
将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,也支持对其进行撤销。
2.结构
2.1.命令模式中的角色
- 接收者(Receiver)
知道如何执行最终的动作,调用者最终通过命令对象中传入的接收者实例执行最终的动作。 - 命令对象(Command)
定义了接收者实例和需要执行的动作的对应关系。 - 调用者(Invoker)
调用者持有一个命令对象,通过执行命令对象对外暴露的方法,将请求付诸执行。
2.2.类图
通过使用命令对象将接收者和调用者解耦,命令对象将接受者与动作放进命令对象中,这个对象只对外暴露一个execute()方法,当方法被调用时候执行这些动作。而调用者并不知道具体由哪个接受者执行的动作,只知道执行execute()方法就能达到目的。(注:类图中接口的实现类到接口的线条应该是虚线,但此处是使用一个在线工具画的,在线作图工具中没有虚线,故此处为实线。)
当然接收者一般也有自己的接口,图中的接收者一般为一个接收者的具体实现。
3.代码示例
3.1.接收者代码
接收者接口:
/**
* 灯光接口(抽象接收者)
*/
public interface Light {
public void on();
public void off();
}
具体接收者:
/**
* 客厅灯光(具体接收者)
*/
public class LivingLight implements Light {
@Override
public void on() {
System.out.println("living light is on");
}
@Override
public void off() {
System.out.println("living light is off");
}
}
3.2.命令对象代码
命令接口:
/**
* 抽象命令接口
*/
public interface Command {
public void execute();
public 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();
}
}
3.3.调用者代码
/**
* 调用者
*/
public class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void action() {
command.execute();
}
public void undoAction() {
command.undo();
}
}
3.4.测试执行命令代码
public class InitMain {
public static void main(String[] args) {
/**
* 命令模式:
* 将请求封装成一个对象(即command对象),以便使不同的对象来参数化其他对象,来达到将调用者和执行者解耦的目的。
*
* 命令对象将接受者与动作放进命令对象中,这个对象只对外暴露一个execute()方法,当方法被调用时候执行这些动作。
* 而调用者并不知道具体由哪个接受者执行的动作,只知道执行execute()方法就能达到目的。
*/
//接收者
LivingLight livingLight = new LivingLight();
//命令对象
//开灯命令
LightOnCommand lightOnCommand = new LightOnCommand(livingLight);
//关灯命令
LightOffCommand lightOffCommand = new LightOffCommand(livingLight);
//调用者
Invoker invoker = new Invoker();
invoker.setCommand(lightOnCommand);
//执行动作
invoker.action();
//撤销动作
invoker.undoAction();
invoker.setCommand(lightOffCommand);
//执行动作
invoker.action();
//撤销动作
invoker.undoAction();
}
}
4.总结
命令模式将发出请求的对象和执行请求的对象解耦,在两者之间通过命令对象沟通,而命令对象封装接收者和一系列的动作。命令对象也可以通过添加undo()方法来实现对execute()方法的撤销动作。甚至可以通过写一个宏命令(宏命令是命令的一种延伸,允许调用多个命令)来执行一组命令。
优点:通过将一组动作或复杂的运算及一个接收者打包成一个命令对象,并将命令对象放在特定的应用中并在特定的时间执行来达到特殊的目的。如:线程池,队列中等。
缺点:每一个命令都会有一个命令子类,如果系统中命令过多,则会产生大量的命令子类。