优缺点
优点 1.Invoker(调用者)的存在使命令执行之前有了更多的设计空间(日志记录、命令回滚、拒绝执行命令、命令队列等) 2.对命令进行封装,使命令易于扩展和修改 3.命令发出者和接受者解耦,使发出者不需要知道命令的具体执行过程即可执行 缺点 1、使用命令模式可能会导致某些系统有过多的具体命令类。使用场景
认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。文章一什么是命令模式图解
参考地址
1.假设现在有个请求处理,正常操作客户端直接调用请求处理的类 类似解释中没有遥控器的电视 1.引入命令模式后客户端的请求发送类和请求处理类之间增加一个Invoker类,再将请求发送类发送 的所有请求封装成对象,然后让Invoker(调用者)类去管理这些请求对象,并决定这些请求是否允许 执行、何时执行、按什么顺序执行。 2.好处:由于在请求发送类和请求处理类之间增加了请求转发者,因此这两个类之间的藕合度就大大降低。 3.分析图:'Infvoker (调用者) 遥控器','Command(命令)开机/关机','Receiver(接受者)就是电视类里面有开关机方法' 命令模式的类图对命令模式的划分
命令模式
文章中的类图文章中的说明 Client : 即请求发出者,通过调用Invoker方法发出请求。 Invoker : 请求的调用者,内部持有具体请求的引用。 ConcreteCommand :封装的请求对象,内部持有Receiver对象。 Receiver:请求接受者,根据请求对象的指挥进行不同的反应。根据上面分析实现代码
1.首先需要一个'Receiver' 请求的接受者,简单的理解这里面有上面所有按钮的'具体行为动作' 2.需要一个'ConcreteCommand ' 是每一个单独指令,这些单独指令对象中都有'Receiver'对象, 他们都会有一个共同的方法(在java 或者是ts中甚至es6我们会吧这个共同的方法定义在一个接口中) 3.需要一个'Invoker'请求的调用者,他将会具体调用某个指令对象(有点像代理模式,但区别是他的 调用方法不用像代理模式那样需要和被代理对象实现名字一样的方法) 代码实现 1.下面代码中的'Receiver' -- MenuBar 和 SubMenu 他们有所有按钮的具体行为动作 2.'ConcreteCommand ' -- 'RefreshMenuBarCommand','AddSubMenuCommand ','DelSubMenuCommand ' 这些对象是将'Receiver' 中的每个具体行为拆分开来,他们都有一个特定,内部都有'Receiver' 也就是请求接受者的对象,有一个共同的方法'execute' 来调用这些'Receiver'中与之匹配的方法 3.'Invoker' -- 'setCommand' 也就是请求调用者,他们会接受'ConcreteCommand' 对象并且 调用他们 4.命令都有同一接口实现为什么这么做看'execute'方法 <html> <head> <meta charset="utf-8"> </head> <body> <button id="button1">点击按钮1</button> <button id="button2">点击按钮2</button> <button id="button3">点击按钮3</button> </body> <script> // 1.菜单这些执行方法拆出来的命令,封装成对象会有个参数(要请求的对象--电视) // 2.要控制 遥控器(调用者)(调用那个对象,那个方法) // 3.拆除命令的对象(请求者)也就是电视机 var button1 = document.getElementById('button1') var button2 = document.getElementById('button2') var button3 = document.getElementById('button3') // 遥控器 var setCommand = function (button, command) { button.onclick = function () { command.execute() } } // 请求者 var MenuBar = { refresh:function () { console.log('刷新菜单目录') } } var SubMenu = { add:function () { console.log('增加子菜单') }, del:function () { console.log('删除子菜单') } } // ---------------命令(命令的参数是要执行请求者)------------------- // 刷新菜单 var RefreshMenuBarCommand = function (receiver) { this.receiver = receiver } RefreshMenuBarCommand.prototype.execute = function () { this.receiver.refresh() } // 针对子菜单 var AddSubMenuCommand = function (receiver) { this.receiver = receiver } AddSubMenuCommand.prototype.execute = function () { this.receiver.add() } var DelSubMenuCommand = function (receiver) { this.receiver = receiver } DelSubMenuCommand.prototype.execute = function () { console.log('删除子菜单') } // 指定命令的请求者 var refreshMenuBarCommand = new RefreshMenuBarCommand( MenuBar ); var addSubMenuCommand = new AddSubMenuCommand( SubMenu ); var delSubMenuCommand = new DelSubMenuCommand( SubMenu ); // 执行 setCommand( button1, refreshMenuBarCommand ); setCommand( button2, addSubMenuCommand ); setCommand( button3, delSubMenuCommand ); </script> </html> 结果分析 1.现在画按钮的A同学画完了按钮,写完了'Invoker' -- 'setCommand' 也就是请求调用者这个方法, 他不用知道这些按钮具体方法实现,也不用知道具体代码实现 2.B同学写完了方法,也将指令的类封装好了,现在他要做的就是调用一下,A同学封装的'Invoker'总结
1.书中举了一个订餐的例子,我们现在来看这个例子,结合代码分析这个理解,一个人去点餐,服务员 是一个调用者,点餐的不用知道那个厨师给我做的饭,只需要告诉调用者,调用者将这个指令告诉 对应的厨师即可