1、 基本概述編程
問題:假設有一個控制器,該控制器上有7個可編程的插槽,每一個均可以指定到一個不一樣的家電裝置,每一個插槽都有對應的開關按鈕。這個遙控器還具有一個總體的撤銷按鈕。廠家已經提供了控制家電基本處理類。但願你可以建立一組控制遙控器的API,讓每一個插槽都可以控制一個或一組裝置。(以下圖,廠商類)數組
分析1:廠家提供的類,有許多都具有On()和off()方法,除此以外還有一些其餘的五花八門的方法。數據結構
分析2:還不僅這樣,聽起來彷佛未來還會有更多的廠商類,並且每一個類還會有各式各樣的方法。ide
分析3:遙控器應該知道如何解讀按鈕被按下的動做,而後發出正確的請求,可是遙控器不需知道這些家電自動化的細節。ui
分析4:咱們不想讓遙控器包含一大堆if語句,例如「if slot1==Light,then light.On(),else if slot1==Hottub,then hottub.JetsOn()」.你們都知道這樣的設計很糟糕。且只要有新的廠商類進來,就必須修改現有的代碼,形成潛在的錯誤。this
分析5:命令模式能夠將「動做的請求者」從「動做的執行者」對象中解耦,在這個例子中,請求者能夠是遙控器,而執行者對象就是廠商類其中之一的實例。編碼
分析6:利用命令對象,把請求(例如打開電燈)封裝成一個特定對象(例如客廳電燈對象)。因此,若是對每一個按鈕都存儲一個命令對象,那麼當按鈕被按下的時候,就能夠請命令對象作相關的工做。遙控器並不須要知道工做內容是什麼,只要有個命令對象能喝正確的對象溝通,把事情作好就能夠了。spa
分析7:使用這個模式,咱們可以建立一個API,將這些命令對象加載到按鈕插槽,讓遙控器的代碼儘可能保持簡單。而把家電自動化的工做和進行該工做的對象一塊兒封裝在命令對象中。線程
2、詳細說明設計
命令模式:將請求封裝成對象,這可讓你使用不一樣的請求、隊列,或者日誌請求來參數化其餘對象。命令模式也能夠支持撤銷操做。
如今仔細看這個定義,咱們知道一個命令對象經過在特定接收者上綁定一組動做來封裝一個請求。要達到這一點,命令對象將動做和接收者包進對象中。這個對象只暴露出一個execute()方法,當此方法被調用的時候,接收者就會進行這些動做。從外面來看,其餘對象不知道究竟哪一個接收者進行了哪些動做,只知道若是調用execute()方法,請求的目的就能達到。
1.定義命令模式類圖:
2.下面的類圖提供了設計的全貌:
問:若是擁有了一個遙控器,卻沒法光憑按下一個按鈕,就同時能弄暗燈光、打開音響和電視、設置好DVD,並讓熱水器開始加溫,那麼要這個遙控器還有什麼意義?
答:此時就能夠用到命令模式的延伸「宏命令」,製造一種新的命令,用來執行其餘一堆命令。在宏命令中,用命令數組(集合)存儲一大堆命令,當這個宏命令被遙控器執行時,就一次性執行數組裏的每一個命令。(具體內容,可在代碼列表查看)
問:接收者必定有必要存在嗎?爲什麼命令對象不直接實現execute()方法的細節。
答:通常來講,咱們儘可能設計「傻瓜」命令對象,它只懂得調用一個接收者的一個行爲。然而,有許多「聰明」命令對象會實現許多邏輯,直接完成一個請求。固然你能夠設計聰明的命令對象,只是這樣一來,調用者和接收者之間的解耦程度是比不上「傻瓜」命令對象的,並且,你也不可以把接收者當作參數傳給命令。
問:我能夠建立PartyCommand,而後在它的execute()方法中調用其餘的命令,利用這種作法實現Party模式(Party Mode)嗎?
答:你能夠這麼作。而後,這等於把Party模式「硬編碼」到PartyCommand中。爲何要這麼麻煩呢?利用宏命令,你能夠動態地決定PartyCommand是由哪些命令組成,因此宏命令在使用上更靈活。通常來講,宏命令的作法更優雅,也須要較少的新代碼。
問:我如何可以實現多層次的撤銷操做?換句話說,我但願可以按下撤銷按鈕許屢次,撤銷到很早很早之前的狀態。
答:其實這至關容易作到,不要只記錄最後一個被執行的命令,而使用一個堆棧記錄操做過程的每個命令。而後,無論何時按下了撤銷按鈕,你均可以從堆棧中取出最上層的命令,而後調用它的Undo()方法。
3.命令模式的更多用途:
(1)隊列請求:命令能夠將運算塊打包(一個接收者和一組動做),而後將它傳來傳去,就像是通常的對象同樣。它甚至能夠在不一樣的線程中被調用。咱們能夠利用這樣的特性衍生一些應用,例如:日程安排、線程池、工做隊列等。
想象有一個工做隊列,你在某一端添加命令,而後另外一端則是線程,線程進行下面的動做,從隊列中取出一個命令,調用它的Execute()方法,等待這個調用完成,而後將此命令對象丟棄,再取出下一個命令......
(2)日誌請求:某些應用須要咱們將全部的動做都記錄在日誌中,並能在系統死機以後,從新調用這些動做恢復到以前的狀態。經過新增兩個方法(Store()、Load()),命令模式可以支持這一點。這種日誌的方式對於遙控器來講沒有意義,然而,有許多調用大型數據結構的動做的應用,沒法在每次改變發生時被快速地存儲。經過使用記錄日誌,咱們能夠將上次檢查點以後的全部操做記錄下來,若是系統出現情況,從檢查點開始應用這些操做。
比方說,對於電子表格應用,咱們可能想要實現的錯誤恢復方式是將電子表格的操做記錄在日誌中,而不是每次電子表格一有變化就記錄整個電子表格。對更高級的應用而言,這些技巧能夠被擴展應用到事務處理中,也就是說,一整組操做必須完成後纔算有效。
4.總結:
(1)當須要將發出請求的對象和執行請求的對象解耦的時候,使用命令模式
(2)在被解耦的二者之間是經過命令對象進行溝通的,命令對象封裝了接收者和一個或一組動做。
(3)調用者經過調用命令對象的execute()發出請求,這會使得接收者的動做被調用。
(4)調用者能夠接受命令當作參數,甚至在運行時動態地進行。
(5)命令能夠支持撤銷,作法是實現一個Undo()方法來回到execute()被執行前的狀態。
(6)宏命令是命令的一種簡單的延伸,容許調用多個命令,宏方法也能夠支持撤銷。
(7)命令也能夠用來實現日誌和事務系統。
3、代碼列表
/// <summary> /// 命令接口 /// </summary> public interface ICommand { /// <summary> /// 執行 /// </summary> void Execute(); /// <summary> /// 撤銷 /// </summary> void Undo(); } public class RemoteControl { private ICommand[] onCommands; private ICommand[] offCommands; private ICommand undoCommand; public RemoteControl() { onCommands = new ICommand[7]; offCommands = new ICommand[7]; ICommand noCommand = new NoCommand(); for (int i = 0; i < 7; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } undoCommand = noCommand; } public void SetCommand(int slot, ICommand onCommand, ICommand offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void OnButtonWasPushed(int slot) { onCommands[slot].Execute(); undoCommand = onCommands[slot]; } public void OffButtonWasPushed(int slot) { offCommands[slot].Execute(); undoCommand = offCommands[slot]; } public void UndoButtonWasPushed() { undoCommand.Undo(); } public override string ToString() { StringBuilder sb=new StringBuilder(); sb.Append("\n---------Remote Control----------\n"); for (int i = 0; i < onCommands.Length; i++) { sb.AppendFormat("[slot {0}] {1} {2}",i,onCommands[i].GetType().FullName, offCommands[i].GetType().FullName); sb.AppendLine(); } return sb.ToString(); } } public class SimpleRemoteControl { private ICommand slot; public void SetCommand(ICommand command) { slot = command; } public void ButtonWasPressed() { slot.Execute(); } } //Worker /// <summary> /// 吊扇 /// </summary> public class CeilLingFan { public CeilLingFanSpeed speed { get; private set; } = 0; private string name; public CeilLingFan(string name) { this.name = name; } /// <summary> /// 高速 /// </summary> public void High() { speed = CeilLingFanSpeed.High; Console.WriteLine("{0} ceilLingFan is highs", name); } /// <summary> /// 中速 /// </summary> public void Medium() { speed = CeilLingFanSpeed.Medium; Console.WriteLine("{0} ceilLingFan is medium", name); } /// <summary> /// 低速 /// </summary> public void Low() { speed = CeilLingFanSpeed.Low; Console.WriteLine("{0} ceilLingFan is low", name); } /// <summary> /// 關閉 /// </summary> public void Off() { speed = CeilLingFanSpeed.Off; Console.WriteLine("{0} ceilLingFan is Off", name); } public enum CeilLingFanSpeed { High = 3, Medium = 2, Low = 1, Off = 0 } } /// <summary> /// 車庫門 /// </summary> public class GarageDoor { private string name; public GarageDoor(string name) { this.name = name; } /// <summary> /// 上升 /// </summary> public void Up() { Console.WriteLine("{0} Garage door is open", name); } /// <summary> /// 降低 /// </summary> public void Down() { Console.WriteLine("{0} Garage door is close", name); } /// <summary> /// 中止 /// </summary> public void Stop() { Console.WriteLine("{0} Garage door is stop", name); } /// <summary> /// 燈光開啓 /// </summary> public void LightOn() { Console.WriteLine("{0} Garage Light is on", name); } /// <summary> /// 燈光關閉 /// </summary> public void LightOff() { Console.WriteLine("{0} Garage Light is off", name); } } /// <summary> /// 熱浴盆 /// </summary> public class Hottub { /// <summary> /// 水循環 /// </summary> public void Circulata() { Console.WriteLine("Tub water is circulata"); } /// <summary> /// 噴射打開 /// </summary> public void JetsOn() { Console.WriteLine("Jets is on"); } /// <summary> /// 噴射關閉 /// </summary> public void JetsOff() { Console.WriteLine("Jets is Off"); } /// <summary> /// 設置溫度 /// </summary> public void SetTemperature() { Console.WriteLine("Default temperature is 46°C"); } } /// <summary> /// 燈光 /// </summary> public class Light { private string name; public Light(string name) { this.name = name; } /// <summary> /// 開啓 /// </summary> public void On() { Console.WriteLine("{0} light is On", name); } /// <summary> /// 關閉 /// </summary> public void Off() { Console.WriteLine("{0} light is Off", name); } } /// <summary> /// 立體聲 /// </summary> public class Stereo { private string name; public Stereo(string name) { this.name = name; } /// <summary> /// 開啓 /// </summary> public void On() { Console.WriteLine("{0} Stereo is on", name); } /// <summary> /// 關閉 /// </summary> public void Off() { Console.WriteLine("{0} Stereo is off", name); } /// <summary> /// 設置CD /// </summary> public void SetCd() { Console.WriteLine("Stereo Cd is on"); } /// <summary> /// 設置DVD /// </summary> public void SetDvd() { Console.WriteLine("Stereo Dvd is on"); } /// <summary> /// 設置收音機 /// </summary> public void SetRadio() { Console.WriteLine("Stereo radio is on"); } /// <summary> /// 設置音量 /// </summary> public void SetVolume(byte volume) { Console.WriteLine("Stereo volume is {0}", volume); } } /// <summary> /// 電視 /// </summary> public class TV { private string name; public TV(string name) { this.name = name; } /// <summary> /// 開啓 /// </summary> public void On() { Console.WriteLine("{0} TV is on", name); } /// <summary> /// 關閉 /// </summary> public void Off() { Console.WriteLine("{0} TV is off", name); } /// <summary> /// 設置頻道 /// </summary> public void SetInputChannel() { Console.WriteLine("Defualt channel is CCTV1"); } /// <summary> /// 設置音量 /// </summary> public void SetVolume() { Console.WriteLine("Defualt volume is 5"); } } //Commands /// <summary> /// 吊扇基礎命令類 /// </summary> public abstract class CeilingFanCommand : ICommand { protected CeilLingFan ceilLingFan; private CeilLingFan.CeilLingFanSpeed speed; public void Execute() { speed = ceilLingFan.speed; FanExecute(); } public abstract void FanExecute(); public void Undo() { switch (speed) { case CeilLingFan.CeilLingFanSpeed.High: ceilLingFan.High(); break; case CeilLingFan.CeilLingFanSpeed.Medium: ceilLingFan.Medium(); break; case CeilLingFan.CeilLingFanSpeed.Low: ceilLingFan.Low(); break; case CeilLingFan.CeilLingFanSpeed.Off: ceilLingFan.Off(); break; } } } public class CeilLingFanHighCommand : CeilingFanCommand { public CeilLingFanHighCommand(CeilLingFan ceilLingFan) { base.ceilLingFan = ceilLingFan; } public override void FanExecute() { ceilLingFan.High(); } } public class CeilLingFanLowCommand : CeilingFanCommand { public CeilLingFanLowCommand(CeilLingFan ceilLingFan) { base.ceilLingFan = ceilLingFan; } public override void FanExecute() { ceilLingFan.Low(); } } public class CeilLingFanMediumCommand : CeilingFanCommand { public CeilLingFanMediumCommand(CeilLingFan ceilLingFan) { base.ceilLingFan = ceilLingFan; } public override void FanExecute() { ceilLingFan.Medium(); } } public class CeilLingFanOffCommand : CeilingFanCommand { public CeilLingFanOffCommand(CeilLingFan ceilLingFan) { base.ceilLingFan = ceilLingFan; } public override void FanExecute() { ceilLingFan.Off(); } } public class GarageDoorCloseCommand : ICommand { private GarageDoor garageDoor; public GarageDoorCloseCommand(GarageDoor garageDoor) { this.garageDoor = garageDoor; } public void Execute() { garageDoor.Down(); garageDoor.LightOff(); } public void Undo() { garageDoor.LightOn(); garageDoor.Up(); } } public class GarageDoorOpenCommand : ICommand { private GarageDoor garageDoor; public GarageDoorOpenCommand(GarageDoor garageDoor) { this.garageDoor = garageDoor; } public void Execute() { garageDoor.Up(); garageDoor.LightOn(); } public void Undo() { garageDoor.Down(); garageDoor.LightOff(); } } public class HottubOffCommand:ICommand { private Hottub hottub; public HottubOffCommand(Hottub hottub) { this.hottub = hottub; } public void Execute() { hottub.JetsOff(); } public void Undo() { hottub.JetsOn(); } } public class HottubOnCommand : ICommand { private Hottub hottub; public HottubOnCommand(Hottub hottub) { this.hottub = hottub; } public void Execute() { hottub.JetsOn(); hottub.SetTemperature(); } public void Undo() { hottub.JetsOff(); } } public class LightOffCommand:ICommand { private Light light; public LightOffCommand(Light light) { this.light = light; } public void Execute() { light.Off(); } public void Undo() { light.On(); } } public class LightOnCommand : ICommand { private Light light; public LightOnCommand(Light light) { this.light = light; } public void Execute() { light.On(); } public void Undo() { light.Off(); } } /// <summary> /// 更多命令(宏命令) /// </summary> public class MacroCommand:ICommand { private ICommand[] commands; public MacroCommand(ICommand[] commands) { this.commands = commands; } public void Execute() { foreach (ICommand command in commands) { command.Execute(); } } public void Undo() { foreach (ICommand command in commands) { command.Undo(); } } } /// <summary> /// 沒有命令 /// </summary> public class NoCommand : ICommand { public void Execute() { } public void Undo() { } } public class StereoOffCommand : ICommand { private Stereo stereo; public StereoOffCommand(Stereo stereo) { this.stereo = stereo; } public void Execute() { stereo.Off(); } public void Undo() { stereo.On(); } } public class StereoOnCommand : ICommand { private Stereo stereo; public StereoOnCommand(Stereo stereo) { this.stereo = stereo; } public void Execute() { stereo.On(); } public void Undo() { stereo.Off(); } } public class StereoOnWithCDCommand:ICommand { private Stereo stereo; public StereoOnWithCDCommand(Stereo stereo) { this.stereo = stereo; } public void Execute() { stereo.On(); stereo.SetCd(); stereo.SetVolume(11); } public void Undo() { stereo.Off(); } } public class TVOffCommand:ICommand { private TV tv; public TVOffCommand(TV tv) { this.tv = tv; } public void Execute() { tv.Off(); } public void Undo() { tv.On(); } } public class TVOnCommand : ICommand { private TV tv; public TVOnCommand(TV tv) { this.tv = tv; } public void Execute() { tv.On(); tv.SetInputChannel(); tv.SetVolume(); } public void Undo() { tv.Off(); } } //RunTest [Test] public void RemoteLoader() { //單個插槽的簡單控制裝置 /*SimpleRemoteControl remote = new SimpleRemoteControl(); Light light = new Light(); GarageDoor garageDoor = new GarageDoor(); LightOnCommand lightOn = new LightOnCommand(light); GarageDoorOpenCommand garageOpen = new GarageDoorOpenCommand(garageDoor); remote.SetCommand(lightOn); remote.ButtonWasPressed(); remote.SetCommand(garageOpen); remote.ButtonWasPressed();*/ //多個插槽(含有關閉功能)的控制裝置 /*RemoteControl remoteControl = new RemoteControl(); Light livingRoomLight = new Light("Living Room"); Light kitchenLight = new Light("Kitchen"); CeilLingFan ceilLingFan = new CeilLingFan("Living Room"); GarageDoor garageDoor = new GarageDoor(""); Stereo stereo = new Stereo("Living Room"); LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight); LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight); LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight); LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight); CeilLingFanLowCommand ceilingFanLow = new CeilLingFanLowCommand(ceilLingFan); CeilLingFanOffCommand ceilLingFanOff = new CeilLingFanOffCommand(ceilLingFan); GarageDoorOpenCommand garageDoorOpen = new GarageDoorOpenCommand(garageDoor); GarageDoorCloseCommand garageDoorClose = new GarageDoorCloseCommand(garageDoor); StereoOnWithCDCommand stereoOnWithCd = new StereoOnWithCDCommand(stereo); StereoOffCommand stereoOff = new StereoOffCommand(stereo); remoteControl.SetCommand(0, livingRoomLightOn, livingRoomLightOff); remoteControl.SetCommand(1, kitchenLightOn, kitchenLightOff); remoteControl.SetCommand(2, ceilingFanLow, ceilLingFanOff); remoteControl.SetCommand(3, garageDoorOpen, garageDoorClose); remoteControl.SetCommand(4, stereoOnWithCd, stereoOff); Console.WriteLine(remoteControl); remoteControl.OnButtonWasPushed(0); remoteControl.OffButtonWasPushed(0); remoteControl.OnButtonWasPushed(1); remoteControl.OffButtonWasPushed(1); remoteControl.OnButtonWasPushed(2); remoteControl.OffButtonWasPushed(2); remoteControl.OnButtonWasPushed(3); remoteControl.OffButtonWasPushed(3); remoteControl.OnButtonWasPushed(4); remoteControl.OffButtonWasPushed(4);*/ //多個插槽(含有關閉功能,含有撤銷功能)的控制裝置 /*RemoteControl remoteControl = new RemoteControl(); Light livingRoomLight = new Light("Living Room"); LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight); LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight); remoteControl.SetCommand(0, livingRoomLightOn, livingRoomLightOff); remoteControl.OnButtonWasPushed(0); remoteControl.OffButtonWasPushed(0); Console.WriteLine(remoteControl); remoteControl.UndoButtonWasPushed(); remoteControl.OffButtonWasPushed(0); remoteControl.OnButtonWasPushed(0); Console.WriteLine(remoteControl); remoteControl.UndoButtonWasPushed();*/ //多個插槽(含有狀態對象的撤銷功能)的控制裝置 /*RemoteControl remoteControl = new RemoteControl(); CeilLingFan ceilLingFan = new CeilLingFan("Living Room"); CeilLingFanHighCommand ceilingFanHigh = new CeilLingFanHighCommand(ceilLingFan); CeilLingFanMediumCommand ceilLingFanMedium = new CeilLingFanMediumCommand(ceilLingFan); CeilLingFanOffCommand ceilLingFanOff = new CeilLingFanOffCommand(ceilLingFan); remoteControl.SetCommand(0, ceilLingFanMedium, ceilLingFanOff); remoteControl.SetCommand(1, ceilingFanHigh, ceilLingFanOff); remoteControl.OnButtonWasPushed(0); remoteControl.OffButtonWasPushed(0); Console.WriteLine(remoteControl); remoteControl.UndoButtonWasPushed(); remoteControl.OnButtonWasPushed(1); Console.WriteLine(remoteControl); remoteControl.UndoButtonWasPushed();*/ //多個插槽(含有宏命令)的控制裝置 RemoteControl remoteControl = new RemoteControl(); Light light = new Light("Living Room"); TV tv = new TV("Living Room"); Stereo stereo = new Stereo("Living Room"); Hottub hottub = new Hottub(); LightOnCommand lightOn = new LightOnCommand(light); StereoOnCommand stereoOn = new StereoOnCommand(stereo); TVOnCommand tvOn = new TVOnCommand(tv); HottubOnCommand hottubOn = new HottubOnCommand(hottub); LightOffCommand lightOff = new LightOffCommand(light); StereoOffCommand stereoOff = new StereoOffCommand(stereo); TVOffCommand tvOff = new TVOffCommand(tv); HottubOffCommand hottubOff = new HottubOffCommand(hottub); ICommand[] partyOn = { lightOn, stereoOn, tvOn, hottubOn }; ICommand[] partyOff = { lightOff, stereoOff, tvOff, hottubOff }; MacroCommand partyOnMacro = new MacroCommand(partyOn); MacroCommand partyOffMacro = new MacroCommand(partyOff); remoteControl.SetCommand(0, partyOnMacro, partyOffMacro); Console.WriteLine(remoteControl); Console.WriteLine("-----Pushing Macro On-----"); remoteControl.OnButtonWasPushed(0); Console.WriteLine("=====Pushing Macro Off----"); remoteControl.OffButtonWasPushed(0); }
---------------------------------以上內容根據《head frist design mode》進行整理