浅谈设计模式 – 命令模式(七)
前言:
命令模式也是一种比拟常见的行为型模式,能够设想咱们的手机智能遥控器,通过按动按钮的模式开启各种家具,说白了,就是将一系列的申请命令封装起来,不间接调用真正执行者的办法,这样比拟好扩大。须要留神的是命令模式和策略模式类似,所以有时候可能容易弄混,这篇文章将会具体介绍命令模式
文章目标:
- 理解命令的模式的特点
- 简略比照命令模式和策略模式
- 命令模式的优缺点总结
什么是命令模式?
解释:把“申请”封装为对应的对象,应用不同的申请参数化对象,命令模式反对撤销撤销的操作
命令模式是一种行为型模式,实现了接口调用对象和返回对象,用命令对象作为桥梁实现调用者和具体实现者之间的解耦和交互。
命令模式的特点:
- 将发出请求的对象和执行申请的对象解耦
- 调用者能够自在定义命令参数进行自在的组合
- 命令能够用来实现日志或者 事务零碎(undo 操作)
命令模式结构图:
上面依据命令模式的定义,以及下面对于命令模式的了解,构建具体的结构图
+ Client 客户端:客户端须要创立具体的命令类,并且通过发送申请给执行者调用具体的对象,发送方和接管方不存在关联,对立由命令对象进行连贯。
+ Invoker 执行者:申请的发送者,负责将申请分发给具体的命令实现类,由实现类调用理论的执行者进行执行操作
+ Command 接口:命令接口,定义命令的标准
+ ConcreteCommand 命令接口实现类:实现命令的同时组合具体对象。
+ ConcreteObject 具体实现类:定义截图的实现生产对象。
+ Receive 执行者 :申请的真正执行者,能够是任意对象,通常以 组合 模式呈现在执行者的外部
命令模式的了解
这里参考 《Head firtst 设计模式》 的案例,模仿具体的交互流程
对象村餐厅交互过程
咱们到餐厅点餐,个别会经验如下的流程
- 客人负责下订单,由服务员承受订单
- 服务器接管订单,调用订单柜台的下订单的办法,不须要关注细节
- 订单柜台告诉厨师进行生产
- 厨师生产订单物品之后,交给服务员上菜
依据下面的步骤利用伪代码的体现如下:
createCommandObject()
构建命令对象setCommand()
传递命令execute()
命令执行action1()
,action2()
执行者理论执行
交互流程图
咱们依据下面的交互过程介绍,构建具体的交互流程图,咱们能够看到外面有角色:客人
、 服务员
、 订单柜台
、 厨师
,他们自身并没有关联,而是通过餐厅的模式彼此产生了具体的关联,同时咱们比照下面的结构图,看下对象村餐厅对应的结构图:
上面依据结构图说一下各种角色的职责:
客人:相当于 client 客户端,负责指挥服务员进行下单的操作。
服务员:充当申请的发送者,承受客户端的申请,调用下订单的接口到具体的订单柜台,然而不须要关怀具体的细节,只具备下订单这一个操作
订单柜台:通过服务员传递的订单,安顿厨师执行具体的工作
厨师:依据订单柜台的订单做菜,将后果返回给服务员(或客人)
咱们从下面的角色图再来看具体的命令模式定义,能够看到根本都是一一对应的状况。
命令模式和策略模式的比照
命令模式和策略模式的结构图有些许的相似,上面咱们来比照看一下这两张图的异同:
策略模式结构图:
命令模式结构图:
相同点:
- 命令模式通过定义命令标准接口,由子类实现命令的执行细节,策略同样定义策略行为同时用子类实现不同的策略性能
- 命令模式和策略都解耦了申请的发送者和执行者
不同点:
- 命令模式利用了命令组合执行对象的模式执行实现具体实现,而策略模式依附上下文对象进行切换
- 策略模式针对某个对象实现不同的策略成果,而命令模式关注申请发送者和实现者之间的业务解耦组合
实战
模仿场景:
这次的案例还是模仿《Head First》设计模式的当中对于遥控器遥控电器的一个案例,咱们定义如下的内容:
遥控器:命令的发送方,负责依据不同的操作按钮调用不同的设施工作,生成具体的命令对象调用接口执行具体的命令
命令接口:负责定义命令的实现标准,充当遥控器外面的每一个按钮,对应都有具体的实现
命令实现类:负责实现命令的接口,同时调用具体的实现对象执行命令
实现对象:命令的真正执行者,个别夬在命令实现类的外部,比方电视,灯泡等
不实用设计模式
在不应用设计模式的状况下,咱们通常通过对象组合的模式组合不同的实体对象执行命令,上面通过一些简略的代码阐明一下设计的弊病:
// 灯泡
public class Light {public void on(){System.out.println("关上灯光");
}
public void off(){System.out.println("敞开灯光");
}
}
// 电视机
public class Television {public void on(){System.out.println("关上电视");
}
public void off(){System.out.println("敞开电视");
}
}
// 遥控器
public class RemoteControl {
private Light light;
private Television television;
public RemoteControl(Light light, Television television) {
this.light = light;
this.television = television;
}
public void button1(){light.on();
}
public void button2(){television.on();
}
}
// 单元测试
public class Main {public static void main(String[] args) {Television television = new Television();
Light light = new Light();
RemoteControl remoteControl = new RemoteControl(light, television);
remoteControl.button1();
remoteControl.button2();}
}/* 运行后果:关上灯光
关上电视
*/
从下面的简略代码能够看到,如果咱们持续减少电器,同时减少办法,不仅会导致遥控器要随着电器的改变一直改变,同时每次新增一个电器,遥控器要进行相似“注册”的行为,须要将电器 接入 到遥控器,这样显然是不合乎逻辑的,因为咱们都晓得,遥控器是单纯的指挥者,他不参加任何命令的操作细节,同时尽管真正工作的办法是具体对象的办法,然而这种模式相似将电器“塞”到了遥控器的外部执行,这样也是存在问题,咱们上面须要批改一下这种重大耦合的设计。
应用命令模式改写:
咱们依照命令模式的结构图,改写案例,咱们须要定义上面的类和对应的接口:
+ RemoteControl 遥控器
+ Command(接口) 命令标准接口,用于接入到遥控器外部
+ LightCommandConcrete 管制电器的亮灭命令实现
+ SwitchCommandConcrete 管制电器的开关命令实现
+ Light 灯泡
+ Television 电视机
首先,咱们定义命令的接口,定义接口的标准办法。而后定义实现子类实现不同命令的操作成果,在命令实现类的外部,咱们组合理论执行对象,在接口办法调用理论的对象办法,这样就做到了执行者和发送者之间的解耦。
接着,咱们改写控制器,他不在持有任何理论的对象办法,通过组合命令的接口,让客户端传入实现的性能,通过这种形式,遥控器不在须要依赖具体的电器实现调用具体方法,而是关注命令的接口办法,所有的细节都在命令的子类外部。
上面代码是按照命令模式进行的最简略的一个实现。
// 命令接口
public interface Command {
/**
* 接口备份
*/
void execute();}
public class LightCommandConcrete implements Command {private Light light = new Light();
@Override
public void execute() {light.on();
}
}
public class SwitchCommandConcrete implements Command{private Television television = new Television();
@Override
public void execute() {television.on();
}
}
// 遥控器
public class RemoteControl {
private Command command;
public RemoteControl(Command command) {this.command = command;}
public void execute(){command.execute();
}
public Command getCommand() {return command;}
public void setCommand(Command command) {this.command = command;}
}
public class Main {public static void main(String[] args) {RemoteControl remoteControl = new RemoteControl(new LightCommandConcrete());
remoteControl.execute();
remoteControl.setCommand(new SwitchCommandConcrete());
remoteControl.execute();}
}
通过下面的代码革新,咱们胜利下面的代码革新为命令模式的代码,应用设计模式之后,咱们将调用者和理论执行者进行理解耦,控制器不须要晓得执行的细节,只须要组合本人的命令接口,由客户端指定心愿实现的内容,执行绝对应的具体命令。
案例的额定扩大:
上面是对应案例如何进行后续的扩大,对于这部分内容文章篇幅无限,同时本着不反复造轮子的理念,请浏览 《Head First 设计模式》 对于命令模式这一个章节,同时安利一下这本书,十分通俗易懂的解说设计模式,对于集体的晋升帮忙很大。
对于下面的设计,如何退出 Undo 的操作?
Undo 是一个很常见的性能,如果想要让 Undo 的操作集成到案例外部,须要依照如下的步骤进行操作:
- Command 接口减少
Undo
的操作,让所有命令反对undo
- 在控制器记录 最初一个命令的执行对象,记录最初的操作命令,实现控制器反对 undo 操作
- 具体 Command 实现减少对于
undo()
办法调用,并且依据理论的组合对象调用办法 - 具体实现类实现
undo()
操作的具体行为成果。
如果 undo 外面,存在一些变量如何解决?
在命令的实现类外部,须要减少一个 最初变量值 的记录,用于记录以后最初一步操作的属性和变量
如何做到宏命令?
实现一个命令类,通过组合 数组 或者 堆栈 组合多个其余命令对象,通过 for 循环的模式顺次调用。
undo 也能够应用这种形式进行调用的,然而要留神 ** 调用的程序相同
命令模式的优缺点:
长处:
- 命令模式实现了申请发送方和实现方解耦,不论是发送方还是接管方都不须要
- 命令模式能够实现不同实现对象的自由组合,通过命令组合能够实现一连串简略性能
毛病:
- 和策略模式相似,命令模式很容易造成子类的收缩
总结:
命令模式是一种十分常见的设计模式,这种模式更多的关注点是解耦申请的发送方和实现方,命令模式在零碎设计中应用还是非常常见的,是一种值得关注的设计模式。