设计模式之命令模式

来源:互联网 发布:才真旺姆解释双修 知乎 编辑:程序博客网 时间:2024/06/09 14:55

       当一系列的类能直接找到我,让我帮他们干活时,其实我是拒绝的。我又要接待你们(接命令),又要干活(执行命令),真是麻烦。自从我有了一个秘书之后,事情就好办多了,因为我再也不需要接待别的类了,因为秘书能帮我接收命令,把方法调用(method invocation)封装起来了,而且我和秘书的耦合程度也很低哦。(解耦)

 

1. 命令模式

      命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

 

2. 封装方法调用

      命令对象不直接实现execute()方法的细节。尽量设计“傻瓜”命令对象,它只懂得调用一个接受者的一个行为,能使调用者和接受者之间的解耦程度比设计聪明的命令对象高。


       命令模式结构代码:

       1)命令抽象类,用来声明操作的接口(interface会更好)

public abstract class Command {protected Receiver receiver;public Command(Receiver receiver){this.receiver = receiver;}abstract public void Execute();}

       2)具体命令类,将一个接受者绑定于一个动作,调用接受者相应的操作,以实现Execute(一系列操作)

class ConcreteCommand extends Command{public ConcreteCommand(Receiver receiver){super(receiver);}@Overridepublic void Execute(){receiver.Action();}}

       3)传达者,要求该命令执行这个或一系列请求

class Invoker{private Command command;//private List<Command> l = new ArrayList<>();public void setCommand(Command command){this.command = command;}public void ExecuteCommand(){command.Execute();}}

      4)接收者,知道如何实施与执行一个与请求相关的操作

class Receiver{public void Action(){/*执行请求*/System.out.println("执行请求!");}}

       5)命令接受及执行过程

public class Test {public static void main(String[] args) {Receiver r = new Receiver();Command c = new ConcreteCommand(r);Invoker i = new Invoker();i.setCommand(c);i.ExecuteCommand();}}


       命令模式的要点:

             I. 命令模式将发出请求的对象和执行请求的对象解耦

              II. 在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作。

              III. 调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用(命令可以支持撤销)。

              IV. 宏命令是命令的一种延伸,允许调用多个命令。

              V. 命令也可以用来实现日志和事务系统。 


3. BBQ烧烤档

       1)烧烤店厨师:执行烧烤命令的类

/*烧烤者,能执行命令的人*/class Barbecuer{public void BakeMutton(){/*烤羊肉串*/System.out.println("烤羊肉串");}public void BakeChickenWing(){/*烤鸡翅*/System.out.println("烤鸡翅");}public void BakeBacon(){/*烤培根*/System.out.println("烤培根");}}

       2)抽象命令类,关于烧烤的命令

//抽象命令interface BBQCommand{//执行命令abstract public void excuteCommand();@Overridepublic String toString();}

       3)具体命令类,执行命令时,执行具体的行为,烤不同的食材。(如烤鸡翅)

/*具体命令类。执行命令时,执行具体的行为*/class BakeMuttonCommand implements BBQCommand{   //烤羊肉串命令Barbecuer barbecuer;public BakeMuttonCommand(Barbecuer barbecuer) {this.barbecuer = barbecuer;}@Overridepublic void excuteCommand() {barbecuer.BakeMutton();}@Overridepublic String toString() {return this.getClass().getName();}}class BakeChickenWingCommand implements BBQCommand{   //烤鸡翅命令Barbecuer barbecuer;public BakeChickenWingCommand(Barbecuer barbecuer) {this.barbecuer = barbecuer;}@Overridepublic void excuteCommand() {barbecuer.BakeChickenWing();}@Overridepublic String toString() {return this.getClass().getName();}}class BakeBaconCommand implements BBQCommand{   //烤培根命令Barbecuer barbecuer;public BakeBaconCommand(Barbecuer barbecuer) {this.barbecuer = barbecuer;}@Overridepublic void excuteCommand() {barbecuer.BakeBacon();}@Overridepublic String toString() {return this.getClass().getName();}}

       4)具体命令类,宏命令。执行此命令时,会执行一系列命令。

//宏命令。(来个烧烤套餐)class MacroCommand implements BBQCommand{BBQCommand[] commands;public MacroCommand(BBQCommand[] commands){this.commands = commands;}@Overridepublic void excuteCommand() {for(BBQCommand c:commands){c.excuteCommand();}}@Overridepublic String toString() {return this.getClass().getName();}}

       5)传递者类,传达请求,与请求者及执行者都解耦了。此处即为服务员。

/*服务员。传递命令的类*/class Waiter{private List<BBQCommand> orders = new ArrayList<>();  //存放命令的容器//设置订单public void setOrder(BBQCommand command){if(command.toString() == "Command.BakeChickenWingCommand"){   //货存确认System.out.println("无存货:" + command.toString());}else{orders.add(command);System.out.println("增加订单:" + command.toString());}}//取消订单public void cancelOrder(BBQCommand command){orders.remove(command);System.out.println("取消订单:" + command);}//通知全部执行public void notifyExecuter(){for(BBQCommand c:orders){c.excuteCommand();}}}

       6)客户端,即完成命令模式的系统或插件。此处为烧烤店。

public class BBQ {public static void main(String[] args) {//开店前的准备Barbecuer boy = new Barbecuer();BBQCommand a = new BakeMuttonCommand(boy);BBQCommand b = new BakeChickenWingCommand(boy);BBQCommand c = new BakeBaconCommand(boy);BBQCommand[] SetMeal = {a,b,c};BBQCommand d = new MacroCommand(SetMeal);  //烧烤套餐Waiter girl = new Waiter();//开店营业  顾客点菜girl.setOrder(a);girl.setOrder(b);girl.setOrder(c);girl.setOrder(d);girl.setOrder(a);girl.cancelOrder(a);  //撤销命令//点菜完毕,通知厨房girl.notifyExecuter();}}

       7)运行结果,当命令类名为烤鸡翅时,显示无存货。(实际应用时可在客户端,即烧烤店里记录存货以对比库存)



4. 总结

1)命令模式较为容易地设计一个命令队列;

2)能将命令记入日志,并以此来支持撤销功能

3)加入新的具体命令类不影响其他的类,因此增加新的命令类很容易;

4)封装方法调用中的五点要点。


注意:

       敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。(自大话设计模式)



参考资料:

1.《Head First设计模式》

2.《大话设计模式》


0 0
原创粉丝点击