命令(Command)模式:又称Action模式或者Transaction模式。它属于对象的行为模式。
命令模式把一个请求或者操作封装到一个对象中,于是这些命令可以被:
重复多次
取消
取消后又再重做
命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开(解耦)。命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
命令模式涉及到五个角色,分别为:
客户(Client)角色:创建一个具体命令(ConcreteCommand)对象,并设置命令的接收者。
命令(Command)角色:定义一个给所有命令类的抽象接口,定义了统一的 execute() 接口方法。
具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合;实现 Command 接口,并实现 execute() 方法,负责调用接收者的相应操作。
请求者(Invoker)角色:负责调用由 Client 下达的对象执行请求。
接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法为行动方法。
这只是通用划分,实现时不一定有这些,设计模式只是思维构建的方式,没有一一对应的模板滴。
来看代码:
using System;
using System.Collections.Generic;
namespace CommandPattern
{
public interface ICommand
{
void Execute();
}
/* The Invoker class */
public class Switch
{
private List<ICommand> _commands = new List<ICommand>();
public void StoreAndExecute(ICommand command)
{
_commands.Add(command);
command.Execute();
}
}
/* The Receiver class */
public class Light
{
public void TurnOn()
{
Console.WriteLine("The light is on");
}
public void TurnOff()
{
Console.WriteLine("The light is off");
}
}
/* The Command for turning on the light - ConcreteCommand #1 */
public class FlipUpCommand : ICommand
{
private Light _light;
public FlipUpCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOn();
}
}
/* The Command for turning off the light - ConcreteCommand #2 */
public class FlipDownCommand : ICommand
{
private Light _light;
public FlipDownCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOff();
}
}
/* The test class or client */
internal class Program
{
public static void Main(string[] args)
{
Light lamp = new Light();
ICommand switchUp = new FlipUpCommand(lamp);
ICommand switchDown = new FlipDownCommand(lamp);
Switch s = new Switch();
string arg = args.Length > 0 ? args[0].ToUpper() : null;
if (arg == "ON")
{
s.StoreAndExecute(switchUp);
}
else if (arg == "OFF")
{
s.StoreAndExecute(switchDown);
}
else
{
Console.WriteLine("Argument \"ON\" or \"OFF\" is required.");
}
}
}
}
【注意】:在某些简单业务场景下,可在 ConcreteCommand 类的 execute 方法中直接编写实现代码,而省去接收者(Receiver)类以简化代码。如在本应用场景中省去 Receiver 类的创建。
来看一个实际例子:
在射击游戏中,用户可以自定义快捷键,根据使用习惯来设置快捷键,如“W”键可以设置为“开枪”的快捷键,也可以设置为“前进”的快捷键,可通过命令模式来实现快捷键设置,类图如图5所示:
在图中,ShortcutKey充当请求调用者,在其press()方法中将判断用户按的是哪个按键,再调用命令对象的execute()方法,在具体命令对象的execute()方法中将调用接收者如ShotHandler、GoAheadHandler的action()方法来执行具体操作。在实现时可以将具体命令类类名和键盘按键的键码(Keycode)存储在配置文件中,配置文件格式如下所示:
……
<FunctionMapping keycode="87" commandClass="ShotCommand"/>
<FunctionMapping keycode="38" commandClass="GoAheadCommand"/>
……
如果需要更换快捷键,只需修改键码和具体命令类的映射关系即可;如果需要在游戏的升级版本中增加一个新功能,只需增加一个新的具体命令类,可通过修改配置文件来为其设置对应的按键,原有类库代码无需任何修改,很好地符合开闭原则。
适用性:在软件系统中,行为请求者与行为实现者之间通常呈现一种紧耦合的关系。但在某些场合,比如要对行为进行记录撤销重做事务等处理,这种无法抵御变化的紧耦合是不合适的。这种情况下,使用 command 模式将行为请求者与行为实现者进行解耦。
一个典型的应用场景就是处理命令队列,减少行为请求者和动作执行者的耦合,提高灵活性和降低代码冗余。
另外,对于撤销事务,有两种实现方式:
1) 在execute()方法执行前先存储当前状态,当undo()方法调用时,将当前状态转换为之前存储的状态(简单,保存了完整信息,浪费空间,没必要)。
2) 只保存状态间变化的信息,当execute()或undo()调用时只改变这些存储的信息
参考:
https://zh.wikipedia.org/wiki/%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F
http://www.ibm.com/developerworks/cn/java/design/
http://treelib.com/book-detail-id-59-aid-3471.html
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/kzq_qmi/article/details/47108307