被说了很多遍的设计模式---命令模式
2017-02-26 19:34
393 查看
[把你的理性思维慢慢变成条件反射]
本文,我们讲介绍命令模式,文章主题结构与上文一致。惯例,先来看看我们示例工程的环境:
操作系统:win7
x64
其他软件:eclipse mars,jdk8
-------------------------------------------------------------------------------------------------------------------------------------
(发布与订阅功能的)转发器,功能开关,等等。
要点一:转发器负责命令的接受与发布,并保证命令被调用。
要点二:转发器解耦双方责任关系。具有较高的灵活性。
创建CommandExecute.java文件,具体内容如下:
创建Command.java文件,具体内容如下:
创建Command.java文件,具体内容如下:
模式的再扩展:
在操作系统中的日志文件,当前流行的内存数据库,MQ组件,CQRS系统设计中等等,其都提供了快照备份的能力,此处功能实现方式有将每次的命令变化都写入备份文件,后续恢复时,再根据该命令的操作历史恢复到之前的状态。关于此功能的实现代码,请各位看官在参考如Redis日志文件的生成策略之后,结合实际需要,自行学习。
讲一个请求封装为一个对象,从而使你可用不同的请求对客户端进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
Invoker(调用者):客户端通过该对象来调用命令。其在设计时,不需要确定命令接收方,而只需要与Command抽象命令类之间关联即可。在程序运行时,在调用具体的注入的对象的方法。
Command(抽象命令类):Command类一般是一个抽象类或者接口,在其中声明了用于执行请求的execute()。这些方法将实际效用接收方的相关方法。
Receiver(接收者):命令的实际执行者,负责具体业务处理操作等。
ConcreteCommand(具体命令类):其实现了Command类中声明的方法,其对应一个具体的接受方对象,并且其中包含了对接收方方法的调用过程。
需要将命令发起方与接收方进行关系解耦。使其具有封装性,隔离性。
需要保持Invoker始终处于生存状态,即,命令的发起方可以随时结束生命周期,而Invoker调用者,始终保持活动。
在扩展模式下,需要记录系统命令操作日志。
有效降低了命令客户端与命令接收方的耦合度。使得双方相互不存在直接关联,方便后续的维护与扩展。
对于新的命令的加入对旧的命令实现过程不存在任何影响。满足了“开闭原则”。
通过记录命令的调用过程,可以实现系统的撤销与恢复功能。
具体的命令类需要对应一个具体的命令接收方,因此,在实际使用时,需要考虑业务复杂度,以免造成ConcreteCommand类的泛滥。
-------------------------------------------------------------------------------------------------------------------------------------
至此,被说了很多遍的设计模式---命令模式 结束
参考资料:
图书:《大话设计模式》
其他博文:http://blog.csdn.NET/lovelion/article/details/7563445
本文,我们讲介绍命令模式,文章主题结构与上文一致。惯例,先来看看我们示例工程的环境:
操作系统:win7
x64
其他软件:eclipse mars,jdk8
-------------------------------------------------------------------------------------------------------------------------------------
经典问题:
(发布与订阅功能的)转发器,功能开关,等等。
思路分析:
要点一:转发器负责命令的接受与发布,并保证命令被调用。要点二:转发器解耦双方责任关系。具有较高的灵活性。
示例工程:
错误写法:
创建CommandExecute.java文件,具体内容如下:
package com.csdn.ingo.gof_Command; public class CommandExecute { public void commandA(){ System.out.println("command A was executed"); } public void commandB(){ System.out.println("command B was executed"); } }创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_Command; public class Window { public static void main(String[] args) { CommandExecute cmd = new CommandExecute(); cmd.commandA(); cmd.commandB(); } }
错误原因:
客户端与被调用方的代码过于耦合,对于后期的维护与扩展极为不利。并且,客户端无法灵活的执行相关功能,即无转接器的功能。
推荐写法:
创建Command.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.one; public abstract class Command { protected Receiver receiver; public Command(Receiver receiver){ this.receiver = receiver; } public abstract void excuteCommand(); }创建ConcreteCommandA.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.one; public class ConcreteCommandA extends Command{ public ConcreteCommandA(Receiver receiver) { super(receiver); } @Override public void excuteCommand() { receiver.commandA(); } }创建ConcreteCommandB.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.one; public class ConcreteCommandB extends Command{ public ConcreteCommandB(Receiver receiver) { super(receiver); } @Override public void excuteCommand() { receiver.commandB(); } }创建Invoker.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.one; public class Invoker { private Command command; public void setOrder(Command command){ this.command = command; } public void notifya(){ command.excuteCommand(); } }创建Receiver.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.one; public class Receiver { public void commandA(){ System.out.println("command A was executed"); } public void commandB(){ System.out.println("command B was executed"); } }创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.one; public class Window { public static void main(String[] args) { Receiver boy = new Receiver(); Command cmdA = new ConcreteCommandA(boy); Command cmdB = new ConcreteCommandB(boy); Invoker ivk = new Invoker(); ivk.setOrder(cmdA); ivk.notifya(); ivk.setOrder(cmdB); ivk.notifya(); } }
推荐原因:
上文的推荐代码将客户端的命令在调度真正的receiver之间进行了封装。由此将命令的发起与执行进行分割。使得客户端不必知道命令的实际接口,及接口内的执行细节。每一个命令类对应一个命令处理器,对于客户端通过注入的命令的参数不同,其对应的实现亦不同。从而实现“封装”。
扩展实现:
创建Command.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.two; public abstract class Command { protected Receiver receiver; public Command(Receiver receiver){ this.receiver = receiver; } public abstract void excuteCommand(); }创建ConcreteCommandA.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.two; public class ConcreteCommandA extends Command{ public ConcreteCommandA(Receiver receiver) { super(receiver); } @Override public void excuteCommand() { receiver.commandA(); } }创建ConcreteCommandB.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.two; public class ConcreteCommandB extends Command{ public ConcreteCommandB(Receiver receiver) { super(receiver); } @Override public void excuteCommand() { receiver.commandB(); } }创建Invoker.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.two; import java.util.ArrayList; import java.util.Date; import java.util.List; public class Invoker { private List<Command> orders = new ArrayList<Command>(); private Command command; public void setOrder(Command command){ if(command.getClass().getName().equals(ConcreteCommandA.class.getName())){ System.out.println("Command A is closed"); }else{ orders.add(command); System.out.println("ADD Command B:"+command.getClass().getName()+",Time:"+new Date()); } } public void cancelOrder(Command command){ orders.remove(command); System.out.println("REMOVE Command:"+command.toString()+",Time:"+new Date()); } public void notifya(){ for(Command c:orders){ c.excuteCommand(); } } }创建Receiver.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.two; public class Receiver { public void commandA(){ System.out.println("Command A was executed"); } public void commandB(){ System.out.println("Command B was executed"); } }创建Window.java文件,具体内容如下:
package com.csdn.ingo.gof_Command.two; public class Window { public static void main(String[] args) { Receiver boy = new Receiver(); Command cmdA = new ConcreteCommandA(boy); Command cmdB = new ConcreteCommandB(boy); Invoker ivk = new Invoker(); ivk.setOrder(cmdA); ivk.setOrder(cmdA); ivk.setOrder(cmdB); ivk.notifya(); } }
特别提醒:
上文设计实现的undo操作,只能实现撤销命令,而不能实现有序撤销。举个栗子:如果客户端为计算器,那么此实现不能实现撤销运算,而是仅代表了正式提交前的删除某个子运算而已。模式的再扩展:
在操作系统中的日志文件,当前流行的内存数据库,MQ组件,CQRS系统设计中等等,其都提供了快照备份的能力,此处功能实现方式有将每次的命令变化都写入备份文件,后续恢复时,再根据该命令的操作历史恢复到之前的状态。关于此功能的实现代码,请各位看官在参考如Redis日志文件的生成策略之后,结合实际需要,自行学习。
模式总结:
命令模式结构图:
命令模式:
讲一个请求封装为一个对象,从而使你可用不同的请求对客户端进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。
组成部分:
Invoker(调用者):客户端通过该对象来调用命令。其在设计时,不需要确定命令接收方,而只需要与Command抽象命令类之间关联即可。在程序运行时,在调用具体的注入的对象的方法。Command(抽象命令类):Command类一般是一个抽象类或者接口,在其中声明了用于执行请求的execute()。这些方法将实际效用接收方的相关方法。
Receiver(接收者):命令的实际执行者,负责具体业务处理操作等。
ConcreteCommand(具体命令类):其实现了Command类中声明的方法,其对应一个具体的接受方对象,并且其中包含了对接收方方法的调用过程。
反思:
应用场景:
需要将命令发起方与接收方进行关系解耦。使其具有封装性,隔离性。需要保持Invoker始终处于生存状态,即,命令的发起方可以随时结束生命周期,而Invoker调用者,始终保持活动。
在扩展模式下,需要记录系统命令操作日志。
优点:
有效降低了命令客户端与命令接收方的耦合度。使得双方相互不存在直接关联,方便后续的维护与扩展。对于新的命令的加入对旧的命令实现过程不存在任何影响。满足了“开闭原则”。
通过记录命令的调用过程,可以实现系统的撤销与恢复功能。
缺点:
具体的命令类需要对应一个具体的命令接收方,因此,在实际使用时,需要考虑业务复杂度,以免造成ConcreteCommand类的泛滥。-------------------------------------------------------------------------------------------------------------------------------------
至此,被说了很多遍的设计模式---命令模式 结束
参考资料:
图书:《大话设计模式》
其他博文:http://blog.csdn.NET/lovelion/article/details/7563445
相关文章推荐
- .NET中的设计模式四:命令模式 选择自 lane_cn 的 Blog
- C++实现网络服务器命令模式(设计及模板实现)
- 乐在其中设计模式(C#) - 命令模式(Command Pattern)
- 设计模式学习日志(15)-Command命令(行为型模式) (引用)
- .NET设计模式-命令模式(Command Pattern)
- java设计模式之Command(菜单命令)
- 设计模式(18)-命令模式(Command)
- 设计模式学习笔记(十七)——Command命令模式
- 乐在其中设计模式(C#) - 命令模式(Command Pattern)
- java设计模式之Command(菜单命令)
- 设计模式之命令(Command)---对象行为型模式
- 设计模式(16)-命令模式(Command)
- C#设计模式之命令模式
- 乐在其中设计模式(C#) - 命令模式(Command Pattern)
- 设计模式----Command(命令)模式
- Java实用设计模式:Command(命令模式)
- 设计模式在二维图形程序里的应用(命令模式--command)
- 设计模式之Command命令模式
- 设计模式学习笔记(十六)——Command命令
- 设计模式学习(十二)职责链模式-命令模式-解释器模式