Blog信息 |
blog名称: 日志总数:1304 评论数量:2242 留言数量:5 访问次数:7592118 建立时间:2006年5月29日 |

| |
[设计模式]Java中的模式 --- 命令模式的(实现,功能,使用场合)及如何配合其它模式使用命令模式 软件技术
lhwork 发表于 2006/12/19 9:59:25 |
一,命令模式的实现:命令模式里边一般都有以下几个角色:客户端,请求者,命令接口,命令实现,接受者,下边是简单命令模式的实现代码实现:
1500)this.width=500'>500)this.width=500'>public class Client500)this.width=500'>{ 2500)this.width=500'>500)this.width=500'> public static void main(String[] args)500)this.width=500'>{ 3500)this.width=500'> Receiver receiver = new Receiver(); 4500)this.width=500'> Command commandOne = new ConcreteCommandOne(receiver); 5500)this.width=500'> Command commandTwo = new ConcreteCommandTwo(receiver); 6500)this.width=500'> Invoker invoker = new Invoker(commandOne,commandTwo); 7500)this.width=500'> invoker.actionOne(); 8500)this.width=500'> invoker.actionTwo(); 9500)this.width=500'> }10500)this.width=500'>}11500)this.width=500'>500)this.width=500'>public class Invoker500)this.width=500'>{12500)this.width=500'> private Command commandOne;13500)this.width=500'> private Command commandTwo;14500)this.width=500'>500)this.width=500'> public Invoker(Command commandOne,Command commandTwo)500)this.width=500'>{15500)this.width=500'> this.commandOne = commandOne;16500)this.width=500'> this.commandTwo = commandTwo;17500)this.width=500'> }18500)this.width=500'>500)this.width=500'> public void actionOne()500)this.width=500'>{19500)this.width=500'> commandOne.execute();20500)this.width=500'> }21500)this.width=500'>500)this.width=500'> public void actionTwo()500)this.width=500'>{22500)this.width=500'> commandTwo.execute();23500)this.width=500'> }24500)this.width=500'>}25500)this.width=500'>500)this.width=500'>public interface Command500)this.width=500'>{26500)this.width=500'> void execute();27500)this.width=500'>}28500)this.width=500'>500)this.width=500'>public class ConcreteCommandOne implements Command500)this.width=500'>{29500)this.width=500'> private Receiver receiver30500)this.width=500'>500)this.width=500'> public ConcreteCommandOne(Receiver receiver)500)this.width=500'>{31500)this.width=500'> this.receiver = receiver;32500)this.width=500'> }33500)this.width=500'>500)this.width=500'> public void execute()500)this.width=500'>{34500)this.width=500'> receiver.actionOne();35500)this.width=500'> }36500)this.width=500'>}37500)this.width=500'>500)this.width=500'>public class ConcreteCommandTwo implements Command500)this.width=500'>{38500)this.width=500'> private Receiver receiver39500)this.width=500'>500)this.width=500'> public ConcreteCommandTwo(Receiver receiver)500)this.width=500'>{40500)this.width=500'> this.receiver = receiver;41500)this.width=500'> }42500)this.width=500'>500)this.width=500'> public void execute()500)this.width=500'>{43500)this.width=500'> receiver.actionTwo();44500)this.width=500'> }45500)this.width=500'>}46500)this.width=500'>500)this.width=500'>public class Receiver500)this.width=500'>{47500)this.width=500'>500)this.width=500'> public Receiver()500)this.width=500'>{48500)this.width=500'> //500)this.width=500'>49500)this.width=500'> }50500)this.width=500'>500)this.width=500'> public void actionOne()500)this.width=500'>{51500)this.width=500'> System.out.println("ActionOne has been taken.");52500)this.width=500'> }53500)this.width=500'>500)this.width=500'> public void actionTwo()500)this.width=500'>{54500)this.width=500'> System.out.println("ActionTwo has been taken.");55500)this.width=500'> }56500)this.width=500'>}二,命令模式的功能,好处,或者说为什么使用命令模式?上边的代码是否看起来很傻呢,本来可以这样简单实现的: 1500)this.width=500'>500)this.width=500'>public class Client500)this.width=500'>{ 2500)this.width=500'>500)this.width=500'> public static void main(String[] args)500)this.width=500'>{ 3500)this.width=500'> Receiver receiver = new Receiver(); 4500)this.width=500'> receiver.actionOne(); 5500)this.width=500'> receiver.actionTwo(); 6500)this.width=500'> } 7500)this.width=500'>} 8500)this.width=500'>500)this.width=500'>public class Receiver500)this.width=500'>{ 9500)this.width=500'>500)this.width=500'> public Receiver()500)this.width=500'>{10500)this.width=500'> //500)this.width=500'>11500)this.width=500'> }12500)this.width=500'>500)this.width=500'> public void actionOne()500)this.width=500'>{13500)this.width=500'> System.out.println("ActionOne has been taken.");14500)this.width=500'> }15500)this.width=500'>500)this.width=500'> public void actionTwo()500)this.width=500'>{16500)this.width=500'> System.out.println("ActionTwo has been taken.");17500)this.width=500'> }18500)this.width=500'>}看多简洁,如果是像上边如此简单的需求,这个才应该是我们的选择,但是有些情况下这样的写法不能解决的,或者说解决起来不好,所以引入命令模式.(1)我们须要Client和Receiver同时开发,而且在开发过程中分别须要不停重购,改名(2)如果我们要求Redo ,Undo等功能(3)我们须要命令不按照调用执行,而是按照执行时的情况排序,执行(4)开发后期,我们发现必须要log哪些方法执行了,如何在尽量少更改代码的情况下实现.并且渐少重复代码(5)在上边的情况下,我们的接受者有很多,不止一个解决办法:情况一,我们可以定义一个接口,让Receiver实现这个接口,Client按照接口调用。情况二,我们可以让Receiver记住一些状态,例如执行前的自己的状态,用来undo,但自己记录自己的状态 实现起来比较混乱,一般都是一个累记录另一个类的状态.情况三,很难实现情况四,,我们须要在每个Action,前后加上log情况五,相对好实现,但是再加上这个,是否感觉最终的实现很混乱呢好,我们再来看看命令模式,在命令模式中,我们增加一些过渡的类,这些类就是上边的命名接口和命令实现,这样就很好的解决了情况一,情况二。我们再加入一个Invoker,这样情况三和情况四就比较好解决了。如下加入Log和排序后的Invoker 1500)this.width=500'>500)this.width=500'>public class Invoker500)this.width=500'>{ 2500)this.width=500'> private List cmdList = new ArrayList(); 3500)this.width=500'>500)this.width=500'> public Invoker()500)this.width=500'>{ 4500)this.width=500'> } 5500)this.width=500'>500)this.width=500'> public add(Command command)500)this.width=500'>{ 6500)this.width=500'> cmdList.add(command); 7500)this.width=500'> } 8500)this.width=500'>500)this.width=500'> public remove(Command command)500)this.width=500'>{ 9500)this.width=500'> cmdList.remove(command);10500)this.width=500'> }11500)this.width=500'>500)this.width=500'> public void action()500)this.width=500'>{12500)this.width=500'> Command cmd;13500)this.width=500'>500)this.width=500'> while((cmd =getCmd()) != null)500)this.width=500'>{14500)this.width=500'> log("begin"+cmd.getName());15500)this.width=500'> cmd.execute();16500)this.width=500'> log("end"+cmd.getName()); 17500)this.width=500'> }18500)this.width=500'> }19500)this.width=500'>500)this.width=500'> public Command getCmd()500)this.width=500'>{20500)this.width=500'> //按照自定义优先级,排序取出cmd21500)this.width=500'> }22500)this.width=500'>}23500)this.width=500'>500)this.width=500'>public class Client500)this.width=500'>{24500)this.width=500'>500)this.width=500'> public static void main(String[] args)500)this.width=500'>{25500)this.width=500'> Receiver receiver = new Receiver();26500)this.width=500'> Command commandOne = new ConcreteCommandOne(receiver);27500)this.width=500'> Command commandTwo = new ConcreteCommandTwo(receiver);28500)this.width=500'> Invoker invoker = new Invoker();29500)this.width=500'> invoker.add(commandOne);30500)this.width=500'> invoker.add(commandTwo);31500)this.width=500'> iinvoker.action();32500)this.width=500'> }33500)this.width=500'>}三,命令模式与其它模式的配合使用:1,看上边的Invoker的实现是否很像代理模式呢,Invoker的这种实现其实就是一种代理模式。2,需求:有个固定命令组合会多次被执行 解决:加入合成模式,实现方法如下,定义一个宏命令类: 1500)this.width=500'>500)this.width=500'>public class MacroCommand implements Command500)this.width=500'>{ 2500)this.width=500'> private List cmdList = new ArrayList(); 3500)this.width=500'>500)this.width=500'> public add(Command command)500)this.width=500'>{ 4500)this.width=500'> cmdList.add(command); 5500)this.width=500'> } 6500)this.width=500'>500)this.width=500'> public remove(Command command)500)this.width=500'>{ 7500)this.width=500'> cmdList.remove(command); 8500)this.width=500'> } 9500)this.width=500'>500)this.width=500'> public void execute()500)this.width=500'>{10500)this.width=500'> Command cmd;11500)this.width=500'>500)this.width=500'> for(int i=0;i<cmdList.size();i++)500)this.width=500'>{12500)this.width=500'> cmd = (Command)cmdList.get(i);13500)this.width=500'> cmd.execute();14500)this.width=500'> }15500)this.width=500'> } 16500)this.width=500'>}3,需求:须要redo undo 解决:加入备忘录模式,一个简单的实现如下 1500)this.width=500'>500)this.width=500'>public class ConcreteCommandOne implements Command500)this.width=500'>{ 2500)this.width=500'> private Receiver receiver 3500)this.width=500'> private Receiver lastReceiver; 4500)this.width=500'>500)this.width=500'> public ConcreteCommandOne(Receiver receiver)500)this.width=500'>{ 5500)this.width=500'> this.receiver = receiver; 6500)this.width=500'> } 7500)this.width=500'>500)this.width=500'> public void execute()500)this.width=500'>{ 8500)this.width=500'> record(); 9500)this.width=500'> receiver.actionOne();10500)this.width=500'> }11500)this.width=500'>500)this.width=500'> public void undo()500)this.width=500'>{12500)this.width=500'> //恢复状态500)this.width=500'>13500)this.width=500'> }14500)this.width=500'>500)this.width=500'> public void redo()500)this.width=500'>{15500)this.width=500'> lastReceiver.actionOne();16500)this.width=500'> //500)this.width=500'>17500)this.width=500'> }18500)this.width=500'>500)this.width=500'> public record()500)this.width=500'>{19500)this.width=500'> //记录状态20500)this.width=500'> }21500)this.width=500'>}4,需求:命令很多类似的地方 解决:使用原型模式,利用clone 这个就不写例子了。四,命令模式的使用场合1,须要callback的时候,例如java awt/swing/swt中的Listening的消息方式2,须要对请求排队执行,命令的发送者和接受者有不同对的生命周期,就是命令执行的时候,可能发出命令的Client已经不存在了3,须要Redo Undo等函数4,须要log每条命令5,须要支持transaction,封装一组数据命令的时候.五,最后再次总结一下命令模式的优点和缺点:优点:降低Client和命令接受者的耦合,是命令请求和命令执行的对象分割便于修改和扩张便于聚合多个命令缺点:造成出现过多的具体命令类,太多文件。五,一个比较有意思的例子,来说明命令模式Client :看电视的人Invoker :遥控器Command :电信号具体命令 :遥控器上的按键对应的不同的电信号Receiver :电视机最后说一句,并不是全部按照模式写一定就好,应该根据你的需求来应用,或者全部应用,或者部分应用,或者根本不用。 |
|
|