了解策略模式和狀態模式,并理解二者差異
策略模式和狀態模式的代碼結構非常相似,其UML類圖更是一致,容易讓人困惑。究其原因,是沒有理解兩種模式的設計目的,以至于明明設計了狀態模式的代碼結構,仍以策略模式的形式使用這些代碼。
策略模式
策略模式比較簡單,分析應用類,將類中用于完成特定任務的不同操作抽離成一組獨立的類,稱之為策略類。
由于不同操作的是服務同一任務的,其功能是一致的,只是具體實現不同,我們將相同的功能抽象為策略接口,所有策略類都通過實現這些接口來實現具體的操作;當然也可以抽離為抽象類,實現一些相同的操作,只抽象那些不同的操作。
這時候原來的應用類,只需要使用一致的策略接口就能完成特定任務,不需要維護具體的策略及其實現。
在客戶端使用該類的時候,根據需求傳入與需求對應的策略對象即可。在新增需求時,只需要新增對應的策略實現就能完成拓展,無需修改應用類和既有的策略類。
狀態模式
狀態模式的目的是讓一個對象的內部狀態變化時改變其行為,使其看上去就像改變了自身所屬的類一樣。
那些適用狀態模式的對象,其操作所發生的行為由當前狀態決定,且行為的發生可能會使對象的狀態從當前狀態轉移到另一種狀態。這就是有限狀態機的概念。
簡單地實現一個狀態機,我們可能會使用 if/else 或 switch/case 這樣的條件運算符來判斷狀態、切換行為、轉換狀態。但是這會隨著功能的膨脹使得代碼難以維護。
狀態的多寡與狀態對應的行為會根據客戶端需求的變化而變化,這也是造成代碼維護困難的原因,將其分離出來即可解決。
策略模式建議將對象所有可能的狀態都新建一個類,然后將狀態所對應的行為抽離到該狀態對應的類中。
狀態模式包含以下主要角色:
- 環境類(Context)角色:也稱為上下文,它定義了客戶端需要的接口,內部維護一個當前狀態,并負責具體狀態的切換。
- 抽象狀態(State)角色:定義一個接口或抽象類,用以封裝環境對象中的特定狀態所對應的行為,可以有一個或多個行為。
- 具體狀態(Concrete State)角色:實現抽象狀態所對應的行為,并且在需要的情況下通過持有環境類對象的引用來進行狀態切換。
將受狀態影響的操作抽離到抽象狀態接口中,再由具體狀態類實現不同的操作。
環境類角色通過依賴抽象狀態接口來維護自身狀態,不需要關心當前狀態對應的具體行為,而是將所有與狀態相關的工作都委派給該狀態對象。
當我們需要拓展狀態時,就新增一個狀態類;當特定狀態的行為發生改動時,只需要修改對應的狀態類。環境類不需要進行改動。
但是,狀態模式的弊端也很明顯,它使類變多了。如果只是維護既有功能,它確實讓代碼變得清晰明了,易于維護。但是當我們的環境類新增了功能,且新增的功能受狀態影響,那我們不得不修改抽象狀態接口和所有的具體狀態類。
理解策略模式與狀態模式的區別
二者的代碼組織方式非常相似,都有運行算法的環境類,而代表具體算法的策略接口、策略類以及狀態接口、狀態類更是一一對應。
但是其核心區別在于運行環境與算法、算法與算法之間的關系的不同,有不同關系是因為二者本質上是有著不同的目的。
-
策略模式
- 由客戶端決定使用何種算法
- 運行環境和具體算法之間沒有感知,不同算法之間也沒有感知
- 運行環境單向依賴抽象的算法接口,算法不依賴運行環境,意味著算法可以被復用
- 策略模式的目的是讓客戶端可以根據自身需求便捷地對同一任務指定不同算法,同時使新增需求的算法易于擴展,既有需求的算法易于維護。
-
狀態模式
- 運行環境的算法由其內部狀態決定
- 運行環境對具體算法沒有感知,但是算卻可以通過轉換運行環境的狀態來實現算法的切換,所以算法對運行環境有感知,不同算法間也有感知
- 算法往往依賴運行環境,算法除了轉換運行環境的狀態,還可能調用運行環境的服務方法,這意味著算法不能被復用
- 狀態模式的目地是為了使運行環境在其內部狀態發生變化時能夠自動地切換對應的算法,同時使新增的狀態易于擴展,既有的狀態易于維護。
雖然狀態模式并未規定在哪里定義初始狀態、在哪里轉換狀態。但是我覺得為了低耦合,客戶端不應對環境對象進行任何狀態對象的注入操作;初始的狀態應在環境類內部創建并定義,而狀態的轉換可以在環境類或狀態類中進行操作。
客戶端不應指定狀態模式中運行環境的狀態(具體算法)。客戶端調用環境對象的功能接口,可能間接地導致環境對象內部狀態的轉換,從而改變內部算法,但客戶端對環境對象內部的變化是沒有感知的。
如果非要在客戶端進行狀態的轉換,這時候狀態就變成了策略。若不能理解狀態和運行環境是一個整體,寫出來的狀態模式的代碼就和策略模式一樣了。

浙公網安備 33010602011771號