l

2012年9月28日 星期五

尋找Force實驗2:State Pattern篇

Sept. 27 14:51~16:40

image

 

昨天Teddy在《尋找Force:Observer篇》中舉了Observer pattern為例子,說明GoF的pattern寫作格式中,Motivation章節相對於Teddy之前提到的pattern六大元素裡面的force。如果《尋找Force:Observer篇》鄉民們有看懂的話,可能會有一個疑問:

這該不會是碰巧運氣好才在Observer pattern的Motivation找到force的吧?!

昨天寫完《尋找Force:Observer篇》之後Teddy也有同樣的疑惑,那今天就找另外一個有點小複雜但卻十分有用的State pattern來做第二個實驗。今天先從Intent改起(把Intent改成Problem)。

Intent

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

修改後:How do you allow an object to alter its behavior when its internal state changes? (後面那句可省略)

中文:要如何讓一個物件的行為隨著內部狀態改變而跟著改變?

接下來看一下GoF書中State pattern的Motivation。

Motivation

Consider a class TCPConnection that represents a network connection. A TCPConnection object can be in one of several different states: Established, Listening, Closed. When a TCPConnection object receives requests from other objects, it responds differently depending on its current state. For example, the effect of an Open request depends on whether the connection is in its Closed state or its Established state. The State pattern describes how TCPConnection can exhibit different behavior in each state.

The key idea in this pattern is to introduce an abstract class called TCPState to represent the states of the network connection. The TCPState class declares an interface common to all classes that represent different operational states. Subclasses of TCPState implement state-specific behavior. For example, the classes TCPEstablished and TCPClosed implement behavior particular to the Established and Closed states of TCPConnection.

The class TCPConnection maintains a state object (an instance of a subclass of TCPState) that represents the current state of the TCP connection. The class TCPConnection delegates all state-specific requests to this state object. TCPConnection uses its TCPState subclass instance to perform operations particular to the state of the connection.

Whenever the connection changes state, the TCPConnection object changes the state object it uses. When the connection goes from established to closed, for example, TCPConnection will replace its TCPEstablished instance with a TCPClosed instance.

抱歉,Teddy能力有限,從上面的段落中完全找不到任何一個force…Orz。

請問一下鄉民,State pattern的Motivation章節看起來比較像什麼?

沒錯,比較像是Solution的範例

***

既然在Motivation找不到force,請問鄉民下一個尋找的章節是哪一個?

答對了,就是Applicability

鄉民甲:為什麼?

Teddy:還記得Teddy在《Force是什麼?》有提到一個問題「force和context很像耶,好像可以把force放到context裡面,也可以把context裡面的東西變成force。要如何判斷?」先不要管這個問題的答案,至少這個問題提供給鄉民們一個線索,在Motivation找不到force的話,那就到Context去找吧。GoF的pattern寫作格式中,Applicability相等於Context,所以接下來看一下State pattern的Applicability。

Applicability

Use the State pattern in either of the following cases:

  • An object's behavior depends on its state, and it must change its behavior at run-time depending on that state.
  • Operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects.

GoF書中記錄的Applicability有兩點,第一點翻成中文的意思是「一個物件的行為和物件本身的狀態有關係;在執行期間,物件的狀態若是改變,則其行為也會跟著改變」。這一點是描述一個事實,比較像是Context。第二點就比較可以找到force,請觀察這一句:

Operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure.

為了讓物件的行為可以隨著狀態改變,一個很簡單的做法就是物件的operation(method或是member function)程式碼裡面有著一個很大的條件判斷式(if-then-else或是switch case)。在不同的物件狀態之下,程式會執行條件判斷式的不同區塊。這還不打緊,問題是相同的條件判斷式結構,會出現在這個物件身上好幾個不同的operation中。這就形成了眾所皆知的duplicated code這個程式碼壞味道了。

扯這麼多,那State pattern的force是什麼?以下是Teddy所推敲的force:

  • 在物件的operation中使用條件判斷式,並依據狀態改變物件行為的這種作法雖然很直覺且簡單,但是會造成物件不同的operation中會出現多處重複的條件判斷式,造成日後維護與擴充的困難度。
  • 由於物件的行為隨著狀態而變,因此若是一個物件的所有行為全部都放在物件自己身上,則容易造成單一物件程式碼太長,同樣增加維護與擴充的困難度。
  • 當物件狀態很多的時候,如果狀態改變的邏輯無法很清楚的被表達出來,則程式可能不容易被理解。

***

State pattern的解決方案Teddy就不在這裡說明(鄉民:不在這裡說明那是要在哪裡說明?挑眉質疑),不然這一篇會寫不完啊。如果知道State pattern的鄉民們,請看一下這三個force,然後思考一下State pattern的solution是否有解決上面這三個force?

螢幕快照 2012-09-26 下午5.25.40

圖片來源:http://upload.wikimedia.org/wikipedia/commons/e/e8/State_Design_Pattern_UML_Class_Diagram.svg

***

做完這個實驗,Teddy才發現一個問題。GoF的《Design Patterns》這本書Teddy也買了超過15年,看了不下N次。這本書寫的真的很棒,但是說真的不是很容易讀懂(有些鄉民讀起來覺得在看天書)。之前Teddy的第一個反應是:「啊,這本書中的C++例子舉的不是很好,所以不容易理解」。但是,就算是讀了其他書籍,像是《大話設計模式》,書中有比較淺顯易懂的C#程式例子,再回頭讀《Design Patterns》,還是會覺得有許多「理解漏洞」無法填補起來。所以,

例子是一個因素,但要深入理解《Design Patterns》,例子非常重要,但並不是決定性的因素。

那到底是什麼問題?從今天的討論中,鄉民們應該可以發現,《Design Patterns》這本書在寫作上其實存在著一些問題。例如,章節定義不清。在昨天的實驗中,Observer pattern的Motivation雖然也有夾雜著example與若干對於solution的說明,但Teddy還是在其中找到了兩個force。在今天的實驗中,Teddy發現State pattern的Motivation只包含了solution的example,並沒有找到任何的force。有一個force跑到了Applicability(Context)章節裡面,經由Teddy把它給拯救出來之後得以和世人見面 XD。另外兩個force是Teddy觀察State pattern的solution與resulting context所歸納出來的。

***

今天又是扯了一大篇。鄉民們可能會想:So what?沒有force又怎麼樣?force跑到Context裡面又怎麼樣?Teddy再幫鄉民們複習一下force的涵義:

  • Force是問題的限制或特性。
  • Force告訴我們為什麼模式所要解決的「問題」是一個真正的問題;為什麼這個問題很難,為什麼這個問題需要一個聰明的,甚至是違反直覺的解決方案。Force也是了解為何會採用此種解決方案(而非其他方案)的關鍵。
  • The often contradictory considerations to be considered when choosing a solution to a problem. Each solution considers certain forces. It optimizes some and may totally ignore others. The relative importance of the forces is determined by the context.

在Teddy的經驗中,很多學習design patterns的程式設計師,在應用design pattern的時候,經常會發生「套錯pattern」的問題。當然這個問題的原因很多,Teddy認為最重要的原因,就是:

  • 沒有弄懂每一個pattern要解決的problem與問題發生的context是什麼。
  • 沒有思考到自己的問題領域中,存在著那些force;在套用某個pattern之後,這些force是否被解決或是平衡了。

這句話再重複一次:

Force告訴我們為什麼模式所要解決的「問題」是一個真正的問題;為什麼這個問題很難,為什麼這個問題需要一個聰明的,甚至是違反直覺的解決方案。Force也是了解為何會採用此種解決方案(而非其他方案)的關鍵

沒有認清force,就很難判斷自己所設計或是挑選的solution是否合適。

***

友藏內心獨白:《設計模式的逆襲》這本書的種子快要發芽了 XD。

沒有留言:

張貼留言