去小摊上吃羊肉串和去店里吃感觉是不一样的。小摊上如果人太多的话,老板容易分不清楚谁是谁,造成分发错误,收钱错误,烤肉质量不过关等。但是在店里,有服务员记录好菜单,然后直接给烤肉的人制作就可以了,修改也不难,可以说是很方便了。

小摊上客户与烤羊肉老板的紧耦合使得容易出错,容易混乱,容易挑剔。这其实就是行为请求者与行为实现者的紧耦合。所以对请求排队或记录请求日志,以及支持可撤销的操作时,紧耦合是不好的。

那么如何做呢?写一个烤肉的程序,  我们要加一个服务员类,把烤肉串类当中的方法,分别写成多个命令类,那么它们就可以被服务员类请求了。可以泛化出一个抽象类,让服务员只管对抽象的命令发号施令就可以了,具体是什么命令,即是烤什么,由客户来决定吧。

 \"\"  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Command
{
    //抽象命令
    abstract class Command
    {
        protected Barbecuer receiver;
        //抽象命令类,只需要确定 烤羊肉串者是谁
        public Command(Barbecuer receiver)
        {
            this.receiver = receiver;
        }
        //执行命令
        public abstract void ExcuteCommand();
    }
    //烤羊肉串命令
    class BakeMuttonCommand : Command
    {
        public BakeMuttonCommand(Barbecuer receiver)
            :  (receiver)
        {
        }
        //具体命令类,执行命令时,执行具体的行为
        public override void ExcuteCommand()
        {
            receiver.BakeMutton();
        }
    }
    //烤鸡翅命令
    class BakeChickenWingCommand : Command
    {
        public BakeChickenWingCommand(Barbecuer receiver)
            :  (receiver)
        {
        }

        public override void ExcuteCommand()
        {
            receiver.BakeChickenWing();
        }
    }
    //烤羊肉者
    class Barbecuer
    {
        //烤羊肉
        public void BakeMutton()
        {
            Console.WriteLine(\"烤羊肉串!\");
        }
        //烤鸡翅
        public void BakeChickenWing()
        {
            Console.WriteLine(\"烤鸡翅!\");
        }
    }
    //服务员
    class Waiter
    {
        private Command command;
        //设置订单
        public void SetOrder(Command command)
        {
            this.command = command;
        }
        //通知执行
        public void Notify()
        {
            command.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.Notify();
            girl.SetOrder(bakeMuttonCommand2);
            girl.Notify();
            girl.SetOrder(bakeChickenWingCommand1);
            girl.Notify();

            Console.Read();
        }
    }
}

这份代码还是有几个问题,并不是用户点一个菜,服务员就通知厨房去做一个,应该是点完烧烤后,服务员一次通知制作。如果此时鸡翅没了,不应该由客户来判断是否还有,应该是服务员或烤肉串的老否决这个请求。客户到底点了哪些烧烤与饮料,是需要记录日志的,以便收费,也包括后期的统计。客户完全可能因为点的肉串太多而考虑取消一些还没有制作的肉串。

\"\"

//服务员
    class Waiter
    {
        private IList<Command> orders = new List<Command>();
        //设置订单
        public void SetOrder(Command command)
        {
            if (command.ToString() == \"CommandPattern.BakeChickenWingCommand\")
            {
                Console.WriteLine(\"服务员:鸡翅没有了,请点别的烧烤.\");
            }
            else
            {
                orders.Add(command);
                Console.WriteLine(\"增加订单: \" + command.ToString() + \"时间:\" + DateTime.Now.ToString());
            }
        }

        public void Cancle(Command command)
        {
            orders.Remove(command);
            Console.WriteLine(\"取消订单: \" + command.ToString() + \"时间:\" + DateTime.Now.ToString());
        }

        //通知执行
        public void Notify()
        {
            foreach (Command command in orders)
            {
                command.ExcuteCommand();
            }
        }
    }

命令模式:讲一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

命令模式的优点:第一,它能较容易的设计一个命令队列;第二,在需要的情况下,可以较容易的将命令记入日志,第三,允许接受请求的一方决定是否要否决请求。

第四,可以容易的实现对请求的撤销和重做;第五,由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易,命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

但是如果不清楚是否需要这个功能时,不要实现命令模式。敏捷开发原则告诉我们,不要为代码添加基于猜测的,实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。

收藏 打印