前言:
本章会将封装带入到一个全新的境界,把方法调用封装起来。通过封装方法调用,把运算块包装成形。调用此运算的对象不需要知道事情是如何进行的,只要知道如何使用包装形成的方法来完成它就ok了。
1 现实场景应用
现在有一个遥控器,该遥控器有7个插槽需要编程,可以在每个插槽上放上不同的装置,然后用按钮控制它,这七个插槽具备各自的“开”“关”按钮,还有一个整体用的撤销按钮,会撤销最后一个按钮的动作。
1.1 创建第一个命令对象
1.1.1 定义命令接口
public interfaceCommand { void Execute(); }
1.1.2 实现一个打开灯的命令
publicclass Light //电灯类 { public void On() { System.Console.WriteLine("Light is On !"); } public void Off() { System.Console.WriteLine("Light is Off !"); } } public class LightOnCommand : Command//实现开灯命令 { private Light light; public LightOnCommand(Light light) { this.light = light; } public void Execute() { light.On(); } }
1.1.3 使用命令对象
public class LightControl { privateCommand soft; publicvoid SetCommand(Command cmd) { soft= cmd;//设置命令对象 } publicvoid ButtonWasPressed() { soft.Execute();//调用命令对象的方法 } }
1.1.4 简单测试
LightControl lightControl = new LightControl();//模拟命令的调用者 Lightlight = new Light();//创建一个电灯对象,作为命令的接收者 LightOnCommand lightOnCommand = new LightOnCommand(light);//创建一个命令,并将接受者传递给它 lightControl.SetCommand(lightOnCommand);//将命令传给调用者 lightControl.ButtonWasPressed();//模拟触发按钮
1.2 实现遥控器
public class RemoteControl { Command[] onCommands;//定义打开的命令数组 Command[] offCommands; //定义关闭的命令数组 public RemoteControl() { onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = newNoCommand(); for (int i = 0; i < 7; i++) { onCommands[i] = noCommand;//初始化命令数组(默认设置为无命令) offCommands[i] = noCommand; } } //将命令设置到对应的控制器 public void SetCommand(int index,Command onCommand, Command offCommand) { onCommands[index] = onCommand; offCommands[index] = offCommand; } //触发打开控制器 public void OnButtonWasPressed(int index) { onCommands[index].Execute(); } //触发关闭控制器 public void OffButtonWasPressed(intindex) { offCommands[index].Execute(); } public override string ToString() { StringBuilder str = newStringBuilder(); str.Append(" ------RemoteControl ------ "); for (int i = 0; i <onCommands.Length; i++) { str.Append("[solt" +i + "]" + onCommands[i].GetType().FullName + " " +offCommands[i].GetType().FullName + " "); } return str.ToString(); } }
1.3 实现其他控制器
//关闭电灯命令 public classLightOffCommand : Command { privateLight light; publicLightOffCommand(Light light) { this.light = light; } publicvoid Execute() { light.Off(); } publicvoid Undo() { light.On(); } } //打开电扇命令 public class CeilingFanOnCommand : Command { CeilingFan ceilingFan; intpreSpeed; publicCeilingFanOnCommand(CeilingFan ceilingFan) { this.ceilingFan = ceilingFan; } publicvoid Execute() { ceilingFan.On(); } publicvoid Undo() { ceilingFan.Off(); } } //关闭电扇命令 public classCeilingFanOffCommand : Command { CeilingFan ceilingFan; publicCeilingFanOffCommand(CeilingFan ceilingFan) { this.ceilingFan = ceilingFan; } publicvoid Execute() { ceilingFan.Off(); } publicvoid Undo() { ceilingFan.On(); } } //打开车库门命令 public classGarageDoorOnCommand : Command { GarageDoor garageDoor; publicGarageDoorOnCommand(GarageDoor garageDoor) { this.garageDoor= garageDoor; } publicvoid Execute() { garageDoor.On(); } publicvoid Undo() { garageDoor.Off(); } } //关闭车库门命令 public classGarageDoorOffCommand : Command { GarageDoor garageDoor; publicGarageDoorOffCommand(GarageDoor garageDoor) { this.garageDoor = garageDoor; } publicvoid Execute() { garageDoor.Off(); } publicvoid Undo() { garageDoor.On(); } } //打开CD命令 public classStereCDOnCommand : Command { StereCDstereCD; publicStereCDOnCommand(StereCD stereCD) { this.stereCD = stereCD; } publicvoid Execute() { stereCD.On(); } publicvoid Undo() { stereCD.Off(); } } //关闭CD命令 public classStereCDOffCommand : Command { StereCDstereCD; publicStereCDOffCommand(StereCD stereCD) { this.stereCD = stereCD; } publicvoid Execute() { stereCD.Off(); } publicvoid Undo() { stereCD.On(); } }
1.4简单测试
RemoteControlremoteControl = new RemoteControl (); CeilingFan ceilingFan = newCeilingFan("Living Room");//创建电扇对象 GarageDoor garageDoor = newGarageDoor();//创建车库门对象 StereCD stereCD = new StereCD();//创建CD对象 Light light = new Light();//创建电灯对象 LightOnCommand lightOnCommand = newLightOnCommand(light);//创建开灯命令 LightOffCommand lightOffCommand =new LightOffCommand(light); //创建关灯命令 CeilingFanOnCommandceilingFanOn = new CeilingFanOnCommand(ceilingFan); //创建开电扇命令 CeilingFanOffCommand ceilingFanOff= new CeilingFanOffCommand(ceilingFan);//创建关电扇命令 GarageDoorOnCommand garageDoorOn =new GarageDoorOnCommand(garageDoor);//创建打开电扇命令 GarageDoorOffCommand garageDoorOff= new GarageDoorOffCommand(garageDoor);//创建关闭电扇命令 StereCDOnCommand stereCDOn = newStereCDOnCommand(stereCD);//创建打开CD命令 StereCDOffCommand stereCDOff = newStereCDOffCommand(stereCD);//创建关闭CD命令 remoteControl.SetCommand(0,lightOnCommand, lightOffCommand);//将电灯命令设置到对应的控制器上 remoteControl.SetCommand(1,ceilingFanOn, ceilingFanOff); //将电灯命令设置到对应的控制器上 remoteControl.SetCommand(2,garageDoorOn, garageDoorOff); //将车库门命令设置到对应的控制器上 remoteControl.SetCommand(3,stereCDOn, stereCDOff); //将CD命令设置到对应的控制器上 remoteControl.OnButtonWasPressed(0); remoteControl.OffButtonWasPressed(0); remoteControl.OnButtonWasPressed(1); remoteControl.OffButtonWasPressed(1); remoteControl.OnButtonWasPressed(2); remoteControl.OffButtonWasPressed(2); remoteControl.OnButtonWasPressed(3); remoteControl.OffButtonWasPressed(3);
1.5 实现撤销命令
1.5.1 修改命令接口,新增Undo()方法
public interfaceCommand { void Execute(); void Undo();//新增撤销方法 }
1.5.2 修改命令,实现Undo方法
public class LightOnCommand : Command { private Light light; public LightOnCommand(Light light) { this.light = light; } public void Execute() { light.On(); } public void Undo()//实现Undo方法 { light.Off(); } }
其他类都依次修改
1.5.3 修改RemoteControl类,新增Command对象记录上一步操作
public class RemoteControlWithUndo { Command[] onCommands; Command[] offCommands; CommandundoCommand;//创建Command对象用来记录上一步执行的命令 publicRemoteControlWithUndo() { onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = new NoCommand(); for(int i = 0; i < 7; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } undoCommand = noCommand; } ……… …….. publicvoid OnButtonWasPressed(int index) { onCommands[index].Execute(); undoCommand = onCommands[index];//记录打开命令 } publicvoid OffButtonWasPressed(int index) { offCommands[index].Execute(); undoCommand = offCommands[index];//记录关闭命令 } publicvoid UndoButtonWasPressed()//执行撤销 { undoCommand.Undo(); } …… …… }
1.6 宏命令
让遥控器具有Party模式,即一个按键能够同步调用多个命令,这就是所谓的宏命令。
public class MacroCommand : Command { Command[] commands;//定义命令数组,用来接收传入的命令 public MacroCommand(Command[] commands) { this.commands = commands; } public void Execute()//批量处理多个命令(即初始化的时候传入的命令数组) { for (int i = 0; i <commands.Length; i++) commands[i].Execute(); } public void Undo() { for (int i = 0; i <commands.Length; i++) commands[i].Undo(); } }
2 定义命令模式
命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
定义命令模式类图:
3 命令模式的更多用途
3.1 队列请求
命令将运算块打包,然后将它传来传去,即使在命令对象创建许久之后,运算依然可以被调用,甚至可以在不同的线程中被调用,我们可以利用这样的特性衍生一下应用,比如:日常安排、线程池、工作队列等。
工作队列类和进行计算的对象之间完全是解耦的。它们只要是实现命令模式的对象就可以放到队列里边,当线程可用时就调用此对象的Execute()方法。
3.2 日志请求
某些应用需要我们将所有动作记录到日志中,并在系统死机之后,重新调用这些动作恢复到之前的状态,命令模式就能够支持这一点。
有许多调用大型数据结构的动作应用无法再每次改变发生时被快速的存储,通过使用记录日志,我们可以将上次检查点之后的所有操作记录下来,如果系统出现状况,可以从检查点开始应用这些操作。对于更高级的应用而言,这些技巧可以被扩展应用到事务处理中,即一整群操作必须全部完成,或者没有进行任何操作。
3 总结
l 命令模式将发出请求的对象和执行请求的对象解耦。
l 被解耦的两者之间是通过命令对象进行沟通的,命令对象封装了接收者和一个或一组动作。
l 调用者通过调用命令对象的Execute()发出请求,会使得接收者的动作被调用。
l 调用者可以接收命令当做参数,甚至在运行时动态进行。
l 命令可以支持撤销,做法是实现一个undo()方法来回到Execute()被执行之前的状态。
l 宏命令是命令的一种简单的延伸,允许调用多个命令,宏方法也可以支持撤销。
l 命令也可以用来实现日志和事务系统。