命令模式

来源:互联网 发布:最快的排序算法 编辑:程序博客网 时间:2024/06/05 00:54

实例场景:

1、餐馆点菜吃饭:当我们在餐馆进行点菜的时候,一般我们会进行点菜,然后服务员下订单,然后厨师根据订单进行炒菜,最后摆在我们面前的就是一盘美味佳肴了。

2、领导下命令:领导要员工做事的时候,一般是把事情告诉秘书或行政人员,然后由她们把领导安排的事情下发到员工。

3、当我们想要听音乐,只要我们按下播放键盘,相应的音乐就自动播放了。

以上场景中我们都使用了一种设计模式:命令模式

命令模式把一个请求或者操作封装到一个对象中。命令模式运行系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

命令模式中是把一个命令封装为一个对象。

命令模式的角色

客户角色:创建一个具体命令对象,并确定其接受者

命令角色:声明一个给所有具体命令类的抽象接口,这是一个抽象角色,通常由一个接口或抽象类实现。

具体命令角色:定义接收者和行为之间的弱耦合,实现execute方法,负责调用接收者的相应操作。

请求者角色:负责调用命令对象执行请求。

接收者角色:负责具体实施和执行一个请求。

在上面的实例场景中,场景分析如下:

1、餐馆点菜,我们相当与一个客户角色,我们点菜就是发送命令,不同的人或有不同的命令请求,就应该把命令设计为抽象类,用户发出的请求就是具体的命令,服务员就是一个请求者,她把命令也就是客户的订单给接受者厨师,厨师只要照着菜单做菜,做好后给客户即可,客户不用去管菜是怎样做出来的,只要发送一个命令,然后得到自己的结果就可以了。

2、领导下命令:我是老总,我只管发个命令,至于这个命令发给谁,谁执行,关我P事,我发钱请人不是为了给自己找麻烦。你是负责事情的员工,你的天职是做好上级交给你的任务,踏踏实实,不要知道太多,不要八卦,不要问太多了。Client对象是发布命令的。Invoker对象是传递命令的,就是跑腿的。Receiver是受气包,底层最累的程序员,负责干活吧

3、一个Mp3。你按了一个播放键盘,就播放了。这就可以算是命令模式的一种。 你是Client ,按键是Invoker,mp3是Receiver,播放就是一个命令Command对象。

命令模式的好处

◆很容易构造一个命令队列

◆记录相关的命令日志

◆增加命令的状态,实现命令的撤销和重做

◆允许接受请求的一方决定是否可做

◆新的命令轻而易举可以加入其中

缺点可能会有过多的具体命令类存在

场景代码实现:

package cn.com.command;//声明一个命令角色public interface Command {public void execute();}

package cn.com.command;//定义一个接收者,相当与一个厨师//负责具体实施和执行一个请求public class Recevier {public void doAction(){System.out.println("客户发订单了,我要炒菜了");}}

package cn.com.command;//具体的命令角色,负责调用接收者相应的操作public class ConcreteCommand implements Command{private Recevier recevier;public ConcreteCommand(Recevier recevier){this.recevier=recevier;}@Overridepublic void execute() {recevier.doAction();}}
package cn.com.command;//定义一个请求者角色,//负责调用命令对象,执行请求public class Invoker {private Command command;public Invoker(Command command){this.command=command;}public void doInvokerAction(){command.execute();}}

package cn.com.command;//创建客户角色,客户进行点菜public class Client {public static void main(String[] args) {//生成一个具体的厨师角色Recevier recevier=new Recevier();//生成一个菜单命令对象Command command=new ConcreteCommand(recevier);//服务员把菜单给厨师,厨师具体实施,进行炒菜Invoker invoker=new Invoker(command);invoker.doInvokerAction();//在整个应用场景中,实际上是请求者调用具体命令对象,具体命令对象调用接收者}}

代码输出:

客户发订单了,我要炒菜了
在整个应用场景中,实际上是请求者调用具体命令对象,具体命令对象调用接收者

JUnit框架中,运用了我们的命令模式:

用户编写测试用例TestCase,把这些测试用例组成请求(可能是一个或者多个),发送到JUnit,然后由JUnit执行,最后报告详细测试结果包括执行的时间,错误方法,错误位置等。这样测试用例的开发人员就不需要知道请求TestCase的具体操作信息,仅把它当作一种命令来执行,然后把执行测试结果发给测试人员。这样就使JUnit框架和TestCase的开发人员独立开来,使得请求的一方不必知道接收请求一方的详细信息,更不必知道是怎样被接收,以及怎样被执行的,实现系统的松藕合。

使用命令模式后给JUnit系统的架构带来的效果

a、将实现请求的一方(TestCase开发)和调用一方JUnit进行解耦

b、使新的TestCase很容易加入,无需改变已有的类,只需继承TestCase类即可,这样方便了测试人员

c、可以将多个TestCase进行组合成一个复合命令,TestSuit就是它的复合命令,使用了组合模式

d、容易把请求的TestCase组成请求队列,这样使接收请求的一方JUnit框架,容易决定是否执行请求,一旦发现测试用例失败或者错误可以立即停止进行报告

简易理解场景模拟二:我们看电视,经常用到遥控,遥控上有电视开机、关机、切换频道命令等,用户按下相应的命令,电视就会执行相应的操作,我们把这个场景用命令模式来设计解决。

在这个场景中,电视机就是一个命令接收者,它接收到命令后执行相应的操作,遥控器就是命令请求者,当然是由用户去按遥控器上的按键,来请求命令,开机、关机、切换频道就是具体的命令,示意图如下:



从上图可以看出:遥控器操纵打开电视、关闭电视、切换频道等一系列命令,而这些命令来操纵电视。请求者操纵的是命令角色,而命令角色操纵的是命令接收者,下面我们编码来实现它:

定义命令接收者,电视

/** * 定义一个命令接收者 * @author dell * */public class TV {/** * 打开电视 */public void turnOn(){System.out.println("打开电视");}/** * 关闭电视 */public void turnOff(){System.out.println("关闭电视");}/** * 更换电视频道 * @param channel */public void changeChannel(int channel){System.out.println("你选择更换的频道为:"+channel);}}
定义一个抽象的命令角色:

public interface Command {public void execute();}
定义打开电视具体命令:

/** * 定义一个具体的命令角色,打开电视命令,直接操纵接收者 * @author dell * */public class CommandOn implements Command{//由命令操作接收者private TV tv;public CommandOn(TV tv){this.tv=tv;}@Overridepublic void execute() {tv.turnOn();}}
定义关闭电视命令:

/** * 定义一个具体的命令角色,关闭电视命令,直接操纵接收者 * @author dell * */public class CommandOff implements Command{//由命令操作接收者private TV tv;public CommandOff(TV tv){this.tv=tv;}@Overridepublic void execute() {tv.turnOff();}}
定义切换频道命令:

/** * 定义一个具体的命令角色,切换频道命令,直接操纵接收者 * @author dell * */public class CommandChange implements Command{//由命令操作接收者private TV tv;private int channel;public CommandChange(TV tv,int channel){this.tv=tv;this.channel=channel;}@Overridepublic void execute() {tv.changeChannel(channel);}}
定义遥控器,请求者:

/** * 定义一个请求者角色,相当于模拟场景中的遥控器,请求者角色直接控制命令 * @author dell * */public class Control {//因为用户在客户端调用的时候,只需要创建一个遥控器请求即可,所以这里可以将所有的命令实现类的引用传入进来private Command commandOn,commandOff,commandChange;public Control(Command commandOn,Command commandOff,Command commandChange){this.commandOn=commandOn;this.commandOff=commandOff;this.commandChange=commandChange;}public void turnOn(){commandOn.execute();}public void turnOff(){commandOff.execute();}public void changeChannel(){commandChange.execute();}}
客户端测试:

/** * 测试命令模式 * 角色: * 命令角色(抽象类或接口) * 具体命令角色(包含一个接收者的引用,因为是由具体命令操纵命令接受者) * 请求者角色(包含一个命令角色的引用,因为是由请求者发出命令) * 命令接受者 * @author dell * */public class Client {public static void main(String[] args) {//定义一个命令接收者(电视)TV tv=new TV();//定义一系列命令,例如打开电视、关闭电视、切换电视频道等Command commandOn=new CommandOn(tv);Command commandOff=new CommandOff(tv);Command commandChange=new CommandChange(tv,5);//客户对遥控器发出相应命令,客户只需要定义一个请求角色,即可获得所有所操控的命令,相当于只要一个遥控器Control control=new Control(commandOn,commandOff,commandChange);control.turnOn();control.turnOff();control.changeChannel();}}

代码输出:

打开电视
关闭电视
你选择更换的频道为:5

这样,客户只管操纵请求者了,至于命令是怎么传达的跟客户端没什么关系,客户也不需要知道这些细节,用户只需拿遥控器按下相应的操作,电视接收到命令,给我执行相应的结果就是了,对于命令模式,是不是又加深了一些理解呢!!记住:请求者-->命令-->接收者

原创粉丝点击