简介
命令模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式。
书中实例
难得大鸟要带小菜去吃羊肉串,两个人走到一个烧烤摊,只见烤肉是老板,端盘是老板,收账是老板,一个人把能干的都干了,结果呢?因为排队的客人很多,老板又忙,谁先谁后老板已然记不清,辣和不辣混在一起,插队现象更是直接让烧烤队吵了起来。咱们还是换一家吧,去前面的烧烤店,有专门负责烤肉的师傅,收钱的老板,点餐送餐的服务员,安全放心还不出岔子。如果程序只是粘贴复制,很可能写成烧烤摊,到时候就得不偿失了。今天我们来学习可以把烧烤摊变成烧烤店的模式——命令模式。
命令模式实现程序
//烤肉串者 public class Barbecuer { //烤羊肉 public void BakeMutton() { Console.WriteLine("烤羊肉串"); } //烤鸡翅 public void BakeChickenWing() { Console.WriteLine("烤鸡翅!"); } }
//抽象命令类 public abstract class Command { protected Barbecuer receiver; public Command(Barbecuer receiver) { this.receiver = receiver; } //执行命令 abstract public void ExcuteCommand(); }
//具体命令-烤肉串 class BakeMuttonCommand : Command { public BakeMuttonCommand(Barbecuer receiver) : base(receiver) { } public override void ExcuteCommand() { receiver.BakeMutton(); } }
//具体命令-烤鸡翅 class BakeChickenWingCommand : Command { public BakeChickenWingCommand(Barbecuer receiver) : base(receiver) { } public override void ExcuteCommand() { receiver.BakeChickenWing(); } }
//服务员 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.ExcuteCommand(); } } }
//客户端 class Program { static void Main(string[] args) { //开店前的准备 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(); Console.Read(); } }
组成类图
关系介绍
命令类(Command)是具体命令类(ConcreteCommand)的父类。具体命令类和接受者类(Receiver)之间是关联关系。调用者类(Invoker)和命令类之间是聚合关系,命令者类拥有调用者类。客户端与接受者类,调用者类之间是依赖关系,客户端依赖于后两者。
命令模式的构成
Command(命令类):用来声明执行操作的接口
ConcreteCommand(具体命令类):将一个接受者对象绑定于一个动作,调用接受者相应的操作,以实现Execute
Invoker(调用者类):要求该命令执行这个请求
Receiver(接受者类):知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接受者
命令模式的优点
1.命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分隔开。
2.它能较容易地设计一个命令队列。
3.在需要的情况下,可以较容易地将命令计入日志
4.允许接收请求的一方决定是否要否决请求
5.由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类和容易。
使用场景
1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2.系统需要在不同的时间指定请求、将请求排队和执行请求。
3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
4.系统需要将一组操作组合在一起,即支持宏命令。