5G未能拯救國內手機市場,手機出貨量持續下滑
命令模式,通常指的是一個物件向另一個物件傳送資訊指令的行為模型,比如父母命令孩子寫作業、將軍命令士兵進攻等。我們經過分析拆解方法會得到三個模組,首先得有命令傳送方,接著是被傳遞的命令本身,最後就是命令的接收執行方了。那麼,這樣拆解到底有什麼好處?讓我們先來看一個最簡單的例子,電燈泡。既然是電燈那一定對應通電和斷電的行為介面了,兩個介面方法互斥,我們就叫它Switchable吧。
public interface Switchable {//電器介面 //通電 public void on(); //斷電 public void off(); }
對於具體的燈泡實現類,必然是通電亮,斷電滅。
同樣地,我們再增加一個裝置,如果是風扇的話則是通電轉,斷電停。
我們該如何操作呢?來吧,直接用電線接通電源。
也許使用者是個糙人,直接用導線給通電了,簡單粗暴,雖然沒有錯,但這看上去與設計模式沒有任何瓜葛。為了體現出模式的優越性,我們需要讓系統進化得更高階一些,於是我們決定加入另一個模組,開關控制。
這裡的開關就類似一個控制器了,有“開”和“關”兩個按鍵分別綁定了裝置的“通電”與“斷電”行為方法。需要特別注意的是,如果在第4行我們宣告地是燈泡,那麼無疑這個開關與燈泡就繫結死了,也就是強耦合了,所以第7行我們宣告的是Switchable介面引用,並提供第10行的替換電器方法給外界注入任何的裝置。好了,我們換個方式執行程式。
這次看上去功能強大多了,開關可以隨意地接入燈泡或者風扇,注入的是誰那麼開關按鈕直接就作用於誰,對於裝置我們還可以繼續擴充套件,設計模式開始體現優勢了。等等,可這跟命令模式有什麼關係?不要著急,我們先看下這個開關策略模式是否滿足了我們的需求。
假設我們的裝置不斷擴充套件,比如有了電視機,收音機等等裝置,它們不止是開關通電這種簡單行為模式了,還可以有轉換頻道、變音量等等更多的行為。
所以,如何把控制器與裝置完全給拆解開勢在必行,此時命令模式粉墨登場。現在我們得新定義出一組”命令“模組把控制器(發令者)與裝置(執行者)徹底解耦,就以電視機和遙控器舉例說明吧。
注意程式碼第1行的介面繼承,我們的高階裝置介面則遺傳了之前的簡單通斷電介面,並新增了調節頻道和音量4個功能。接下來是電視機與收音機實現類。
沒什麼好說的,下來是解耦的重點了,我們在策略模式的基礎上又增加一層中間模組,開始編寫命令模組程式碼,首先是命令介面。
命令介面有執行操作與反執行操作兩個標準功能,然後定義其命令實現類,開關機命令、頻道轉換命令、以及音量調節命令。
程式碼很簡單,但是系統模組相對複雜,所以一定要搞清楚各模組間關係再繼續。最後一個模組是遙控器類,也就是命令傳送方了。我們保持簡單,遙控器集成了OK按鍵以及上下左右方向鍵。
1 public class Controller { 2 private Command okCommand; 3 private Command verticalCommand; 4 private Command horizontalCommand; 5 6 // 繫結OK鍵命令 7 public void bindOKCommand(Command okCommand) { 8 this.okCommand = okCommand; 9 } 10 11 // 繫結上下方向鍵命令 12 public void bindVerticalCommand(Command verticalCommand) { 13 this.verticalCommand = verticalCommand; 14 } 15 16 // 繫結左右方向鍵命令 17 public void bindHorizontalCommand(Command horizontalCommand) { 18 this.horizontalCommand = horizontalCommand; 19 } 20 21 // 開始按鍵對映命令 22 public void buttonOKHold() { 23 System.out.print("長按OK按鍵……"); 24 okCommand.exe(); 25 } 26 27 public void buttonOKClick() { 28 System.out.print("單擊OK按鍵……"); 29 okCommand.unexe(); 30 } 31 32 public void buttonUpClick() { 33 System.out.print("單擊↑按鍵……"); 34 verticalCommand.exe(); 35 } 36 37 public void buttonDownClick() { 38 System.out.print("單擊↓按鍵……"); 39 verticalCommand.unexe(); 40 } 41 42 public void buttonLeftClick() { 43 System.out.print("單擊←按鍵……"); 44 horizontalCommand.unexe(); 45 } 46 47 public void buttonRightClick() { 48 System.out.print("單擊→按鍵……"); 49 horizontalCommand.exe(); 50 } 51 }
這個遙控器持有三個命令元件,並且於第7行開始定義命令繫結方法,最後從第22行開始定義各按鍵觸發方法並對映到相應的命令操作上。可以看到,控制器對裝置一無所知,也就是它上面不再繫結有任何裝置了,而是隻繫結命令。最後,客戶端又換了一種方式執行程式。
1 public class Client { 2 3 public static void main(String[] args) { 4 System.out.println("===客戶端用【可程式設計式遙控器】操作電器 ==="); 5 Device tv = new TV(); 6 Device radio = new Radio(); 7 Controller controller = new Controller(); 8 9 //繫結【電視機】的【命令】到【控制器按鍵】 10 controller.bindOKCommand(new SwitchCommand(tv)); 11 controller.bindVerticalCommand(new ChannelCommand(tv));//上下調臺 12 controller.bindHorizontalCommand(new VolumeCommand(tv));//左右調音 13 14 controller.buttonOKHold(); 15 controller.buttonUpClick(); 16 controller.buttonUpClick(); 17 controller.buttonDownClick(); 18 controller.buttonRightClick(); 19 20 /*列印輸出: 21 ===客戶端用【可程式設計式遙控器】操作電器=== 22 長按OK按鍵……電視機啟動 23 單擊↑按鍵……電視機頻道+ 24 單擊↑按鍵……電視機頻道+ 25 單擊↓按鍵……電視機頻道- 26 單擊→按鍵……電視機音量+ 27 */ 28 29 //繫結【收音機】的【命令】到【控制器按鍵】 30 controller.bindOKCommand(new SwitchCommand(radio)); 31 controller.bindVerticalCommand(new VolumeCommand(radio));//上下調音 32 controller.bindHorizontalCommand(new ChannelCommand(radio));//左右調臺 33 34 controller.buttonOKHold(); 35 controller.buttonUpClick(); 36 controller.buttonUpClick(); 37 controller.buttonRightClick(); 38 controller.buttonDownClick(); 39 40 /*列印輸出: 41 長按OK按鍵……收音機啟動 42 單擊↑按鍵……收音機音量+ 43 單擊↑按鍵……收音機音量+ 44 單擊→按鍵……收音機調頻+ 45 單擊↓按鍵……收音機音量- 46 */ 47 48 } 49 50 }
很顯然,客戶端可以肆意妄為地組裝各個模組了,也就是說可以遙控電視,也可以遙控收音機,或許繫結上下鍵調音量,或許是換成左右鍵調音量,甚至可以定義一個巨集命令去控制燈泡的切換開關實現一種霓虹燈閃爍的效果(讀者可以思考怎樣實現),而對於控制器端本身,同樣可以繼續擴充套件,或許乾脆替換個遊戲手柄或者鍵盤,一樣可以發號施令。
至此,發令控制方與接受執行方完全被拆解開,這讓我們實現了對各模組的自由擴充套件,對指令對映、裝置繫結的靈活操控,鬆散的系統得以成就繁多模組解耦的最終目的。
1publicclassController{
2privateCommandokCommand;
3privateCommandverticalCommand;
4privateCommandhorizontalCommand;
5
6//繫結OK鍵命令
7publicvoidbindOKCommand(CommandokCommand){
8this.okCommand=okCommand;
9}
10
11//繫結上下方向鍵命令
12publicvoidbindVerticalCommand(CommandverticalCommand){
13this.verticalCommand=verticalCommand;
14}
15
16//繫結左右方向鍵命令
17publicvoidbindHorizontalCommand(CommandhorizontalCommand){
18this.horizontalCommand=horizontalCommand;
19}
20
21//開始按鍵對映命令
22publicvoidbuttonOKHold(){
23System.out.print("長按OK按鍵……");
24okCommand.exe();
25}
26
27publicvoidbuttonOKClick(){
28System.out.print("單擊OK按鍵……");
29okCommand.unexe();
30}
31
32publicvoidbuttonUpClick(){
33System.out.print("單擊↑按鍵……");
34verticalCommand.exe();
35}
36
37publicvoidbuttonDownClick(){
38System.out.print("單擊↓按鍵……");
39verticalCommand.unexe();
40}
41
42publicvoidbuttonLeftClick(){
43System.out.print("單擊←按鍵……");
44horizontalCommand.unexe();
45}
46
47publicvoidbuttonRightClick(){
48System.out.print("單擊→按鍵……");
49horizontalCommand.exe();
50}
51}