博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式之命令模式
阅读量:5346 次
发布时间:2019-06-15

本文共 14045 字,大约阅读时间需要 46 分钟。

让我们从一个简单的项目(遥控器)了解命令模式,然后再给出他的定义:

此项目地址:

          

实现命令接口:

首先,让所有的命令对象实现相同的包含一个方法的接口。我们暂且使用一贯的名称execute()。

这就是命令接口(Command.java): 

1 public interface Command {2     public void execute();  //简单,只须一个方法 execute()3 }

实现一个打开电灯的命令:

现在,假设想实现一个打开电灯的命令。有已知我们的Light类有两个方法:on()和off()。下面是如

何将它实现成一个命令(LightOnCommand.java):

1 //这是一个命令,所以要实现Command接口 2 public class LightOnCommand implements Command{ 3     Light light; 4  5 //    构造器被传入某个电灯(比如客厅的电灯),以便让这 6 //    个命令控制。一旦调用execute(),就有这个电灯对象 7 //    成为接收者。负责接收请求。 8     public LightOnCommand(Light light){ 9         this.light = light;10     }11 12 //    这个execute()方法调用接收对象(我们正在控制的电灯)的on()方法13     public void execute(){14         light.on();15     }16 }

现在有了LightOnCommand类,接下来让我们看一下如何使用它(SimpleRemoteController.java):

1 public class SimpleRemoteControl { 2     //有一个插槽持有命令,而这个命令控制着一个装置 3     Command slot; 4      5     public SimpleRemoteControl(){}; 6      7     //这个方法用来设置插槽控制的命令。如果想改变遥 8     // 控器按钮的行为,可以多次调用这个方法 9     public void setCommand(Command command){10         slot = command;11     }12     13     //当按下按钮时,这个方法就会被调用,使得当前命14     // 令衔接插槽,并调用方法execute()15     public void buttonWasPressed(){16         slot.execute();17     }18 }

现在让我们测试一下(Main.java):

1 public class Main { 2  3     public static void main(String[] args) { 4         SimpleRemoteControl remote = new SimpleRemoteControl(); 5         Light light = new Light(); 6         LightOnCommand lightOn = new LightOnCommand(light); 7  8         remote.setCommand(lightOn); 9         remote.buttonWasPressed();10     }11 }

结果:

 

好啦!通过如上例子,现在我们该给出命令模式的定义了:

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

                     模式也支持撤销的操作。

    来看看他的类图:

    

上面完成了简单遥控,但是要知道,一个遥控器可不止有一个插槽(每个插槽都具备了“开”和“关”

按钮),所以我们现在就要用到数组,作如下工作:

1 onCommand[0] = onCommand;2 offCommand[0] = offCommand;

实现遥控器(RemoteController.java):

1 public class RemoteControl { 2     //这时候,遥控器要处理7个开宇关的命令,使用相应的数组记录这些命令 3     Command[] onCommands; 4     Command[] offCommands; 5   6     public RemoteControl() { 7         //在构造器中,只需实例化并初始化这两个开与关的数组 8         onCommands = new Command[7]; 9         offCommands = new Command[7];10  11         Command noCommand = new NoCommand();12         for (int i = 0; i < 7; i++) {13             onCommands[i] = noCommand;14             offCommands[i] = noCommand;15         }16     }17   18     //这个方法需要有3个参数,分别是插槽的位置,开的命令、关的命令。这些命令将记录19     // 在开关数组中对应的插槽位置、以供稍后使用20     public void setCommand(int slot, Command onCommand, Command offCommand) {21         onCommands[slot] = onCommand;22         offCommands[slot] = offCommand;23     }24  25     //当按下开或关的按钮,硬件就会负责调用相应的方法,也就是onButtonWasPushed26     // 或offButtonWasPushed27     public void onButtonWasPushed(int slot) {28         onCommands[slot].execute();29     }30  31     public void offButtonWasPushed(int slot) {32         offCommands[slot].execute();33     }34   35     public String toString() {36         StringBuffer stringBuff = new StringBuffer();37         stringBuff.append("\n------ Remote Control -------\n");38         for (int i = 0; i < onCommands.length; i++) {39             stringBuff.append("[slot " + i + "] " + onCommands[i].getClass().getName()40                 + "    " + offCommands[i].getClass().getName() + "\n");41         }42         return stringBuff.toString();43     }44 }

实现命令:

LightOffCommand.java:

1 public class LightOffCommand implements Command { 2     Light light; 3   4     public LightOffCommand(Light light) { 5         this.light = light; 6     } 7   8     public void execute() { 9         light.off();10     }11 }

还有很多命令就不一一列举了,原理都一样,相当于很多机械操作。

开始测试遥控器:

1 public class RemoteLoader { 2   3     public static void main(String[] args) { 4         RemoteControl remoteControl = new RemoteControl(); 5   6         Light livingRoomLight = new Light("Living Room"); 7         Light kitchenLight = new Light("Kitchen"); 8         CeilingFan ceilingFan= new CeilingFan("Living Room"); 9         GarageDoor garageDoor = new GarageDoor("");10         Stereo stereo = new Stereo("Living Room");11   12         LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);13         LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);14         LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);15         LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);16   17         CeilingFanOnCommand ceilingFanOn = new CeilingFanOnCommand(ceilingFan);18         CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan);19  20         GarageDoorUpCommand garageDoorUp = new GarageDoorUpCommand(garageDoor);21         GarageDoorDownCommand garageDoorDown = new GarageDoorDownCommand(garageDoor);22  23         StereoOnWithCDCommand stereoOnWithCD = new StereoOnWithCDCommand(stereo);24         StereoOffCommand  stereoOff = new StereoOffCommand(stereo);25  26         remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);27         remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);28         remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);29         remoteControl.setCommand(3, stereoOnWithCD, stereoOff);30   31         System.out.println(remoteControl);32  33         remoteControl.onButtonWasPushed(0);34         remoteControl.offButtonWasPushed(0);35         remoteControl.onButtonWasPushed(1);36         remoteControl.offButtonWasPushed(1);37         remoteControl.onButtonWasPushed(2);38         remoteControl.offButtonWasPushed(2);39         remoteControl.onButtonWasPushed(3);40         remoteControl.offButtonWasPushed(3);41     }42 }

注:在遥控器中,我们不想每次都检查是否某个插槽都加载了命令。比方说,在这个onButtonWasP-

      ushed()方法中,我们可能需要这样的代码:

1 public void onButtonWasPushed(int slot){2       if(onCommands[slot] != null){3             onCommands[slot].execute();4       }5 }

       So,为了避免上述做法,我们实现一个不做事前的命令:

1 public class NoCommand implements Command {2     public void execute() { }3 }

       这么一来,在RemoteControl构造器中,我们将个每个插槽都预先制定或NoCommand对象,以便

      确定每个插槽永远都有名命令对象。

1         Command noCommand = new NoCommand();2         for (int i = 0; i < 7; i++) {3             onCommands[i] = noCommand;4             offCommands[i] = noCommand;5         }

        所以在测试的输出中,没有被明确指定命令的插槽,其命令将是默认的NoCommand对象。

第二个项目代码地址:

            

还记得我们之前说的undo操作嘛?现在我们来实现它:

代码地址:

我们只需要在Command.java,我们只需加上第三行代码就好:

1 public interface Command {2     public void execute();3     public void undo();4 }

然后再从LightOnCommand开始下手:

1 public class LightOnCommand implements Command { 2     Light light; 3     int level; 4     public LightOnCommand(Light light) { 5         this.light = light; 6     } 7   8     public void execute() { 9         level = light.getLevel();10         light.on();11     }12  13     //加上14     public void undo() {15         light.dim(level);16     }17 }

最后是RemoteControllerWithUndo.java:

1 public class RemoteControlWithUndo { 2     Command[] onCommands; 3     Command[] offCommands; 4     Command undoCommand; 5   6     public RemoteControlWithUndo() { 7         onCommands = new Command[7]; 8         offCommands = new Command[7]; 9  10         Command noCommand = new NoCommand();11         for(int i=0;i<7;i++) {12             onCommands[i] = noCommand;13             offCommands[i] = noCommand;14         }15         undoCommand = noCommand;16     }17   18     public void setCommand(int slot, Command onCommand, Command offCommand) {19         onCommands[slot] = onCommand;20         offCommands[slot] = offCommand;21     }22  23     public void onButtonWasPushed(int slot) {24         onCommands[slot].execute();25         undoCommand = onCommands[slot];26     }27  28     public void offButtonWasPushed(int slot) {29         offCommands[slot].execute();30         undoCommand = offCommands[slot];31     }32  33     public void undoButtonWasPushed() {34         undoCommand.undo();35     }36   37     public String toString() {38         StringBuffer stringBuff = new StringBuffer();39         stringBuff.append("\n------ Remote Control -------\n");40         for (int i = 0; i < onCommands.length; i++) {41             stringBuff.append("[slot " + i + "] " + onCommands[i].getClass().getName()42                 + "    " + offCommands[i].getClass().getName() + "\n");43         }44         stringBuff.append("[undo] " + undoCommand.getClass().getName() + "\n");45         return stringBuff.toString();46     }47 }

测试类(RemoteLoader.java):

1 public class RemoteLoader { 2   3     public static void main(String[] args) { 4         RemoteControlWithUndo remoteControl = new RemoteControlWithUndo(); 5   6         Light livingRoomLight = new Light("Living Room"); 7   8         LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight); 9         LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);10  11         remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);12  13         remoteControl.onButtonWasPushed(0);14         remoteControl.offButtonWasPushed(0);15         System.out.println(remoteControl);16         remoteControl.undoButtonWasPushed();17         remoteControl.offButtonWasPushed(0);18         remoteControl.onButtonWasPushed(0);19         System.out.println(remoteControl);20         remoteControl.undoButtonWasPushed();21 22     }23 }

好了,实现电灯的撤销很简单。但是,通常,想要实现撤销的功能,需要记录一些状态。让我们是一个更有

趣的例子,比方说厂商类中的天花板的吊扇。吊扇允许有多种转动速度,当然也允许被关闭。

吊扇源码如下:

1 public class CeilingFan { 2     public static final int HIGH = 3; 3     public static final int MEDIUM = 2; 4     public static final int LOW = 1; 5     public static final int OFF = 0; 6     String location; 7     int speed; 8   9     public CeilingFan(String location) {10         this.location = location;11         speed = OFF;12     }13   14     public void high() {15         speed = HIGH;16         System.out.println(location + " ceiling fan is on high");17     } 18  19     public void medium() {20         speed = MEDIUM;21         System.out.println(location + " ceiling fan is on medium");22     }23  24     public void low() {25         speed = LOW;26         System.out.println(location + " ceiling fan is on low");27     }28   29     public void off() {30         speed = OFF;31         System.out.println(location + " ceiling fan is off");32     }33   34     public int getSpeed() {35         return speed;36     }37 }

加入撤销到吊扇的命令类(CeilingFanHighCommand.java):

1 public class CeilingFanHighCommand implements Command { 2     CeilingFan ceilingFan; 3     int prevSpeed; 4    5     public CeilingFanHighCommand(CeilingFan ceilingFan) { 6         this.ceilingFan = ceilingFan; 7     } 8   9     public void execute() {10         prevSpeed = ceilingFan.getSpeed();11         ceilingFan.high();12     }13  14     public void undo() {15         if (prevSpeed == CeilingFan.HIGH) {16             ceilingFan.high();17         } else if (prevSpeed == CeilingFan.MEDIUM) {18             ceilingFan.medium();19         } else if (prevSpeed == CeilingFan.LOW) {20             ceilingFan.low();21         } else if (prevSpeed == CeilingFan.OFF) {22             ceilingFan.off();23         }24     }25 }

添加测试类(RemoteLoader.java):

1 public class RemoteLoader { 2  3     public static void main(String[] args) { 4         CeilingFan ceilingFan = new CeilingFan("Living Room"); 5  6         CeilingFanMediumCommand ceilingFanMedium = new CeilingFanMediumCommand(ceilingFan); 7         CeilingFanHighCommand ceilingFanHigh = new CeilingFanHighCommand(ceilingFan); 8         CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan); 9 10         remoteControl.setCommand(0, ceilingFanMedium, ceilingFanOff);11         remoteControl.setCommand(1, ceilingFanHigh, ceilingFanOff);12 13         remoteControl.onButtonWasPushed(0);14         remoteControl.offButtonWasPushed(0);15         System.out.println(remoteControl);16         remoteControl.undoButtonWasPushed();17 18         remoteControl.onButtonWasPushed(1);19         System.out.println(remoteControl);20         remoteControl.undoButtonWasPushed();21     }22 }

最后,我们相同时弄暗灯光、打开音响和电视、设置好DVD,并让热水器开始加温:

代码地址: 

MacroCommand.java:

1 public class MacroCommand implements Command { 2     Command[] commands; 3   4     public MacroCommand(Command[] commands) { 5         this.commands = commands; 6     } 7   8     public void execute() { 9         for (int i = 0; i < commands.length; i++) {10             commands[i].execute();11         }12     }13     14     public void undo() {15         for (int i = commands.length -1; i >= 0; i--) {16             commands[i].undo();17         }18     }19 }

使用宏命令(RemoteLoader.java):

1 public class RemoteLoader { 2  3     public static void main(String[] args) { 4  5         RemoteControl remoteControl = new RemoteControl(); 6  7         //首先创建想要进入宏的命令集合 8         Light light = new Light("Living Room"); 9         TV tv = new TV("Living Room");10         Stereo stereo = new Stereo("Living Room");11         Hottub hottub = new Hottub();12  13         LightOnCommand lightOn = new LightOnCommand(light);14         StereoOnCommand stereoOn = new StereoOnCommand(stereo);15         TVOnCommand tvOn = new TVOnCommand(tv);16         HottubOnCommand hottubOn = new HottubOnCommand(hottub);17         LightOffCommand lightOff = new LightOffCommand(light);18         StereoOffCommand stereoOff = new StereoOffCommand(stereo);19         TVOffCommand tvOff = new TVOffCommand(tv);20         HottubOffCommand hottubOff = new HottubOffCommand(hottub);21 22         //接下来创建两个数组,其中一个用来记录开启命令,另一个用来记录关闭命令,并在数组内放入对应的命令23         Command[] partyOn = { lightOn, stereoOn, tvOn, hottubOn};24         Command[] partyOff = { lightOff, stereoOff, tvOff, hottubOff};25   26         MacroCommand partyOnMacro = new MacroCommand(partyOn);27         MacroCommand partyOffMacro = new MacroCommand(partyOff);28  29         //然后将宏命令指定给我们所希望的按钮30         remoteControl.setCommand(0, partyOnMacro, partyOffMacro);31   32         //最后,按一按按钮,测试一下33         System.out.println(remoteControl);34         System.out.println("--- Pushing Macro On---");35         remoteControl.onButtonWasPushed(0);36         System.out.println("--- Pushing Macro Off---");37         remoteControl.offButtonWasPushed(0);38     }39 }

 

转载于:https://www.cnblogs.com/Trojan00/p/11339539.html

你可能感兴趣的文章
ASP.NET MVC分页实现之改进版-增加同一个视图可设置多个分页
查看>>
关于ASP.NET MVC开发设计中出现的问题与解决方案汇总 【持续更新】
查看>>
关于Entity Framework中的Attached报错的完美解决方案终极版
查看>>
Selenium之Web页面滚动条滚操作
查看>>
组合数据类型练习,英文词频统计实例上
查看>>
Uber回馈开源的一些软件
查看>>
day 3 修改haproxy.cfg 作业
查看>>
UIScrollView —— 缩放实现案例(二)
查看>>
【Qt】Qt Linguist介绍【转】
查看>>
sim usim Uim 区别
查看>>
网页中插入透明Flash的方法和技巧
查看>>
动态内存申请函数选择(realloc、malloc 、alloca、 calloc)
查看>>
获取元素属性get_attribute
查看>>
视觉设计师的进化
查看>>
Python/jquery
查看>>
WPF之Binding
查看>>
【BZOJ】【2132】圈地计划
查看>>
智能机不如功能机待机时间长的原因
查看>>
Redis source code analysis
查看>>
http://web.stanford.edu/~jurafsky/slp3/
查看>>