一、定義
將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作.
我覺得可以這樣:將一個請求封裝為一個物件,對請求排隊或記錄請求日誌,以及支援可撤銷的操作;從而使你可用不同的請求對客戶進行引數化;
二、例項:一個遙控皮膚,兩臺小家電。一個皮膚控制兩臺小家電
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錯了,撤銷~~。然後喝水吃飯打豆豆