設計模式(五):命令模式

K戰神發表於2016-01-07

 

一、定義

 將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作.

我覺得可以這樣:將一個請求封裝為一個物件,對請求排隊或記錄請求日誌,以及支援可撤銷的操作;從而使你可用不同的請求對客戶進行引數化;

二、例項:一個遙控皮膚,兩臺小家電。一個皮膚控制兩臺小家電

2.1  不用命令模式實現

命令發起者:一塊遙控器

命令接收者:一堆小家電

小家電介面:

 public interface ISmallAppliance
    {
        int Appid { get; set; }
        void Open();
        void Off();
    }

 電器:

public class Purifier : ISmallAppliance
    {
        public int Appid { get; set; }
        public Purifier(int appid)
        {
            Appid = appid;
            Console.WriteLine("淨化器: {0}", Appid);
        }
        public void Open()
        {
            Console.Write(" 啟動【淨化器】成功.");
        }
        public void Off()
        {
            Console.Write(" 關閉【淨化器】成功.");
        }
    }

    public class Kettle : ISmallAppliance
    {
        public int Appid { get; set; }
        public Kettle(int appid)
        {
            Appid = appid;
            Console.WriteLine("電水壺: {0}", Appid);
        }

        public void Open()
        {
            Console.Write(" 啟動【電水壺】成功.");
        }
        public void Off()
        {
            Console.Write(" 關閉【電水壺】成功.");
        }
    }

遙控皮膚:

 public class CommandPannel
    {
        List<ISmallAppliance> apps = new List<ISmallAppliance>();        
        public void Sign(ISmallAppliance _sa)
        {
            if (!apps.Contains(_sa))
                apps.Add(_sa);
        }
        public void OpenCommand(int appid)
        {
            ISmallAppliance app = apps.Where(q => q.Appid == appid).FirstOrDefault();
            app.Open();
        }
        public void OffCommand(int appid)
        {
            ISmallAppliance app = apps.Where(q => q.Appid == appid).FirstOrDefault();
            app.Off();
        }
    }

客戶端:

            //客廳一臺電水壺
            Command.ISmallAppliance kettle_1 = new Command.Kettle(1);
            //廚房一臺電水壺
            Command.ISmallAppliance kettle_2 = new Command.Kettle(2);
            //臥室一臺淨化器
            Command.ISmallAppliance purifier_1 = new Command.Purifier(100);

            List<Command.ISmallAppliance> smallAppliances = new List<Command.ISmallAppliance>();
            smallAppliances.Add(kettle_1);
            smallAppliances.Add(kettle_2);
            smallAppliances.Add(purifier_1);

            //遙控器
            Command.CommandPannel cp = new Command.CommandPannel(smallAppliances);
            //開啟:kettle_1
            cp.OpenCommand(kettle_1.Appid);
            //關閉:purifier_1
            cp.OffCommand(purifier_100.Appid);

由上可知:

命令的發出者—遙控皮膚 和 命令的接收者—註冊的各個小家電.

命令發出者和命令接收者是緊耦合的:關閉和開啟需要傳遞具體的電器ID。

---------------------------------------------------割----------------------------------------------------

2.2、命令模式實現

 重溫一下定義,其實每個設計模式的定義很重要,很多都限定了使用場景。

 定義:將一個請求封裝為一個物件,對請求排隊或記錄請求日誌,以及支援可撤銷的操作;從而使你可用不同的請求對客戶進行引數化;

我們將定義分三個層次解讀:

2.2.1  將一個請求封裝為一個物件. 

即:我們將命令抽象,單獨拿出來封裝為物件。

也就是 命令模式 三大要素之一:  Command  (命令載體)

原來:遙控器有開按鈕和關閉按鈕。

 public class CommandPannel
    {
        List<ISmallAppliance> apps = new List<ISmallAppliance>();        
        public void Sign(ISmallAppliance _sa)
        {
            if (!apps.Contains(_sa))
                apps.Add(_sa);
        }
        public void OpenCommand(int appid)
        {
            ISmallAppliance app = apps.Where(q => q.Appid == appid).FirstOrDefault();
            app.Open();
        }
        public void OffCommand(int appid)
        {
            ISmallAppliance app = apps.Where(q => q.Appid == appid).FirstOrDefault();
            app.Off();
        }
    }

現在:我們需要進行抽象,將命令抽象出來,像這樣 :當然每個命令指定了執行者 protected SmallAppliance sa;這個合乎實際,每個命令肯定針對具體的電器。

 abstract public class Commands
    {
        protected SmallAppliance sa;
        public Commands(SmallAppliance _sa)
        {
            sa = _sa;
        }
        abstract public void Do();
    }
    public class OpenCommand : Commands
    {
        public OpenCommand(SmallAppliance _sa) : base(_sa)
        {
        }
        override public void Do()
        {
            sa.Open();
        }
    }
    public class OffCommand : Commands
    {
        public OffCommand(SmallAppliance _sa) : base(_sa)
        {
        }
        override public void Do()
        {
            sa.Off();
        }
    }

 

2.2.2  對請求排隊或記錄請求日誌,以及支援可撤銷的操作

這是說的誰?遙控器 — 命令模式 三大要素之一:Invoker(請求處理者) 

不過現在的遙控器,要擁有下面三個功能:請求排隊、記錄日誌、可撤銷

public class Pannel
    {
        List<Commands> cmds = new List<Commands>();
        public Pannel()
        {
            Console.WriteLine("遙控器.");
        }
        public void Send(Commands cmd)
        {
            //請求排隊
            cmds.Add(cmd);
            //記錄日誌
            Console.WriteLine("傳送命令時間 : {0}", DateTime.Now);
        }
        public void CancleCmd(Commands cmd)
        {
            //可撤銷           
            cmds.Remove(cmd);
            //記錄日誌
            Console.WriteLine("取消命令時間 : {0}", DateTime.Now);
        }

        public void Execute()
        {
            if (cmds != null && cmds.Count > 0)
            {
                foreach (var cmd in cmds)
                {
                    cmd.Do();
                }
            }
        }
    }

這就是我們改造之後的遙控器,簡單粗暴的直接執行命令,不管誰的。來者不拒。

2.2.3 命令模式 三大要素之一:Receiver(接收者)

  abstract public class SmallAppliance
    {
        abstract public int ID { get; set; }
        abstract public void Open();
        abstract public void Off();
    }
    public class Purifier : SmallAppliance
    {
        public Purifier(int id)
        {
            ID = id;
            Console.WriteLine("淨化器: {0}", ID);
        }
        override public int ID { get; set; }
        override public void Open()
        {
            Console.Write(" 啟動【淨化器{0}】成功.", ID);
        }
        override public void Off()
        {
            Console.Write(" 關閉【淨化器{0}】成功.", ID);
        }
    }
    public class Kettle : SmallAppliance
    {

        public Kettle(int id)
        {
            ID = id;
            Console.WriteLine("電水壺: {0}", ID);
        }
        override public int ID { get; set; }
        override public void Open()
        {
            Console.Write(" 啟動【電水壺{0}】成功.", ID);
        }
        override public void Off()
        {
            Console.Write(" 關閉【電水壺{0}】成功.", ID);
        }
    }

2.2.4  從而使你可用不同的請求對客戶進行引數化

看看原來的客戶端:開啟和關閉沒有好的辦法進行統一傳遞引數,需要傳遞具體的電器的具體ID號

            //客廳一臺電水壺
            Command.ISmallAppliance kettle_1 = new Command.Kettle(1);
            //廚房一臺電水壺
            Command.ISmallAppliance kettle_2 = new Command.Kettle(2);
            //臥室一臺淨化器
            Command.ISmallAppliance purifier_1 = new Command.Purifier(100);

            List<Command.ISmallAppliance> smallAppliances = new List<Command.ISmallAppliance>();
            smallAppliances.Add(kettle_1);
            smallAppliances.Add(kettle_2);
            smallAppliances.Add(purifier_1);

            //遙控器
            Command.CommandPannel cp = new Command.CommandPannel(smallAppliances);
            //開啟:kettle_1
            cp.OpenCommand(kettle_1.Appid);
            //關閉:purifier_1
            cp.OffCommand(purifier_100.Appid);

再來看看現在:

            //Receiver(接收者):客廳一臺電水壺
            Command.SmallAppliance kettle_1 = new Command.Kettle(1);
            //Receiver(接收者):廚房一臺電水壺
            Command.SmallAppliance kettle_2 = new Command.Kettle(2);
            //Receiver(接收者):臥室一臺淨化器
            Command.SmallAppliance purifier_100 = new Command.Purifier(100);

            //Invoker(處理者)—遙控器
            Command.Pannel cp = new Command.Pannel();
            Command.OpenCommand cmd_open_k1 = new Command.OpenCommand(kettle_1);
            Command.OpenCommand cmd_open_p100 = new Command.OpenCommand(kettle_1);
            Command.OffCommand cmd_off_k1 = new Command.OffCommand(kettle_1);          
            Command.OffCommand cmd_off_p100 = new Command.OffCommand(kettle_1);            
            //傳送命令 
            cp.Send(cmd_open_k1);
            cp.Send(cmd_open_p100);
            cp.Send(cmd_off_k1);
            cp.CancleCmd(cmd_off_k1);
            cp.Send(cmd_off_p100);
            //執行命令
            cp.Execute();

 

 

三、總結

1、Recevier(接收者):最簡單的接收者還是那些小家電,還是一樣的味道.

2、Command(抽象命令): 將遙控器的的命令或者請求,抽象為Command(命令).【這是思想重要的轉變】

3、Invoker(處理者):然後改造遙控器使之擁有三個核心功能:請求排隊、記錄日誌、可撤銷.【操作核心】

4、Client(客戶端):就是我,我家裡買了兩臺電水壺外加一臺淨化器,然後我快速的按著遙控器,嘀嘀,嘀嘀,嘀嘀,嘀嘀,嘀,TMD錯了,撤銷~~。然後喝水吃飯打豆豆

相關文章