top
Loading...
深入淺出Java設計模式之狀態模式
一、引子

狀態模式自身結構非常簡單——前面剛剛介紹了幾個結構比較簡單的設計模式,和他們一樣,狀態模式在具體實現上留下了可變換的余地。我前面已經介紹過它的孿生兄妹策略模式了,大家可以兩者比較著閱讀。本文將會討論兩者的區別。

二、定義與結構

GOF《設計模式》中給狀態模式下的定義為:允許一個對象在其內部狀態改變時改變它的行為。這個對象看起來似乎修改了它的類。看起來,狀態模式好像是神通廣大——居然能夠“修改自身的類”!

能夠讓程序根據不同的外部情況來做出不同的響應,最直接的方法就是在程序中將這些可能發生的外部情況全部考慮到,使用if else 語句來進行代碼響應選擇。但是這種方法對于復雜一點的狀態判斷,就會顯得雜亂無章,容易產生錯誤;而且增加一個新的狀態將會帶來大量的修改。這個時候“能夠修改自身”的狀態模式的引入也許是個不錯的主意。

狀態模式可以有效的替換充滿在程序中的if else語句:將不同條件下的行為封裝在一個類里面,再給這些類一個統一的父類來約束他們。來看一下狀態模式的角色組成吧:

1) 使用環境(Context)角色:客戶程序是通過它來滿足自己的需求。它定義了客戶程序需要的接口;并且維護一個具體狀態角色的實例,這個實例來決定當前的狀態。

2) 狀態(State)角色:定義一個接口以封裝與使用環境角色的一個特定狀態相關的行為。

3) 具體狀態(Concrete State)角色:實現狀態角色定義的接口。

類圖如下,結構非常簡單也與策略模式非常相似。

 


三、實現

由于狀態模式結構非常簡單,所以在這里羅列一些反映狀態模式實現結構的代碼沒有什么太大的作用。如果你有興趣的話可以按照上面類圖來編寫一下。

在引子中已經提到,狀態模式在具體實現上存在不同的方案。因此這里重點就這些不同的實現方式進行介紹和討論。

首先,實現時是否將狀態角色、具體狀態角色暴露給客戶程序?按照GOF的建議是不希望將狀態角色暴露給客戶程序的,與客戶程序打交道的僅僅是使用環境角色,客戶是不知道系統是怎么實現的,更不關心什么有幾個具體狀態。但是當使用環境角色中的初始狀態緊緊依賴于客戶程序時,適乎暴露是在所難免的——這就與策略模式異常相似了!

具體狀態角色中的行為一般是與使用環境角色密切相關的。因此這里便有了一個小細節:我們把使用環境角色作為參數傳遞進入具體狀態角色后,是在具體狀態角色中來實現狀態響應行為;還是僅僅調用在使用環境角色中已經實現了的方法?由于這些行為往往與使用環境角色相關,所以按照《重構》一書的“指導”——后一種實現方法是比較地道的。

從定義可知,狀態模式是要應對狀態轉換的。那么狀態的轉換在哪里定義呢?你可以選擇在使用環境角色的代碼中來表現出來,當然這便意味著狀態轉變的規則就固定下來了。GOF還給出了另外一種稍微靈活一點的實現方式:在每一個具體狀態角色中來指定后續狀態以及何時進行轉換。

其實在java強大的反射機制的支持下,我們還可以將狀態的轉換做的更加靈活——我們可以將狀態轉換的規則寫在.xml等等的配置文件里面甚至是數據庫中,我們姑且叫做狀態轉換表。進行轉換前,根據狀態轉換表來讀取下一個狀態,然后利用反射獲得具體的狀態對象……。看起來很不錯的樣子,只是效率可能低一些,在企業應用中這應該不是最重要的。

狀態模式已經被我們想象著“實現”了一番。那么狀態模式的引入會給我們的程序帶來哪些優勢呢?前面我們已經說過:狀態模式的引入免除了代碼中復雜而庸長的邏輯判斷語句。而且具體狀態角色將具體狀態和它對應的行為封裝了起來,這使得增加一種新的狀態變得簡單一些。而且如果設計合理得話,具體狀態角色可以被重用(和策略模式一樣,可以考慮使用享元模式來實現)。

使用狀態模式也會帶來一些問題。每個狀態對應一個具體的狀態類,使得整體分散,邏輯不太清晰。當然對于一個狀態非常多的系統,狀態模式帶來的優點還是大于它的缺點的。

由上面的分析就可以很明確的知道什么時候該使用狀態模式了。下面是GOF在《設計模式》中給出的狀態模式的適用情況:

1) 一個對象的行為取決于它的狀態, 并且它必須在運行時刻根據狀態改變它的行為。

2) 一個操作中含有龐大的多分支的條件語句,且這些分支依賴于該對象的狀態。

四、狀態VS策略

仔細對比狀態模式和策略模式,難免會產生疑問:這兩個明明是一個東西嘛!下面我們就來分析下兩者區別。

首先我要聲明,在實際應用中只要能夠使得你的代碼靈活漂亮起來,何必計較這些方方面面的差別呢?

Brandon Goldfedder在《模式的樂趣》里是怎么說的:“strategy模式在結構上與state模式非常相似,但是在概念上,他們的目的差異非常大。區分這兩個模式的關鍵是看行為是由狀態驅動還是由一組算法驅動,這條規則似乎有點隨意,但是在判斷時還是需要考慮它。通常,State模式的“狀態”是在對象內部的,Strategy模式的“策略”可以在對象外部,不過這也不是一條嚴格、可靠的規則。”

我很同意Brandon Goldfedder的觀點。這兩個模式的劃分,就在于使用的目的是不同的——策略模式用來處理算法變化,而狀態模式則是處理狀態變化(好玄乎阿)。

策略模式中,算法是否變化完全是由客戶程序開決定的,而且往往一次只能選擇一種算法,不存在算法中途發生變化的情況。從《深入淺出策略模式》中的例子可以很好的看出。

而狀態模式如定義中所言,在它的生命周期中存在著狀態的轉變和行為得更改,而且狀態變化是一個線形的整體;對于客戶程序來言,這種狀態變化往往是透明的。

五、總結

比較籠統地介紹了下狀態模式,并將它和非常相近的策略模式進行了比較。歡迎大家學習指正。
作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗