July 30 10:29~11:45;15:22~16:20
Teddy今天在公司所做的練習。
在第四梯次「Design Patterns這樣學就會了入門實作班」的第二天課程中,有一位學員在休息時間拿了他用context、force、problem重新整理過的State模式找Teddy討論。這門公開班課程上了四次,這還是第一次有學員當場自行做這樣的練習,讓Teddy非常感動。
類似的練習,Teddy之前也曾經做過,請參考《尋找Force實驗2:State Pattern篇》和《Behavioral Patterns要解決什麼問題(一)?》。最近Teddy對於context、force、form有一些新的體驗,今天有點時間就再來練習一次好了。
Teddy在《設計的定義》提到:「設計是決定form、context,以及兩者之間的關係。」所以Teddy先畫出下面這個圖,開始思考State模式的form、context、force各是什麼。
第一步,從Form開始
因為pattern是整理已經存在的設計,所以在探討form、context、force的時候,第一步就是先把form(也就是solution)寫出來。為什麼?因為GoF已經幫鄉民們把form設計好擺在書上,只要把它描述出來就可以了。有了form之後,反推force與context就比較容易。
把GoF的書翻出來,第306頁就有State模式的form(下圖上半部)。這個form是套用State模式之後的form,如果可以把套用之前的form也畫出來,兩相比較之下,就更容易判斷是怎樣的force,導致before與after的差別(減肥前、減肥後,美容前、美容後的差別)。
這是Teddy在上課時所舉的例子,套用State模式之前程式中出現許多與狀態相關的重複程式碼。從實際的程式碼範例也可以觀察到force。
第二步,尋找Force
State模式的force,Teddy在《尋找Force實驗2:State Pattern篇》已經找過一次,這裡再列出來一次:
- 在物件的operation中使用條件判斷式,並依據狀態改變物件行為的這種作法雖然很直覺且簡單,但是會造成物件不同的operation中會出現多處重複的條件判斷式,造成日後維護與擴充的困難度。
- 由於物件的行為隨著狀態而變,因此若是一個物件的所有行為全部都放在物件自己身上,則容易造成單一物件程式碼太長,同樣增加維護與擴充的困難度。
- 當物件狀態很多的時候,如果狀態改變的邏輯無法很清楚的被表達出來,則程式可能不容易被理解。
Teddy今天重新整理的force的結果剛好也找到三個,不過內容比較簡短一些:
- 把所有與狀態相關的程式碼寫在一個物件內,則該物件的程式碼會太多,不好理解與修改(違反Open-Closed Principle)。
- 物件容易存在許多與狀態相關的重複程式碼。
- 單一物件狀態太多,變得不易測試。
以上這些force,更抽象一點來說,就是modifiability、extendibility、testability、understandability的問題。這幾股力量,把原本的form(全部和狀態有關的程式都寫在一個物件裡面),擠壓成後來就變成了State模式的form(由原本單一物件搞定全部的事情,演變成context、state、concrete state這三種不同的物件互相合作來完成原本的工作)。
第三步,定義Problem
簡單一點來看,problem就是form的相反(把解答反過來就變成問題)。既然State模式的form已經設計好了,problem應該也很簡單才對。所以,第一個problem的版本只要把GoF書中關於State模式的intent改成問句就好了:
Problem(v1):How do you allow an object to alter its behavior when its internal state changes so that the object will appear to change its class?
以上這個問題的敘述,非常明確,剛好可以對應到State模式的解決方案。
另一種定義problem的角度,是從「GoF書中所有的behavioral pattern想要解決那些問題」作為出發點。Teddy在《Behavioral Patterns要解決什麼問題(一)?》做過這樣的練習,得到的問題是:
Problem(v2):How do you assign responsibilities between objects to manage complex control flow?
第二版本的problem,就比較抽象一些。
第四步,釐清Context
既然「設計是決定form、context,以及兩者之間的關係」,form已經有了,要決定context「理論上」應該很簡單才對。事實上,說簡單也沒那麼簡單,context可大可小,端看描述這個pattern的人眼中是如何看待這個世界。
在GoF書中,在State模式的Motivation章節,用一個TCPConnection作為例子,算是一種給定context的作法。GoF設計模式的context還可以從Applicability章節來尋找。
Context:一個物件的行為會因為物件自身狀態不同而表現出不同的行為,例如一個自動販賣機物件,同樣一個「選擇貨品」按鈕,會因為顧客有沒有投錢、投了多少錢、販賣機有沒有零錢、貨品是否賣完,而有著不同的反應。
***
最後把今天整理State模式的結果重新謄寫一遍:
Name:State
Context:一個物件的行為會因為物件自身狀態不同而表現出不同的反應動作,這在程式開發中是一個很常見的情況。例如一個自動販賣機物件,具備「選擇貨品」的功能。同樣一個「選擇貨品」功能,會因為顧客有沒有投錢、投了多少錢,而有著不同的行為反應。
Problem:當物件內部狀態改變時,你要如何讓它的行為也跟著改變,就好像這個物件換了另外一個人似的?
Force:把所有與狀態相關的程式碼寫在一個物件內可以讓物件依據狀態改變行為,但:
- 物件的程式碼很容易變得太長,不好理解與修改(而且會違反Open-Closed Principle)。
- 物件容易存在許多與狀態相關的重複程式碼。
- 單一物件狀態太多,變得不易測試。
Solution:將物件中與狀態相關的行為抽離出來。設計一個State介面,針對每一個狀態,新增一個實作State介面的Concrete State物件。把物件中與狀態相關的行為移到相對應的Concrete State物件之中。讓物件擁有一個指向State介面的成員變數,藉由改變State成員變數所指向的Concrete State物件,來改變原始物件的行為。
圖片來源在此。
***
整理之後的State模式,好像真的有比較容易理解一點耶。以上整理,雖然不算完美,但總算是一個開始。至於resulting context,就留給鄉民們當作家庭作業自行練習。
寫到這裡突然覺得,為什麼談設計與設計模式的相關文章,都搞得好像在寫論文一樣。
***
友藏內心獨白:說好的「設計模式的逆襲」哩?