1、命令模式概要
定义: 对命令进行封装,将发出命令的责任和执行命令的责任分割开
你可以想象一个场景,你是一名在餐馆吃饭的顾客,你在菜单上写上想吃的菜,呼叫服务员帮你送给厨师
当然你完全可以自己跑去和厨师说,我要吃什么菜,但是你所坐的地方和厨房还是有一定距离的,你的每一次加菜都必须自己到厨房去,在这里你和厨师的关系是高度耦合的,因为是你和厨师说的话,厨师必须要记得你。厨师又要做菜又要把人和菜对应上,这样就会和麻烦,所以餐厅招聘了服务员,你只需要把你的需求告诉服务员,让他来帮你跑腿。这样厨师只需要埋头做菜就行了。
服务员不需要知道你需要吃什么菜,他的责任就是把菜单交给厨师,这样你和厨师之间的关系就被解耦了。
在代码中的表现就为,厨师类中不会出现有关顾客的代码,顾客类中不会出现有关厨师的代码
优点:命令模式可以很轻易的组合一些指令,而且很轻易的扩展,并且降低对象和对象之间的耦合度
缺点:会编写一些无关代码(比如上面的服务员,本质上是不需要的,但是为了代码易维护,降低耦合所添加的)
2、命令模式
Command:
定义命令的接口,声明执行的方法。
ConcreteCommand:
命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
Receiver:
接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
Invoker:
要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
Client:
创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。
3、代码举例
你现在需要编写一个遥控器上面有多个开关接口,来控制家里智能家电的开关,比如家里的电灯,电视
注意:你的遥控器应该需要有撤回指令
1)首先我们设计命令接口(Command)
public interface Command {
void Execute();
void Undo();
}
最基础的命令接口
2)设计具体类Receiver
public class Light {
private String name;
public Light(String name) {
this.name = name;
}
void lightOn(){
System.out.println(name + " Light is on");
}
void lightOff(){
System.out.println(name + " Light is off");
}
}
Light类里面有两个方法,分别是开和关
public class Tv {
private String name;
public void setName(String name) {
this.name = name;
}
public void tvOn(){
System.out.println(name + " Tv is on");
}
public void tvOff(){
System.out.println(name + " Tv is off");
}
}
Tv类同理
3)编写具体命令类(Concrete Command)
public class LightCommand implements Command {
private Light light;
public LightCommand(Light light) {
this.light = light;
}
@Override
public void Execute() {
light.lightOn();
}
@Override
public void Undo() { //撤销操作,我们不可能关了灯再点撤销,这个操作是不合理的
light.lightOff();
}
}
Light类会统一执行开灯操作,撤销则为关灯
public class TvCommand implements Command {
private Tv tv;
public TvCommand(Tv tv) {
this.tv = tv;
}
@Override
public void Execute() {
tv.tvOn();
}
@Override
public void Undo() {
tv.tvOff();
}
}
4)命令持有类(Invoker)
public class Remoter {
private Command[] onCommand;
private Command[] offCommand;
private Command undoCommand;
public Remoter() {
onCommand = new Command[2];
offCommand = new Command[2];
noCommand noCommand = new noCommand();
for (int i = 0; i < onCommand.length; i++) {
onCommand[i] = noCommand;
offCommand[i] = noCommand;
}
undoCommand = noCommand;
}
public void setCommand(int slot, Command command) {
onCommand[slot] = command;
offCommand[slot] = command;
}
public void buttonPress(int slot){
onCommand[slot].Execute();
undoCommand = onCommand[slot];
}
public void Undo(){
undoCommand.Undo();
}
}
noCommand是空对象,设置空对象将处理null的责任转移给空对象
通过数组来确定遥控器上的按键位置
4、测试代码
public class Main {
public static void main(String[] args) {
//初始化命令变量
Light Kitchen = new Light("Kitchen");
Tv LivingRoom = new Tv("Living Room");
LightCommand lightCommand = new LightCommand(Kitchen);
TvCommand tvCommand = new TvCommand(LivingRoom);
//把命令加入遥控器
Remoter remoter = new Remoter();
remoter.setCommand(0,lightCommand);
remoter.setCommand(1,tvCommand);
// Light is on
remoter.buttonPress(0);
// Light is off
remoter.Undo();
// Tv is on
remoter.buttonPress(1);
// Tv is off
remoter.Undo();
}
}
命令行输出,这样我们就完成了命令模式最基本的使用