2014年4月23日 星期三

設計模式(十二) Bridge 模式

Bridge 模式 :  將抽象與實作解耦合,使他們都可以獨立的變化。

解耦: 讓事物獨立地行事,或者至少明確的宣告之間的關係
抽象:不同事物之間概念上的關聯方式


Bridge模式遵循兩個原則:

  1.  找出變化並封裝之
  2. 優先使用物件聚合而不是類別繼承

舉個例子:
假設我有兩個版本的繪圖程式DP1和DP2然後我希望用這兩個繪圖程式來繪製圖形
方形和圓形,範例如下圖



依照需求來看,我們可能會畫出類似上面的UML圖,
接著開始思考該如何將圖組合在一起,

首先我們從Client使用物件的順序來思考,假設Client需要畫一個V1Rectangle,
依序會呼叫 Client -> rectangle.draw -> dp1.drawline *4 
我們發現雖然類別圖的物件非常的多,但是實際上我們只須處理三個物件
  1. 使用矩形的Client物件
  2. V1Rectangle物件
  3. DP1物件
接著我們可能會畫出如下的UML圖


利用繼承來解決這個需求似乎是很自然的解法,但是再仔細思考一下,如果現在新增了一種形狀和一種繪圖程式,物件的數量會變成9個,如果將繪圖程式持續增加,將會造成物件爆炸,
這讓我們必須重頭思考這樣的設計是否是有問題的。

過度使用繼承
過度使用繼承透過特殊化處理變化,從已有的類別衍生新類別,會造成物件越來越難以維護
用設計模式進行思考,用物件的職責來思考而不是其結構,良好的設計應該為每種變化使用不同的特殊化(繼承)並將變化轉移到使用或擁有這種變化的物件中(組合)

那我們應該怎麼做呢?

我們應該
  1. 找出變化並封裝之
  2. 優先使用物件聚合,而不是類別繼承
例如 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做測試。

沒有留言:

張貼留言