1. 程式人生 > >Head First Design Mode(8)-命令模式

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模式:

    策略模式:定義演算法族,分別封裝起來,讓他們之間互相替換,此模式讓演算法的變化獨立於使用演算法的客戶;

    觀察者模式:在物件之間定義一對多的依賴,這樣一來,當一個物件改變狀態,依賴它的物件都會收到通知,並自動更新;

    裝飾者模式:動態地將責任附加到物件上;想要擴充套件功能,裝飾者提供有別於繼承的另一種選擇;

    簡單工廠模式;

    工廠方法模式:定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個;工廠方法讓類把例項化推遲到子類;

    抽象工廠模式:提供一個介面,用於建立相關或依賴物件的家族,而不需要明確具體的類;

    單件模式:確保一個類只有一個例項,並提供全域性訪問點;

    ——命令模式:將請求封裝成物件,這可以讓你使用不同的請求,佇列或者日誌請求來引數化其他物件;命令模式也支援撤銷操作;