实例场景:
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;
}
@Override
public 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();
//在整个应用场景中,实际上是请求者调用具体命令对象,具体命令对象调用接收者
}
}
代码输出:
客户发订单了,我要炒菜了
在整个应用场景中,实际上是请求者调用具体命令对象,具体命令对象调用接收者
用户编写测试用例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;
}
@Override
public void execute() {
tv.turnOn();
}
}
定义关闭电视命令:
/**
* 定义一个具体的命令角色,关闭电视命令,直接操纵接收者
* @author dell
*
*/
public class CommandOff implements Command{
//由命令操作接收者
private TV tv;
public CommandOff(TV tv){
this.tv=tv;
}
@Override
public 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;
}
@Override
public 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
这样,客户只管操纵请求者了,至于命令是怎么传达的跟客户端没什么关系,客户也不需要知道这些细节,用户只需拿遥控器按下相应的操作,电视接收到命令,给我执行相应的结果就是了,对于命令模式,是不是又加深了一些理解呢!!记住:请求者-->命令-->接收者