命令模式也是一种比较常见的行为型模式,能够想象咱们的手机智能遥控器,经过按动按钮的形式开启各类家具,说白了,就是将一系列的请求命令封装起来,不直接调用真正执行者的方法,这样比较好扩展。须要注意的是命令模式和策略模式类似,因此有时候可能容易弄混,这篇文章将会详细介绍命令模式java
解释:把“请求”封装为对应的对象,使用不一样的请求参数化对象,命令模式支持撤销撤销的操做设计模式
命令模式是一种行为型模式,实现了接口调用对象和返回对象,用命令对象做为桥梁实现调用者和具体实现者之间的解耦和交互。数组
下面根据命令模式的定义,以及上面对于命令模式的理解,构建具体的结构图服务器
+ Client 客户端:客户端须要建立具体的命令类,而且经过发送请求给执行者调用具体的对象,发送方和接收方不存在关联,统一由命令对象进行链接。ide
+ Invoker 执行者:请求的发送者,负责将请求分发给具体的命令实现类,由实现类调用实际的执行者进行执行操做单元测试
+ Command 接口:命令接口,定义命令的规范测试
+ ConcreteCommand 命令接口实现类:实现命令的同时组合具体对象。this
+ ConcreteObject 具体实现类:定义截图的实现生产对象。spa
+ 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
的操做,让全部命令支持undo
undo()
方法调用,而且根据实际的组合对象调用方法undo()
操做的具体行为效果。在命令的实现类内部,须要增长一个最后变量值的记录,用于记录当前最后一步操做的属性和变量
实现一个命令类,经过组合数组或者堆栈组合多个其余命令对象,经过for循环的形式依次调用。
undo也可使用这种方式进行调用的,可是要注意**调用的顺序相反
优势:
缺点:
命令模式是一种很是常见的设计模式,这种模式更多的关注点是解耦请求的发送方和实现方,命令模式在系统设计中使用仍是十分常见的,是一种值得关注的设计模式。