目录
动机(Motivation)
在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合--比如需要对行为进行“记录、撤销/重做(undo/redo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。
在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
意图(Intent)
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对其排队或者记录请求日志,支持可撤销的操作。
结构(Structure)
Command:定义命令的接口,声明执行的方法。
ConcreteCommand:命令接口实现对象,是“虚”的实现,通常会有接受者,并调用接受者的功能来完成命令要执行的操作。
Receiver:接受者,真正执行命令的对象。任何类都可能成为一个接受者,只要它能够实现命令要求实现的相应功能。
Invoker:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
Client:创建具体的命令对象,并且设置命令对象的接受者。这不是常规意义上的客户端,而是在组装命令对象和接受者,真正使用命令的客户端是从Invoker来触发执行。
Command模式的几个要点
1.Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言中,常见的实现手段是“将行为抽象为对象”。
2.实现Command接口的具体命令对象ConcereteCommand有时候根据需要可能会保存一些额外的状态信息。
Exampel
背景:街边的烧烤摊很多都是一个人边做边卖,把消费者看做是命令的请求者(发出购买烤串的命令),烤串的人看作是命令的接受者(接收购买烤串命令进行烧烤),这就是一个紧耦合的关系。
而一般正规的烧烤店,都是由服务员负责顾客的点餐,顾客点餐之后,服务员会通知后厨进行制作。(这就将命令的请求者和命令的接受者解耦)。
下面是具体的代码实现
烤肉串者:对应于结构图中的Receiver类,也就是命令请求的实际执行者。在本例中,就是后厨的大厨们,为我们烤美味的羊肉串和鸡翅。
public class Barbecuer
{
//烤羊肉
public void BakeMutton()
{
Console.WriteLine("烤羊肉串!");
}
//烤鸡翅...
}
抽象命令类:对应于结构图中的Command,定义命令的接口,声明执行的方法。在本例中,抽象命令类,只需要确定“烤肉串者”是谁,而不需要知道Barbecuer里面的具体行为(方法)。
public abstract class Command
{
protected Barbecuer receiver;
//抽象命令类,只需要确定“烤肉串者”是谁
public Command(Barbecuer receiver)
{
this.receiver = receiver;
}
//执行命令
abstract public void ExecuteCommand();
}
烤羊肉串命令和烤鸡翅命令类:对应于结构图中的ConcreteCommand,实现Command的命令接口,并且执行receiver的具体的行为。在本例中,具体命令类中要执行大厨的具体行为(烤羊肉串,烤鸡翅)。
//烤羊肉串命令
class BakeMuttonCommand : Command
{
public BakeMuttonCommand(Barbecuer receiver) : base(receiver) { }
//具体命令类,执行命令时,执行具体的行为
public override void ExecuteCommand()
{
receiver.BakeMutton();
}
}
//烤鸡翅命令类
class BakeChickenWingCommand : Command
{
//代码同上
}
服务员类:对应于结构图中的Invoker类,触发命令对象的执行。在本例中,服务员给后厨要求后厨按订单制作,并且记录顾客对订单的增加和撤销情况,以备结账使用。
public class Waiter
{
//存放具体命令的容器
private IList<Command> orders = new List<Command>();
//设置订单
public void SetOrder(Command command)
{
//在客户提出请求时,对没货的烧烤进行回绝
if (command.ToString()=="实例.BakeChickenWingCommand")
{
Console.WriteLine("服务员:鸡翅没有了,请点别的烧烤");
}
else
{
orders.Add(command);
//记录客户所点的烧烤的日志,以备算账收钱
Console.WriteLine("增加订单:"+command.ToString()+"时间:"+DateTime.Now.ToString());
}
}
//取消订单
public void CancelOrder(Command command)
{
orders.Remove(command);
Console.WriteLine("取消订单:"+command.ToString()+"时间:"+DateTime.Now.ToString());
}
//通知执行
public void Notify()
{
//根据用户点好的烧烤订单通知厨房制作
foreach (Command cmd in orders)
{
cmd.ExecuteCommand();
}
}
}
客户端代码:创建具体的命令对象,并且设置命令对象的接受者。在本例中,在开店前设置了命令对象和命令对象的接受者Barbecuer。
//开店前准备
Barbecuer boy = new Barbecuer();
Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
Waiter girl = new Waiter();
//开门营业,顾客点菜
girl.SetOrder(bakeMuttonCommand1);
girl.SetOrder(bakeMuttonCommand2);
girl.SetOrder(bakeChickenWingCommand1);
//点菜完毕,通知厨房
girl.Notify();
如有问题,欢迎指正~~~~