需求落地分散式應用服務 將需求轉化為分散式應用服務的過程可以按照以下步驟進行: 理解需求:首先,你需要仔細閱讀和理解業務需求。與相關的利益相關者(如業務分析師、產品經理等)進行溝通,確保你對需求的理解是準確的。 設計架構:根據需求,設計一個適合的分散式應用架構。這包括確定應用的組件和模塊,以及它們之 ...
基本介紹:
命令模式,顧名思義就是將命令抽象化,然後將請求者和接收者通過命令進行綁定。
而命令的請求者只管下達命令,命令的接收者只管執行命令。
從而實現瞭解耦,請求者和接受者二者相對獨立。
單獨理解起來比較困難,咱們還是通過具體實例來說明吧。
舉例說明:
生活中遙控控制電器就是命令模式,比如智能開關控制一盞燈。
首先將燈的開和關封裝成一個命令,然後將這個命令綁定到智能開關上。
智能開關的執行其實就是燈開關的執行,燈和智能開關是通過命令來進行交互的。
這個時候如果想控制電視的開關,那就可以將電視的開關封裝成一個命令。
然後將這個命令綁定到智能開關上,智能開關的執行這個時候就變成了電視的開關。
如果即想控制電視又想控制燈,那就可以將燈的命令和電視的命令都綁定到智能開關上。
又比如皇帝寫了一道聖旨,命令張三即刻回京。
聖旨跟張三是綁定關係,也就是說張三是實際執行者,聖旨是命令,起到的作用就是跟張三進行綁定和傳達信息。
那想個問題傳達聖旨的宦官是誰有關係嗎,是沒有的,是誰都可以。但又是不可或缺的。它就相當於一個遙控或者說是中間人、傳話人。
是皇帝將這個宦官和聖旨進行了綁定(客戶端就是那個皇帝)。那在想個問題,是先有聖旨再有張三呢,還是先有張三再有聖旨。
答案是肯定的,先有張三,也就是說得先有具體執行者。
然後再有聖旨即命令,通過名字將張三和聖旨綁定到一起。
如果皇帝想命令李四也回京,那這個聖旨可以命令李四嗎?是不可以的,需要重新寫一份聖旨。
所以從這個不難看出,命令類和具體的執行者是強綁定關係,當然也可以做成工廠。
這個時候大家想個問題,兩道聖旨是不是可以讓同一個宦官去傳達就可以。因為宦官和執行者不存在綁定,它只和聖旨綁定。
所以說宦官就相當於遙控,它只需要宣讀聖旨而不必在意具體是怎麼執行的,就可以讓不同的執行人進行不同的操作。
這就是命令模式的本質。
基本結構:
通過上述例子不難看出,命令模式主要核心構件有以下幾個:
◊ 命令的執行者(接收者Receiver):它單純的只具體實現了功能。(實例中對應的則是張三和李四)
◊ 命令的下達者(調用者Invoker):
就是起到遙控的作用,首先在類中聲明抽象命令類的引用,並通過參數註入等方式進行實例化。
然後通過調用命令對象來告訴執行者執行功能。起到一個傳聲筒的作用。(實例中對應的是宦官)
◊ 抽象命令類Command:
主要作用有兩點,其一就是為了規範具體命令類,聲明其執行方法。
其二就是為了方便調用者Invoker調用,使其不需要知道具體命令類,只需要執行抽象命令類即可。(實例中對應的是泛指聖旨)
◊ 具體命令類ConcreteCommand:
繼承自抽象類,在類中首先聲明瞭執行者的引用,通過參數註入等方式進行創建執行者對象,將命令類和執行者進行綁定。
其次具體實現了抽象類中聲明的執行方法,調用執行者對象中方法進行執行。(實例中對應的是張三和李四具體的兩道聖旨)
◊ 客戶端角色Client:
主要是負責將執行者和命令類進行綁定,其次將命令類和調用者進行綁定。
使其調用者可以通過命令類給執行者傳遞消息進行執行。(實例中對應的是皇帝角色)
優缺點:
優點:
1.降低了系統的耦合性。將調用操作對象和知道如何實現該操作對象的解耦。
2.通過命令模式,新的命令可以很輕鬆的加入系統中,且不會影響其他類,符合開閉原則。
缺點:
使用命令模式可能會導致某些系統存在過多的命令類,會影響使用。
命令模式適用情形:
1.將用戶界面和行為分離。
2.對請求進行排隊,記錄請求日誌以及支持可撤銷的操作等。
具體實例:
就以皇帝下達聖旨作為實例吧,首先先說下順序。
1.得先有執行者也就是接收聖旨的人,它主要是執行具體的行為和功能,如果是遙控控制燈,那這裡就是那盞燈本身。這裡命名為Receiver_ZhangSan類。
2.其次就需要創建聖旨,也就是傳達命令的媒介。但每道聖旨都是一樣的材質和規則,所以需要先創建一個抽象類來規範。這裡將抽象類命名為Command類,將具體命令類即聖旨內容命名為ConcreteCommand類它繼承自抽象類。
3.有了執行人也有了聖旨,那就可以安排一個傳旨的人就可以了(如果是遙控控制燈,這裡相當於遙控)。這裡命名為Invoker類。
4.執行人有了,聖旨有了,傳旨的人也有了,那皇帝就可以下令執行了。這裡就相當於客戶端調用了。
實現方式說明:
1.創建Receiver_ZhangSan類,設置具體執行方法,這裡可以是騎馬回京,可以是其他的操作。它只負責具體功能的實現。
2.創建Command抽象類,聲明Execute方法。
3.創建ConcreteCommand命令類,繼承自抽象類。在該類中創建Receiver_ZhangSan類的引用,並通過參數註入或屬性賦值等方式實例化該類,併在Execute方法中具體調用該類中的具體實現方法。
4.創建Invoker類,在該類中創建Command抽象類的引用,並通過參數註入或屬性賦值等方式將ConcreteCommand類實例化,併在該類中創建方法來執行抽象類中的Execute方法。
5.客戶端直接調用Invoker類中的方法來執行命令。
具體的執行過程就是,通過Invoker類中的方法來調用抽象類中的Execute方法其實就是調用ConcreteCommand中的Execute方法,
然後ConcreteCommand中的Execute調用的又是Receiver_ZhangSan類中的具體實現方法。
這樣就相當於通過Invoker類調用了Receiver_ZhangSan類中的方法,只不過是中間加了一個傳話人即命令類。
為什麼這麼做呢?這樣做有什麼好處呢?
好處也很好理解,那就是Invoker類不必知道具體執行人是誰,也不必知道具體怎麼執行,只需要將命令註入給該類就好了。
這樣如果命令改變了,那直接改變註入的命令類就可以了,方便快捷,而且不需要修改現有的類,符合開閉原則。
而且還可以按順序批量執行命令,只需要將命令依次註入給Invoker類進行調用就可以了。即任務隊列。
也可以一次性註入多條命令,同時執行。
1 /// <summary> 2 /// 張三類,它是實際執行者,也就是命令接收者 3 /// </summary> 4 public class Receiver_ZhangSan 5 { 6 private string strName = "張三"; 7 public void Eat() 8 { 9 Console.WriteLine(strName + ":吃飯。"); 10 } 11 public void Behavior1() 12 { 13 Console.WriteLine(strName + ":騎馬回京。"); 14 } 15 public void Behavior2() 16 { 17 Console.WriteLine(strName + ":原地待命。"); 18 } 19 } 20 21 /// <summary> 22 /// 命令抽象類,規範命令類,並提供執行方法。 23 /// </summary> 24 public abstract class Command 25 { 26 //執行方法 27 public abstract void Execute(); 28 } 29 30 /// <summary> 31 /// 具體命令類,也就是聖旨書寫的具體內容。 32 /// </summary> 33 public class ConcreteCommand1 : Command 34 { 35 //創建執行人的引用,這裡使用readonly,規定只可以在構造函數中進行賦值。 36 private readonly Receiver_ZhangSan _Receiver_ZhangSan; 37 38 //構造函數,參數註入方式,將執行人註入到命令類中。好比是將人名寫在聖旨上。 39 public ConcreteCommand1(Receiver_ZhangSan receiver_ZhangSan) 40 { 41 this._Receiver_ZhangSan = receiver_ZhangSan; 42 } 43 44 //具體命令 45 public override void Execute() 46 { 47 //下達立刻回京的命令 48 this._Receiver_ZhangSan.Behavior1(); 49 } 50 } 51 52 /// <summary> 53 /// 調用者類,命令下達者,即宦官。 54 /// </summary> 55 public class Invoker 56 { 57 //創建抽象命令類的引用,這裡不同於具體命令類,方便演示,使用了參數賦值的形式進行註入。 58 public Command command { get; set; } 59 60 //下達命令,即宣讀聖旨 61 public void Reading() 62 { 63 if (command != null) 64 { 65 command.Execute(); 66 } 67 } 68 } 69 70 /// <summary> 71 /// 客戶端 72 /// </summary> 73 class Client 74 { 75 static void Main(string[] args) 76 { 77 //首先創建接收人,即張三。 78 Receiver_ZhangSan receiver_ZhangSan = new Receiver_ZhangSan(); 79 80 //然後創建命令類,即聖旨。這裡通過構造函數參數註入的形式將張三和命令進行綁定。 81 Command command = new ConcreteCommand1(receiver_ZhangSan); 82 83 //其次創建調用者,即宦官。 84 Invoker invoker = new Invoker(); 85 //為了演示不同註入方式,這裡通過屬性賦值的方式將命令和調用者綁定。 86 invoker.command = command; 87 88 //調用,即宣讀聖旨 89 invoker.Reading(); 90 91 Console.WriteLine("\r\n"); 92 93 Console.ReadKey(); 94 } 95 }
如果皇帝想讓張三吃飽飯再回京,同時想讓李四原地待命該如何實現呢。
兩種方式,如果張三和李四在一起,就可以寫在一個聖旨里即寫在一個命令類里。
如果不在一起,那就需要寫兩道聖旨,即兩個命令類。
添加李四類:
1 /// <summary> 2 /// 李四類,它是實際執行者,也就是命令接收者 3 /// </summary> 4 public class Receiver_LiSi 5 { 6 private string strName = "李四"; 7 public void Eat() 8 { 9 Console.WriteLine(strName + ":吃飯。"); 10 } 11 public void Behavior1() 12 { 13 Console.WriteLine(strName + ":坐馬車回京。"); 14 } 15 public void Behavior2() 16 { 17 Console.WriteLine(strName + ":原地待命。"); 18 } 19 }
添加新命令類:
1 /// <summary> 2 /// 聖旨,即命令類 3 /// </summary> 4 public class ConcreteCommand3 : Command 5 { 6 //創建執行人的引用 7 private readonly Receiver_LiSi _Receiver_LiSi; 8 private readonly Receiver_ZhangSan _Receiver_ZhangSan; 9 10 //將執行人註入到命令類中。 11 public ConcreteCommand3(Receiver_LiSi receiver_LiSi, Receiver_ZhangSan receiver_ZhangSan) 12 { 13 this._Receiver_LiSi = receiver_LiSi; 14 this._Receiver_ZhangSan = receiver_ZhangSan; 15 } 16 17 //具體命令 18 public override void Execute() 19 { 20 //讓張三吃飽飯再回京 21 this._Receiver_ZhangSan.Eat(); 22 this._Receiver_ZhangSan.Behavior1(); 23 //讓李四原地待命 24 this._Receiver_LiSi.Behavior2(); 25 } 26 }
客戶端調用:
1 /// <summary> 2 /// 客戶端 3 /// </summary> 4 class Client 5 { 6 static void Main(string[] args) 7 { 8 //首先創建接收人,即張三和李四。 9 Receiver_ZhangSan receiver_ZhangSan = new Receiver_ZhangSan(); 10 Receiver_LiSi receiver_LiSi = new Receiver_LiSi(); 11 12 //然後創建命令類,即聖旨。這裡通過構造函數參數註入的形式將張三和命令進行綁定。 13 Command command = new ConcreteCommand3(receiver_LiSi, receiver_ZhangSan); 14 15 //其次創建調用者,即宦官。 16 Invoker invoker = new Invoker(); 17 //為了演示不同註入方式,這裡通過屬性賦值的方式將命令和調用者綁定。 18 invoker.command = command; 19 20 //調用,即宣讀聖旨 21 invoker.Reading(); 22 23 Console.WriteLine("\r\n"); 24 25 Console.ReadKey(); 26 } 27 }
以上實例則是張三和李四在一起,寫在一個聖旨里即寫在一個命令類里。如果不在一起呢?
創建新命令類:
1 /// <summary> 2 /// 讓李四原地待命的聖旨 3 /// </summary> 4 public class ConcreteCommand2 : Command 5 { 6 //創建執行人的引用 7 private readonly Receiver_LiSi _Receiver_LiSi; 8 9 //將執行人註入到命令類中。 10 public ConcreteCommand2(Receiver_LiSi receiver_LiSi) 11 { 12 this._Receiver_LiSi = receiver_LiSi; 13 } 14 15 //具體命令 16 public override void Execute() 17 { 18 //讓李四原地待命 19 this._Receiver_LiSi.Behavior2(); 20 } 21 }
客戶端調用:
1 /// <summary> 2 /// 客戶端 3 /// </summary> 4 class Client 5 { 6 static void Main(string[] args) 7 { 8 //首先創建接收人,即張三和李四。 9 Receiver_ZhangSan receiver_ZhangSan = new Receiver_ZhangSan(); 10 Receiver_LiSi receiver_LiSi = new Receiver_LiSi(); 11 12 //然後創建命令類,即聖旨1 13 Command command1 = new ConcreteCommand1(receiver_ZhangSan); 14 //然後創建命令類,即聖旨2 15 Command command2 = new ConcreteCommand2(receiver_LiSi); 16 17 //其次創建調用者,即宦官。 18 Invoker invoker = new Invoker(); 19 invoker.command = command1; 20 invoker.Reading(); 21 22 invoker.command = command2; 23 invoker.Reading(); 24 25 Console.WriteLine("\r\n"); 26 27 Console.ReadKey(); 28 } 29 }
另一種方式,可以在Invoker里申明數組保存命令依次執行。
1 /// <summary> 2 /// 調用者類,命令下達者,即宦官。 3 /// </summary> 4 public class InvokerList 5 { 6 public List<Command> commands; 7 public void AddCommand(Command command) 8 { 9 if (commands == null) 10 { 11 commands = new List<Command>(); 12 } 13 commands.Add(command); 14 } 15 16 //下達命令,即宣讀聖旨 17 public void Reading() 18 { 19 if (commands != null) 20 { 21 foreach (Command item in commands) 22 { 23 item.Execute(); 24 } 25 } 26 } 27 }
客戶端調用:
1 /// <summary> 2 /// 客戶端 3 /// </summary> 4 class Client 5 { 6 static void Main(string[] args) 7 { 8 //首先創建接收人,即張三和李四。 9 Receiver_ZhangSan receiver_ZhangSan = new Receiver_ZhangSan(); 10 Receiver_LiSi receiver_LiSi = new Receiver_LiSi(); 11 12 //然後創建命令類,即聖旨1 13 Command command1 = new ConcreteCommand1(receiver_ZhangSan); 14 //然後創建命令類,即聖旨2 15 Command command2 = new ConcreteCommand2(receiver_LiSi); 16 17 //其次創建調用者,即宦官。 18 InvokerList invoker = new InvokerList(); 19 invoker.AddCommand(command1); 20 invoker.AddCommand(command2); 21 invoker.Reading(); 22 23 Console.WriteLine("\r\n"); 24 25 Console.ReadKey(); 26 } 27 }
從以上事例中不難看出,命令的調用者即Invoker類無需知道執行人和具體執行什麼內容,更換命令也很方便,只需要改變註入的命令類就可以了。
但也可以從實例中看出,新增一個命令就需要新增一個命令類,這可能會導致某些系統存在過多的命令類,會影響使用。
讀後思考:書讀百遍其義自見,讀不夠百遍,那不如自己動手寫寫,不如就拿遙控控制不同電器為例子。
友情提示:
創建一盞燈
創建一個命令,該命令控制繼承自抽象類,執行燈的一些操作,所以需要將創建好的燈註入到命令對象里。
創建一個遙控,將遙控的某一個按鈕綁定上該命令。可以是參數註入,也可以是屬性賦值。
具體執行就是執行遙控的函數,然後傳遞到命令類 最後才是具體燈的操作。
關鍵點就是燈跟命令進行綁定,方式就是在命令里聲明一個燈的引用,然後參數註入等方式進行實例化。
然後命令再跟遙控進行綁定,方式相同,在遙控里聲明一個命令的引用,然後參數註入等方式進行實例化。
最後實行執行操作的是遙控。
為什麼要將命令抽象化,因為要跟遙控綁定,遙控只是執行命令,具體是什麼樣的命令,就交給命令本身去決定了。
還有一個問題就是,有多少個電器 就需要多少個命令 。那其實可以合併成一個。大家可以動手試試。
總結:
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化。適用於需要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。
命令模式的實現要點在於把某個具體的命令抽象化為具體的命令類,並通過加入命令請求者角色來實現將命令發送者對命令執行者的依賴分割開。
命令模式的目的是解除命令發出者和接收者之間的緊密耦合關係,使二者相對獨立,有利於程式的並行開發和代碼的維護。
命令模式的核心思想是將請求封裝為一個對象,將其作為命令發起者和接收者的中介,而抽象出來的命令對象又使得能夠對一系列請求進行操作,如對請求進行排隊,記錄請求日誌以及支持可撤銷的操作等。