2011/06/26

[Design Patterns] 命令模式(Command)

型態:行為模式(Behavioral Pattern)。
定義:將一個請求封裝為一個物件,讓你可用不同的請求對客戶進行參數化;對請求排隊或紀錄請求日誌,以及支援可取消的操作。
使用時機: 當需要如取消/恢復操作等功能。

結構圖:來源
 2011-6-26 上午 10-50-25

假設有一家燒烤店,我們想用程式的方式來實現整個生意的運作方式,比方說客人跟服務生點餐,
並且可以隨時取消,服務生將單子送出,然而負責烤肉的廚師,就會收到單子(命令),並且處理食物。
結構圖:
2011-6-26 上午 10-50-25

實現程式碼一:
抽象命令
//抽象命令
    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();
        }
    }

用戶端實現

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();

        }
    }

以上設計似乎不錯,但是考慮實際狀況,我們發現以下幾點情形。
  • 第一:服務生應該是點完燒烤後,一次性的通知製作,而不是點一樣,服務生就通知廚房做一個。
  • 第二:如果有東西沒了,應該是由服務生或烤肉串者來否決這個請求,而不是客戶。
  • 第三:客戶點了哪些東西是需要被記錄的,最後結帳可以查詢。
  • 第四:客戶隨時可以考慮還沒有製作的肉串。
經過以上幾點,重寫程式碼

實現程式碼二:
服務生類別

 //服務生
    public class Waiter {
        //增加存放具體命令的容器
        private IList<Command> orders = new List<Command>();

        //設置訂單
        public void SetOrder(Command command) {
            //在客戶提出請求時,對沒貨的收烤進行回絕
            if (command.ToString() == "Command.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();

        }
    }


輸出結果:
2011-06-26_234748

最後,命令模式的作用
  • 容易設計一個命佇列。
  • 在需要的情況下,可以較容易地將命令記入日誌。
  • 允許接收請求的一方決定是否要否決請求。

0 Comments:

張貼留言