您的位置:首页 > 其它

设计模式 -- 命令模式(command)

2013-04-10 17:10 323 查看
1、什么是command命令模式?

command命令模式是“封装方法调用”的一个模式,通过封装方法调用,把运算块封装成形。那么调用此运算的对象就不必深究事情是如何进行的,只需要知道可以使用这个包装成型的方法来完成它就可以了。因为它把运算封装,而不关心具体对象和实现,因此命令模式在我们的系统中有很多应用,如队列请求,日志请求,撤消操作等等。

“封装方法调用”也可以这样理解,命令模式把“命令的请求者”和“命令的执行者”解耦。这样一来,当命令请求者在请求一个命令时不必关心执行此命令的是哪个对象,这个对象怎么执行这个命令这些细节问题。要实现这两者之间的解耦,必然的要多出一个“中间处理者”——命令,我们就把方法调用封装在这个结构里。更通俗的来讲一个例子,比如你去餐馆吃饭,服务员招待你,你点你要的东西,服务员会把这些内容记录成一个单子,这个单子就是命令,为什么呢?因为大厨必须根据这个单子来做菜,否则你是不会满意的。如果你认同我这种说法,那么就说明,你承认了“命令请求者”(你)和“命令执行者”之间解耦了。因为这之间,你不知道大厨是谁,也不知道他是怎么做的菜。

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

2、如何实现“命令模式”模式?

实现命令模式,需要有一个“命令”接口(command),这个接口定义了一些列的命令,比如execute,undo等。你可以实现这个接口,那么你就有了一些具体的命令了。这个“命令”(command )接口就是我刚才说的“中间处理者”。命令的请求者在请求命令时,只需要指明需要的具体命令,而这个具体命令就必须维护自己的命令执行者,这样三者之间就发生了联系。看看下面的类图:



来解释这个类图:

Command:(可以是接口或者抽象类)为所有命令声明了接口。调用命令对象的execute方法就可以让接受者进行相关动作。

Concretecommand:定义了动作和接收者之间的绑定关系。调用者只要调用execute就可以发出请求,然后由Concretecommand调用接收者的一个或多个动作。

Client:(通常是我们的测试程序)负责创建一个ConcreteCommand,并设置其接收者。

Invoker:调用者,他要维护自己的命令对象,并在某个时间点调用命令对象的execute方法,将请求付诸实行。

Receiver:接收者,它知道如何进行必要的工作来实现这个请求。

3、具体情境分析

遥控器有很多按钮,我们打算将遥控器的每个按钮对应到一个命令,这样让遥控器成为“调用者”。当按下按钮,相应命令对象的execute方法会执行被调用,其结果就是,接收者(如电灯,车库门,吊扇)的动作被调用。

类图:







代码:

//命令接收者 -- 天花板
public class CeilingFan
{
public string position;

public CeilingFan(string aPosition)
{
this.position = aPosition;
}

public void on()
{
Console.WriteLine("the ceilingfan is on");
}

public void off()
{
Console.WriteLine("the ceilingfan is off");
}

}


//命令接收者 -- 电灯类
public class Light
{
public string position;//指明灯的位置

public Light(string aName)
{
this.position = aName;
}

public void on()
{
Console.WriteLine("电灯打开了");
}

public void off()
{
Console.WriteLine("电灯关上了");
}
}


//命令接收者 -- 车库门
public class GarageDoor
{
public void up()
{
Console.WriteLine("门向上开启");
}

public void down()
{
Console.WriteLine("门向下关闭");
}

public void stop()
{
Console.WriteLine("门停止");
}
public void lightOn()
{
Console.WriteLine("打开车库门");
}
public void lightOff()
{
Console.WriteLine("车库门关上");
}
}


//命令接收者 -- 音响类
public class Stereo
{
public string position;

public Stereo(string aposition)
{
this.position = aposition;
}

public void on()
{
Console.WriteLine("音响打开了");
}

public void off()
{
Console.WriteLine("音响关上了");
}

public void setCD()
{
Console.WriteLine("CD就绪");
}

public void setVolume(int num)
{
Console.WriteLine("音响有");
Console.WriteLine(num);
Console.WriteLine("首歌");
}

public void setRadio()
{
Console.WriteLine("音响录音");
}
}


//命令请求者 -- 遥控器
public class RemoteControl
{
public Command[] onCommands;//7个开命令
public Command[] offCommands;//7个关命令
public Command undoCommand;//前一个命令将被记录在这里

public RemoteControl()
{
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;//一开始没有所谓的“前一个”命令
}

//参数:插槽位置 开命令 关命令。这些命令记录在开关数组中对应插槽的位置上
public void setCommand(int slot, Command onComand, Command offCommand)
{
onCommands[slot] = onComand;
offCommands[slot] = offCommand;
}

public void onButtonWasPushed(int slot)
{
if (onCommands[slot] != null)
{
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
}

public void offButtonWasPushed(int slot)
{
if (offCommands[slot] != null)
{
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
}

public void undoButtonWasPressed()
{
undoCommand.undo();
}
public string toString()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("\n------ Remote  Control ------\n");
for (int i = 0; i < 7;i++ )
{
stringBuilder.Append("[slot" + i + "]" + onCommands[i] + "      " + offCommands[i] + "\n");
}
return stringBuilder.ToString();
}
}


//命令接口
public interface Command
{
void execute();
void undo();//撤销操作
}

//打开电灯命令类
public class LightOnCommand:Command
{
Light light;

public LightOnCommand(Light alight)
{
this.light = alight;
}

public void execute()
{
light.on();
}

public void undo()
{
light.off();
}
}

//关闭电灯命令类
public class LightOffCommand:Command
{
public Light light;

public LightOffCommand(Light alight)
{
this.light = alight;
}

public void execute()
{
light.off();
}

public void undo()
{
light.on();//execute是关闭电灯,那么undo就是打开电灯
}
}


其他的命令类都是大同小异的,在此省略了。

测试代码:

static void Main(string[] args)
{
//命令调用者
RemoteControl remoteControl = new RemoteControl();
//命令接收者
Light livingRoomLight = new Light("Living  Room");
Light kitchenLight = new Light("Kitchen");
CeilingFan ceilingfan = new CeilingFan("Living  Room");
GarageDoor garagedoor = new GarageDoor();
Stereo stereo = new Stereo("Living  Room");

//命令
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);

LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);

CeilingFanOnCommand ceilingFanOn = new CeilingFanOnCommand(ceilingfan);
CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingfan);

GarageDoorOpenCommand garageDoorOpen = new GarageDoorOpenCommand(garagedoor);
GarageDoorDownCommand garageDoorDowm = new GarageDoorDownCommand(garagedoor);

StereoOnWithCDCommand stereoOnWithCD = new StereoOnWithCDCommand(stereo);
StereoOffCommand stereoOff = new StereoOffCommand(stereo);

//将命令对应到调用者的接口上
remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
remoteControl.setCommand(3, stereoOnWithCD, stereoOff);
remoteControl.setCommand(4, garageDoorOpen, garageDoorDowm);

string str = remoteControl.toString();
Console.WriteLine(str);

remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);

remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);

remoteControl.onButtonWasPushed(2);
remoteControl.offButtonWasPushed(2);

remoteControl.onButtonWasPushed(3);
remoteControl.offButtonWasPushed(3);

remoteControl.onButtonWasPushed(4);
remoteControl.offButtonWasPushed(4);

//以下是撤销操作
Console.WriteLine("------------------\n");
remoteControl.undoButtonWasPressed();
remoteControl.offButtonWasPushed(4);
remoteControl.onButtonWasPushed(4);
remoteControl.undoButtonWasPressed();

Console.ReadKey();

}
}


呵呵,好了,也许你发下了,我们这里的撤销操作只是一步撤销,而往往我们希望能够连续撤销而回到很久以前的一个状态去。实现多层次撤销,我们可以使用堆栈记录操作的每一个命令,然后,不管什么时候按下按钮,你都可以从堆栈中取出最上层命令,然后调用它的undo()方法。我们可以使用宏命令,将命令放在一个一个集合里,这样就可实现这个遥控器的“Party”模式,开让一个按钮一次性控制多个命令。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: