设计模式—–命令模式
个人博客,想要搭建个人博客的可以进来看看: http://www.ioqian.top/
命令模式 , 将请求封装成对象,以便使用不同的请求,队列或日志来参数化其他对象,命令模式也支持可撤销的操作
设计模式系列源码: https://github.com/liloqian/DesiginModeDemo
背景
现在有一个遥控器,上面有1个按钮,我们要根据不同的请求来控制不同的电器,比如说可以控制风扇,点灯等,这里我们就可以引入命令模式
在面向对象的程序设计中,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;设置调用参数;调用目标对象的方法。
但在有些情况下有必要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。
1.下面先大概看一下UML
- Command ,是所有命令的一个接口,调用Command中的execute()方法就可以让接受者进行相关的动作
- Receiver , 动作的最终执行者 ,比如我们背景中的风扇 , 电灯,打开风扇,打开电灯等动作最终只可能由电灯风扇自身实现
- CreateCommand ,持有Receiver的引用,继承了Command接口,在Command接口的execute()方法中直接调用Receiver的具体动作
- Invoker , 调用者,持有一个命令接口,当客户调用时直接调用命令接口的execute()方法
2.下面来看具体的代码
Receiver,这里实现了2个命令接收者,分别是风扇和电灯
/**Receiver 具体的命令接收者,接受者指导如何进行必要的工作,也是真正执行命令的方法*/
public class Light {
//实现了这个方法都可以成为命令接收者,这个方法是真正执行命令的方法,对应上图中的action()方法
public void on(){
System.out.println(this.toString()+": turn on the light");
}
@Override
public String toString() {
return "I-am-light";
}
}
/**另外一个接受者*/
public class Fan {
public void on(){
System.out.println(this.toString()+": turn on the fan");
}
@Override
public String toString() {
return "I-am-fan";
}
}
Command接口
/**命令接口,所以的遥控器控制的电器命令都要实现这个接口*/
public interface Command {
public void execute();
}
CreateCommand,实现了Command接口的具体命令,持有接受者Receiver的引用
/**CreateCommand*/
public class LightCommand implements Command {
//持有一份接受者Receiver中Light的引用
Light light ;
public LightCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
/**和上面的一样*/
public class FanCommand implements Command {
Fan fan;
public FanCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.on();
}
}
Invoker
/**Invoker 持有一个命令对象,当点击按钮时就执行命令对象的方法*/
public class SimpleRemoteControl {
//命令对象
Command command;
//点击按钮后调用此方法去调用命令对象实现的方法
public void buttonPressed(){
command.execute();
}
public void setCommand(Command command) {
this.command = command;
}
}
Client ,直接在Main测试中进行
public class Main {
public static void main(String[] args) {
//实例化一个命令调用者
SimpleRemoteControl remote = new SimpleRemoteControl();
//电灯命令
LightCommand lightOn = new LightCommand(new Light());
//设置电灯命令,点击按钮去执明LightCommand的方法
remote.setCommand(lightOn);
remote.buttonPressed();
//下面的和上面的一样
FanCommand fanOn = new FanCommand(new Fan());
remote.setCommand(fanOn);
remote.buttonPressed();
}
}
//结果 , 我们通过传入不同的命令参数可以执行不同的请求
I-am-light: turn on the light
I-am-fan: turn on the fan
Process finished with exit code 0
上面的模型中也可以不必一定要有Receiver,可以把具体的动作放在ConcreteCommand中
3.命令模式的用途,队列请求
新建一个专门处理请求对象的线程,从队列获取请求对象(就是请求封装的对象),进行处理;在其他线程中把请求封装成对象加入这个队列
4.命令模式的要点
- 命令模式将要发出请求的对象和执行请求的对象解耦,执行请求的对象不关心请求的具体内容,只要把具体的请求封装成请求的对象就可以了
- 被解耦的两者之间是通过命令对象进行沟通的,命令对象封装了接收者和一个或一组动作
- 调用者通过调用命令对象的execute()方法使得接受者的动作被执行
- 命令可以被撤销,做法是实现一个undo()方法来回到execute()之前的状态,undo()和execute()的具体实现一样
- 实际中,使用聪明命令对象,而不是把工作给了接受者,就是前面说的,不必一定要有Receiver,可以把具体的动作放在ConcreteCommand中