四、 數據機問題
感覺《敏捷式軟體開發 (Agile Software Development)-原則、模式與實踐》中關於Bridge模式的例子很好。(《Java與模式》一書33章的對變化的封裝一節也寫得很不錯,推薦
大家讀一讀。它深入的闡述了《Design Patterns Explained》一書中"1)Design to interfaces.
2)Favor composition over inheritance. 3)Find what varies and
encapsulate it"的三個觀點。)。
,有大量的數據機客戶程式在使用Modem介面。Modem介面被幾個衍生類別HayesModem、USRoboticsModem和
EarniesModem實現。它很好地遵循了OCP、LSP和DIP。當增加新種類的數據機時,數據機的客戶程式不會受影響。
假定這種情形持續了幾年,並有許多數據機的客戶程式都在使用著Modem介面。現出現了一種不撥號的數據機,被稱為專用數據機。它們位
於一條專用連線的兩端。有幾個新應用程式使用這些專用數據機,它們無需撥號。我們稱這些使用者為DedUser。但是,客戶希望當前所有的數據機
客戶程式都可以使用這些專用數據機。他們不希望去更改許許多多的數據機客戶應用程式,所以完全可以讓這些數據機客戶程式去撥一些假
(dummy)電話號碼。
如果能選擇的話,我們會把系統的設計更改為所示的那樣。
我們把撥號和通訊功能分離為兩個不同的介面。原來的數據機實現這兩個介面,而數據機客戶程式使用這兩個介面。DedUser只使用
Modem介面,而DedicateModem只實現Modem介面。但這樣做會要求我們更改所有的數據機客戶程式--這是客戶不允許的。
一個可能的解決方案是讓DedicatedModem從Modem派生並且把dial方法和hangup方法實現為空白,就像下面這樣:
幾個月後,已經有了大量的DedUser,此時客戶提出了一個新的更改。為了能撥國際電話號碼、信用卡電話、PIN標識電話等等,必修對現有dial中使用char[10]儲存號碼改為能夠撥打任意長度的電話號碼。
顯然,所有的數據機客戶程式都必須更改。客戶同意了對數據機客戶程式的更改,因為他們別無選擇。糟糕的是,現在必須要去告訴DedUser的編寫者,他們必須要更改他們的代碼!你可以想象他們聽到這個會有多高興。本來他們是不用調用dial的。
這就是許多項目都會具有的那種有害的混亂依賴關係。系統某一部分中的一個雜湊體(kludge)建立了一個有害的依賴關係,最終導致系統中完全無關的部分出現問題。
如果使用ADAPTER模式解決最初的問題的話,就可以避免這個嚴重問題。
請注意,雜湊體仍然存在。適配器仍然要類比串連狀態。然而,所有的依賴關係都是從適配器發起的。雜湊體和系統隔離,藏身於幾乎無人知曉的適配器中。
BRIDGE模式
看待這個問題,還有另外一個方式。現在,出現了另外一種切分Modem階層的方式。如:
這不是一個理想的結構。每當增加一款新硬體時,就必須建立兩個新類--一個針對專用的情況,一個針對撥號的情況。每當增加一種新連線類型時,就必須建立3個新類,分別對應3款不同的硬體。如果這兩個自由度根本就是不穩定的,那麼不用多久,就會出現大量的衍生類別。
在類型階層具有多個自由度的情況中,BRIDGE模式通常是有用的。我們可以把這些階層分開並通過橋把它們結合到一起,而不是把它們合并起來。
我們把數據機類階層分成兩個階層。一個表示串連方法,另一個表示硬體。
這個結構雖然複雜,但是很有趣。它的建立不會影響到數據機的使用者,並且還完全分離了串連策略和硬體實現。
ModemConnectController的每個衍生類別代表了一個新的串連策略。在這個策略的實現中可以使用sendlmp、receivelmp、
diallmp和hanglmp。新imp方法的增加不會影響到使用者。可以使用ISP來給串連控制類增加新的介面。這種做法可以建立出一條遷移路徑,調
制解調器的客戶程式可以沿著這條路徑慢慢地得到一個比dial和hangup層次更高的API。