Head First Design Mode(8)-命令模式
該系列文章繫個人讀書筆記及總結性內容,任何組織和個人不得轉載進行商業活動!
命令模式:
封裝呼叫——本節把封裝帶到了一個全新的境界:把方法呼叫(method invocation)封裝起來;
通過封裝方法呼叫,我們可以將功能模組包裝成型,呼叫功能的模組不需要關心事情如何進行,只要知道如何使用包裝成型的方法來完成功能即可;
家電自動化遙控器API示例:
七個可程式設計卡槽,對應七種家電的控制開關;
有一個整體的控制撤銷的按鈕;
針對每一種家電我們都有各自開發的JavaAPI來進行控制;
我們想要的是建立一組控制遙控器的API,讓每個插槽對相應的電器進行控制,最主要的是可以支援未來可能出現的裝置的控制;
我們可以在Machine插槽上接上不同的裝置,然後既可以控制它了;
撤銷按鈕是整體公用的,會撤銷最後一個按鈕的動作;
不同的裝置廠商為裝置提供了介面類:
包括TV、Light等;而且這樣的類還不少(很多家電),介面各異;
遙控器按下按鈕執行動作,但不一定要知道具體家電實現方法的細節;
命令模式可以將“動作的請求者”從“動作的執行者”物件中解耦:
請求者是遙控器;
執行者是具體家電類的一個例項(一個電視機或是一盞燈);
設計中使用“命令物件”:
利用命令物件,把請求封裝成一個特定物件;
當按下開關開啟燈時,就可以請命令物件做相關的工作;遙控器並不知道具體做什麼,只要命令物件能和正確的物件溝通即可;
這樣,遙控器就和燈解耦了;
餐廳場景舉例——命令模式的簡單介紹:
顧客把訂單給到招待,招待再把訂單拿到櫃檯,並通知廚師訂單來了,趕快準備,廚師根據訂單製作菜品;
餐廳的角色和職責:
一張訂單封裝了準備點餐的請求;
訂單物件可以被傳遞,且只有一個方法:orderUp();他封裝了準備餐點的動作;
訂單內有一個執行“準備餐點動作”的物件——廚師;
招待並不需要知道訂單內容是什麼,也不需要知道誰來準備餐點,他的工作是接受訂單,然後呼叫訂單的orderUp()方法;
這樣,只要是顧客createOrder()的招待都可以takeOrder(),然後呼叫所有訂單都需要有的orderUp();
招待不是讓廚師準備餐點,而是呼叫訂單的方法提醒廚師訂單需要被準備了;即招待和廚師解耦了;
類似的場景對應一種設計模式——允許將“發出請求的物件”與“接受與執行請求的物件”分隔開;
招待發出準備餐點的請求——>【訂單】——>廚師準備餐點
人操作家電的請求——>【遙控器】——>家電執行動作
遙控器不知道有哪些物件,但當按鈕按下,呼叫該物件的orderUp()方法,燈就開了;
從餐廳到命令模式:
回到我們的遙控器——使用命令物件:
實現命令介面Command:所有命令物件實現相同的包含一個方法的介面;一般慣用名稱execute();
命令的執行物件Light:如燈,可以開啟或關閉;
實現具體的命令LightOnCommand:如開燈命令;
使用命令類SimpleControl: 假設一個只有一個按鈕的遙控器;
log:
bogon:遙控器簡單測試 huaqiang$ javac *.java
bogon:遙控器簡單測試 huaqiang$ java SimpleControlDriver
Light on! For - LightA
此例中,遙控器就是呼叫者,會傳入命令物件,可以用發請求;
定義命令模式:
命令模式將“請求”封裝成物件,以便使用不同的請求、佇列或者日誌來引數化其他物件;
命令模式也支援可撤銷的操作;
重點理解:
一個命令物件通過在特定接收者上繫結一組動作來來封裝一個請求;
命令物件將接收者和動作包進物件中,只暴露一個execute()方法,此方法呼叫時,接收者就會執行這些動作;
對外,其他物件並不知道誰做了那些事,只知道,如果呼叫execute()方法,請求的目的就能達到;
命令模式類圖:
客戶構建命令;
讓呼叫者(Invoker)持有請求(Command),用請求(ConcreteCommand)去通知命令的執行者(Receiver);
完成呼叫者和執行者之間的解耦;
完成7個插槽的遙控器:
類似簡單遙控器所做的,我們需要提供一個方法,將命令指定到插槽;
實際上,我們將有7個插槽,每個插槽具備開關按鈕;
1.實現遙控器
2.實現命令
3.測試遙控器
關鍵點:
NoCommand物件是一個空物件,是為了確定每個插槽永遠都有命令;
我們看到execute()方法可以封裝一組命令;
命令物件持有對一個廠商類的例項的引用,並實現一個execute()方法;
這個方法會呼叫廠商類例項的一個或多個方法,完成特定的行為;
加入撤銷操作也很簡單,只需要把execute之前執行的動作倒過來,如開啟命令的撤銷就是關閉;
巨集命令-命令的“Party模式”:
比如在關燈的同時,關掉音響,關掉電視等;
我們可以通過一個MacroCommand來實現一個新命令,用來執行一組命令;
如果想實現多層次的撤銷操作:
只需要使用一個堆疊記錄操作的過程的每一個命令;不管什麼時候按下了撤銷按鈕,都可以從堆疊中取出最上層的命令,然後呼叫他的undo()方法;
命令模式的更多用途:佇列請求
命令可以將運算打包,一個接收者和一組動作;命令物件被呼叫之後,運算就可以被執行;
甚至是在不同的執行緒之中被呼叫;也衍生出一些應用:日程安排(Scheduler)、執行緒池、工作佇列;
想象一個佇列,你在某一端新增命令,然後另一端則是執行緒;
執行緒從佇列中取出一個命令,呼叫它的execute()方法,等待這個呼叫完成,然後將此命令丟棄,再取出下一個命令;
這樣能有效地把運算限制在固定數目的執行緒中進行;
不同的命令物件可以封裝不同的功能,但只要實現了命令模式,就都可以加入佇列中;
命令模式的更多請求:日誌請求
某些應用需要將所有的動作都記錄在日誌中,並能在系統宕機之後,呼叫這些動作恢復到之前的狀態;
可以用命令模式支援,將歷史命令儲存到磁碟,需要時,重新載入,批量依次呼叫這些物件的execute()方法;
對於更高階的應用,這些技巧可以被擴充套件到事務處理中;
總結:
1.命令模式將發出請求的物件和執行請求的物件解構;
2.在被解耦的兩者之間是通過命令物件進行溝通的,命令物件封裝了接收者和一個或一組動作;
3.呼叫者通過呼叫命令物件的execute()發出請求,這會使得接收者的動作被呼叫;
4.呼叫者可以接受命令當做引數,甚至在執行時動態地進行;
5.命令可以支援撤銷,做法是實現一個undo()方法來回倒execute()被執行前的狀態;
6.巨集命令是命令的一種簡單延伸,允許呼叫多個命令;巨集命令也支援撤銷;
7.命令也可以用來實現日誌和事務系統;
OO基礎:
抽象;
封裝
繼承;
多型;
OO原則:
封裝變化
多用組合,少用繼承
針對介面程式設計,不針對實現程式設計
為互動物件之間的鬆耦合設計而努力;
類應該對擴充套件開放,對修改關閉;
依賴抽象,不要依賴具體類;
OO模式:
策略模式:定義演算法族,分別封裝起來,讓他們之間互相替換,此模式讓演算法的變化獨立於使用演算法的客戶;
觀察者模式:在物件之間定義一對多的依賴,這樣一來,當一個物件改變狀態,依賴它的物件都會收到通知,並自動更新;
裝飾者模式:動態地將責任附加到物件上;想要擴充套件功能,裝飾者提供有別於繼承的另一種選擇;
簡單工廠模式;
工廠方法模式:定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個;工廠方法讓類把例項化推遲到子類;
抽象工廠模式:提供一個介面,用於建立相關或依賴物件的家族,而不需要明確具體的類;
單件模式:確保一個類只有一個例項,並提供全域性訪問點;
——命令模式:將請求封裝成物件,這可以讓你使用不同的請求,佇列或者日誌請求來引數化其他物件;命令模式也支援撤銷操作;