一,命令模式的实现: 命令模式里边一般都有以下几个角色:客户端,请求者,命令接口,命令实现,接受者, 下边是简单命令模式的实现代码实现:
1 public class Client { 2 public static void main(String[] args) { 3 Receiver receiver = new Receiver(); 4 Command commandOne = new ConcreteCommandOne(receiver); 5 Command commandTwo = new ConcreteCommandTwo(receiver); 6 Invoker invoker = new Invoker(commandOne,commandTwo); 7 invoker.actionOne(); 8 invoker.actionTwo(); 9 } 10 } 11 public class Invoker { 12 private Command commandOne; 13 private Command commandTwo; 14 public Invoker(Command commandOne,Command commandTwo) { 15 this.commandOne = commandOne; 16 this.commandTwo = commandTwo; 17 } 18 public void actionOne() { 19 commandOne.execute(); 20 } 21 public void actionTwo() { 22 commandTwo.execute(); 23 } 24 } 25 public interface Command { 26 void execute(); 27 } 28 public class ConcreteCommandOne implements Command { 29 private Receiver receiver 30 public ConcreteCommandOne(Receiver receiver) { 31 this.receiver = receiver; 32 } 33 public void execute() { 34 receiver.actionOne(); 35 } 36 } 37 public class ConcreteCommandTwo implements Command { 38 private Receiver receiver 39 public ConcreteCommandTwo(Receiver receiver) { 40 this.receiver = receiver; 41 } 42 public void execute() { 43 receiver.actionTwo(); 44 } 45 } 46 public class Receiver { 47 public Receiver() { 48 // 49 } 50 public void actionOne() { 51 System.out.println("ActionOne has been taken."); 52 } 53 public void actionTwo() { 54 System.out.println("ActionTwo has been taken."); 55 } 56 }
二,命令模式的功能,好处,或者说为什么使用命令模式? 上边的代码是否看起来很傻呢,本来可以这样简单实现的:
1 public class Client { 2 public static void main(String[] args) { 3 Receiver receiver = new Receiver(); 4 receiver.actionOne(); 5 receiver.actionTwo(); 6 } 7 } 8 public class Receiver { 9 public Receiver() { 10 // 11 } 12 public void actionOne() { 13 System.out.println("ActionOne has been taken."); 14 } 15 public void actionTwo() { 16 System.out.println("ActionTwo has been taken."); 17 } 18 }
看多简洁,如果是像上边如此简单的需求,这个才应该是我们的选择,但是有些情况下这样的写法不能解决的, 或者说解决起来不好,所以引入命令模式. (1)我们须要Client和Receiver同时开发,而且在开发过程中分别须要不停重购,改名 (2)如果我们要求Redo ,Undo等功能 (3)我们须要命令不按照调用执行,而是按照执行时的情况排序,执行 (4)开发后期,我们发现必须要log哪些方法执行了,如何在尽量少更改代码的情况下实现.并且渐少重复代码 (5)在上边的情况下,我们的接受者有很多,不止一个 解决办法: 情况一,我们可以定义一个接口,让Receiver实现这个接口,Client按照接口调用。 情况二,我们可以让Receiver记住一些状态,例如执行前的自己的状态,用来undo,但自己记录自己的状态 实现起来比较混乱,一般都是一个累记录另一个类的状态. 情况三,很难实现 情况四,,我们须要在每个Action,前后加上log 情况五,相对好实现,但是再加上这个,是否感觉最终的实现很混乱呢 好,我们再来看看命令模式,在命令模式中,我们增加一些过渡的类,这些类就是上边的命名接口和命令实现, 这样就很好的解决了情况一,情况二。我们再加入一个Invoker,这样情况三和情况四就比较好解决了。
如下加入Log和排序后的Invoker
1 public class Invoker { 2 private List cmdList = new ArrayList(); 3 public Invoker() { 4 } 5 public add(Command command) { 6 cmdList.add(command); 7 } 8 public remove(Command command) { 9 cmdList.remove(command); 10 } 11 public void action() { 12 Command cmd; 13 while((cmd =getCmd()) != null) { 14 log("begin"+cmd.getName()); 15 cmd.execute(); 16 log("end"+cmd.getName()); 17 } 18 } 19 public Command getCmd() { 20 //按照自定义优先级,排序取出cmd 21 } 22 } 23 public class Client { 24 public static void main(String[] args) { 25 Receiver receiver = new Receiver(); 26 Command commandOne = new ConcreteCommandOne(receiver); 27 Command commandTwo = new ConcreteCommandTwo(receiver); 28 Invoker invoker = new Invoker(); 29 invoker.add(commandOne); 30 invoker.add(commandTwo); 31 iinvoker.action(); 32 } 33 }
三,命令模式与其它模式的配合使用: 1,看上边的Invoker的实现是否很像代理模式呢,Invoker的这种实现其实就是一种代理模式。
2,需求:有个固定命令组合会多次被执行 解决:加入合成模式,实现方法如下,定义一个宏命令类:
1 public class MacroCommand implements Command { 2 private List cmdList = new ArrayList(); 3 public add(Command command) { 4 cmdList.add(command); 5 } 6 public remove(Command command) { 7 cmdList.remove(command); 8 } 9 public void execute() { 10 Command cmd; 11 for(int i=0;i<cmdList.size();i++) { 12 cmd = (Command)cmdList.get(i); 13 cmd.execute(); 14 } 15 } 16 }
3,需求:须要redo undo 解决:加入备忘录模式,一个简单的实现如下
1 public class ConcreteCommandOne implements Command { 2 private Receiver receiver 3 private Receiver lastReceiver; 4 public ConcreteCommandOne(Receiver receiver) { 5 this.receiver = receiver; 6 } 7 public void execute() { 8 record(); 9 receiver.actionOne(); 10 } 11 public void undo() { 12 //恢复状态 13 } 14 public void redo() { 15 lastReceiver.actionOne(); 16 // 17 } 18 public record() { 19 //记录状态 20 } 21 }
4,需求:命令很多类似的地方 解决:使用原型模式,利用clone 这个就不写例子了。 四,命令模式的使用场合 1,须要callback的时候,例如java awt/swing/swt中的Listening的消息方式 2,须要对请求排队执行,命令的发送者和接受者有不同对的生命周期,就是命令执行的时候,可能发出命令的 Client已经不存在了 3,须要Redo Undo等函数 4,须要log每条命令 5,须要支持transaction,封装一组数据命令的时候. 五,最后再次总结一下命令模式的优点和缺点: 优点: 降低Client和命令接受者的耦合,是命令请求和命令执行的对象分割 便于修改和扩张 便于聚合多个命令 缺点: 造成出现过多的具体命令类,太多文件。
五,一个比较有意思的例子,来说明命令模式 Client :看电视的人 Invoker :遥控器 Command :电信号 具体命令 :遥控器上的按键对应的不同的电信号 Receiver :电视机 最后说一句,并不是全部按照模式写一定就好,应该根据你的需求来应用,或者全部应用,或者部分应用,或者根本不用。
|