解耦: 讓事物獨立地行事,或者至少明確的宣告之間的關係
抽象:不同事物之間概念上的關聯方式
Bridge模式遵循兩個原則:
- 找出變化並封裝之
- 優先使用物件聚合而不是類別繼承
舉個例子:
假設我有兩個版本的繪圖程式DP1和DP2然後我希望用這兩個繪圖程式來繪製圖形
方形和圓形,範例如下圖
依照需求來看,我們可能會畫出類似上面的UML圖,
接著開始思考該如何將圖組合在一起,
首先我們從Client使用物件的順序來思考,假設Client需要畫一個V1Rectangle,
依序會呼叫 Client -> rectangle.draw -> dp1.drawline *4
我們發現雖然類別圖的物件非常的多,但是實際上我們只須處理三個物件
- 使用矩形的Client物件
- V1Rectangle物件
- DP1物件
接著我們可能會畫出如下的UML圖
利用繼承來解決這個需求似乎是很自然的解法,但是再仔細思考一下,如果現在新增了一種形狀和一種繪圖程式,物件的數量會變成9個,如果將繪圖程式持續增加,將會造成物件爆炸,
這讓我們必須重頭思考這樣的設計是否是有問題的。
過度使用繼承
過度使用繼承透過特殊化處理變化,從已有的類別衍生新類別,會造成物件越來越難以維護
用設計模式進行思考,用物件的職責來思考而不是其結構,良好的設計應該為每種變化使用不同的特殊化(繼承)並將變化轉移到使用或擁有這種變化的物件中(組合)
那我們應該怎麼做呢?
我們應該
- 找出變化並封裝之
- 優先使用物件聚合,而不是類別繼承
例如 Client應該只知道他在使用一個Shap物件而不應該知道是Rectangle或Circle,
Shap應該只知道目前是使用某一個版本的Drawing繪圖程式而不必知道是V1或是V2
再看一次下面的圖
Client只知道Shap 所以我們可以思考出左邊的類別圖,Shap應該只知道Drawing
所以我們會得到右邊的圖,接著兩張圖該怎麼聯結再一起呢?
這是一個抽象與實作分離的例子,形狀的抽象與繪圖實作分離,這其實就是Bridge模式。
讓我們來看一下Bridge模式的特徵
意圖:將一組實作與另一組使用他們的物件分離
問題:一個抽象類別的衍生類別必須使用多個實作,但不能出現類別數量爆炸性增長。
參與者與協作者:Abstraction為要實作的物件定義介面,Implementor為具體的實作類別定義介面
Abstraction的衍生類別使用Implementor的衍生類別,卻無須知道自己具體使用哪一個ConcreteImplementor
解決方案:為所有實作定義一個介面,供抽象類別的所有衍生類別使用。
效果:實作與使用實作的物件解耦,提供了可擴展性,客戶物件無須操心實作問題。
實作:
- 將實作封裝在一個抽象類別。
- 在要實作的抽象基礎類別中包含一個實作(也可利用介面代替抽象類別)。
小結:
Bridge模式把握幾個原則
物件對自己負責: Shap可以繪製自己,Drawing負責繪圖元素
抽象類別:Shap代表形狀,Drawing代表繪圖程式
透過抽象類別進行封裝:Client只看的到Shap , Shap只知道Drawing
一條規則,實作一次: 抽象類別中經常有些方法實際使用實作物件,抽象類別的子類別會忽叫這些方法,如此再需要修改時,只需要修改一個地方。
可測試性:改進的方案,讓我們可以很輕易的做測試,可分別對shap以及drawing做測試。
沒有留言:
張貼留言