夏日的夜晚,大街上总是热闹非凡,人们都喜欢约三五好友去吃烧烤喝啤酒,所谓吃着烤串儿。还唱着歌儿,那感觉就一个字——爽。
在街上溜达,突然发现一个烧烤摊人不多。于是你就上前对老板说:来10串羊肉串,3串鸡翅……。说完之后,你就站在边上等,这时你发现。人越来越多,之间大家七嘴八舌跟老板说自己的要求,明显看到老板有些手足无措。首先老板要记住谁先来的谁后来的。谁给钱了谁没给钱。谁不要放辣椒……等等,太多的问题接踵而至。并且那么多人都在那里盯着老板烤串,这样就引起了挑剔,哪一串多了。哪一串少了什么的,真的非常是混乱。
于是你你选择了去不远处的一家烧烤店。坐下后。直接给服务员说了自己要吃什么吃多少。服务员在订单上记下之后。直接将订单送往后厨。然后招待其它刚进来的顾客。作为一个程序猿的你,此时此刻陷入了沉思……
今晚这两种情况恰好说明了一个软件设计中设计模式,就是标题提到的命令模式,我们来看看怎样将现实情况映射到命令模式。
首先第一种情况,烧烤摊的经营模式我们能够对其进行抽象。得到例如以下类图:
结构图非常easy,就两个类,可是正由于简单。其隐含的问题就太多了.单纯地从编程的角度看,客户是"行为的请求者",而烤串老板即为"行为的实现者",上面的那种情况下,两者的耦合性非常高,这在编程中是大忌,并且假设行为请求者要撤销自己的请求,这样的紧耦合的关系实在是非常麻烦.那么怎样改进呢?
我们再来看看对烧烤店的情况进行的抽象,其结构图例如以下:
从上面的类图中能够看到,我们增加了一个服务员类,从而使客户和烤串者解耦合,服务员一方面要从客户那里接受订单,还有一方面就是通知后厨师傅运行客户的要求.我们结合详细的代码实现来说明类图中的关系时怎样在代码中实现的,至于第一个类图的代码实现因为比較简单,就留给读者去实现吧,这里不再赘述.
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ConsoleApplication1{ //client代码实现 class Program { static void Main(string[] args) { //开店前的准备 Barbecuer boy = new Barbecuer(); Command bakeMuttonCommand1 = new BakeMuttonCommand(boy); Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy); Waiter girl = new Waiter(); //开门营业,顾客点菜 girl.SetOrder(bakeMuttonCommand1); girl.SetOrder(bakeChickenWingCommand1); //点菜完成。通知厨房做菜 girl.Notify(); Console.Read(); } } //烤串者类 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 Command command; // public void SetOrder(Command command) // { // this.command = command; // } // public void Notify() // { // command.ExcuteCommand(); // } //} //改写后的服务员类******************************************************************************* public class Waiter { private IListorders = new List (); //设置订单 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(); } } }}
首先我们看服务员类和抽象命令类之间是聚合关系,为什么呢?非常easy就是服务员能够接受各种各样的命令,而服务员和各个命令类分别是相互独立的,因此是一种聚合关系.代码中我们在服务员类中声明一个存放详细命令的容器(List类型),然后将详细的命令类的实例作为其方法的參数,以便对其进行操作.
其次是抽象命令类和详细的命令类之间的继承关系,这个非常easy应该不用多少了,就是在创建详细类的时候在类名的后面加上:后跟上父类类名(C#语言).
再者是烤肉串者和详细命令类之间的依赖关系,这个我们在定义详细类的时候,类的构造方法的參数设为烤串者,这样就表达了,详细命令类的运行要依赖于详细的实现者,然后在方法体中通过烤串者类的实例对象调用烤串者的方法,完毕详细命令要求的行为和操作.
最后是client类和服务员类以及烤肉串者类之间的关联关系,这样的关系是通过在client类中加入服务员类和烤串者类的引用来表现的,就是在client的代码中实例化一个烤串者类的详细对象和一个服务员类的详细对象.
以上就是类图中各种关系在代码中的实现方法,我们能够用这种方式去分析其它设计模式的结构图中的各种关系,这样我们在类图和代码之间的转换就会游刃有余,胸有成竹了.
最后给出命令模式的定义:将一个请求封装为一个对象,从而使你能够用不同的请求对客户进行參数化;对请求排队或记录请求日志,以及支持可撤销的操作。我们能够对比上面的样例来理解命令模式的定义,这样就能够形象的理解命令模式究竟是怎么一回事了。