我學設計模式之橋梁模式
1. 簡介
橋梁模式是是對象結構模式。橋梁模式又稱柄體模式或接口模式、橋接模式。橋梁模模式用意:将抽象化與實作化脫耦,使得二者可以獨立的變化。
熟悉這個模式對于了解面向對象的設計原則,包括“開閉原則(OCP)”以及“組合/聚合原則(CARP)”都很有幫助。
在繼續學這個模式之前先來複習了這幾個概念:
抽象化:存在于多個實體中的共同概念性的聯系,就是抽象化。做為一個過程,抽象化就是忽略一些資訊,進而把不同的實體當做同樣的實體對待。
實作化:抽象化給出的具體實作,就是實作化。
脫耦:就是兩個實體的行為的某種強關聯。而将它們的強關聯去掉,就是耦合的解脫。在橋接模式這裡是将抽象化和實體化之間的耦合解脫開,或者說将它們之間的強關聯改換成弱關聯。
強關聯:就是在編譯時期已經确定的,無法在運作時期改變的關聯;
弱關聯:就是可以動态确定并且可以在運作時期動态的改變的關聯。
顯然在java、C#這些面向對象語言中,繼承關系是強關聯,而聚合關系是弱關聯。将兩個角色之間的繼承關系改為聚合關系,就是将它們之間的強關聯改換成弱關聯。
開閉原則:對擴充開放,對修改關閉。這個原則說的是在設計一個子產品的時候,應當使用這個子產品可以在不被修改的前提下被擴充。換言之,應當可以在不修改源代碼的情況下改變這個子產品的行為。
合成聚合複用原則:盡量使用合成/聚合,盡量不要使繼承。聚合表示一種弱的“擁有關系”,展現的是A對象可以包含B對象,但B對象不是A對象的一部分;合成則是一種強的‘擁有’關系,展現了嚴格的部分和整體的關系,部分和整體的生命周期一樣。
優先使用對象的合成/聚合将有助于你保持每個類被封裝,并被集中在單個任務上。這樣類和類的繼承層次會保持較小的規模,并且不太可能增長為不可控制的龐然大物。
2. 橋接模式的結構
下面是橋梁模式的UML圖:
這個系統包含兩個等級結構:
ü 由抽象化角色和修正抽象化角色組成的抽象化等級結構。
ü 由實作化和兩個具體實作化角色所組成的實作化等級結構。
橋梁模式所涉及的角色有:
Ø 抽象化角色:抽象化給出的定義,并儲存一個對實作化對象的引用。
Ø 修正抽象化角色:擴充抽象化角色,改變和修正父類對抽象化的定義。
Ø 實作化角色:這個角色給出實作化角色的接口,但不給出具體的實作。必須提出的是,這個接口不一定和抽象化角色的接口定義相同,實際上,這兩個接口可以非常不一樣。實作化角色應當隻給出底層操作,而抽象化角色應當給出基于底層操作的更高一層的操作。
Ø 具體實作化角色:這個角色給出的實作化角色接口的具體實作。
3. 橋接模式的用法
用橋接模式來描述手機品牌與手機軟體的關系,是用Java來實作:
實作化的源代碼:
package com.zsw.bridge; /** * 手機軟體 * @author zsw * 2010年9月26日0:49:29 */ publicabstractclass HandSetSoft { publicabstractvoid run(); } |
具體實作化通訊錄:
package com.zsw.bridge; /** * 通訊錄 * @author zsw * 2010年9月26日1:08:36 */ publicclass HandSetSoftAddressList extends HandSetSoft { @Override publicvoid run() { System.out.println("------運作手機通訊錄!!!"); } } |
具體實作化遊戲:
package com.zsw.bridge; /** * 手機遊戲 * @author zsw * 2010年9月26日0:49:29 */ publicclass HandSetSoftGame extends HandSetSoft{ @Override publicvoid run(){ System.out.println("------運作手機遊戲!!!"); } } |
抽象化手機品牌:
package com.zsw.bridge; /** * 手機品牌 * @author zsw * 2010年9月26日0:49:29 */ publicabstractclass HandSetBrand { protected HandSetSoft soft; publicvoid setSoft(HandSetSoft soft) { this.soft = soft; } publicabstractvoidrun(); } |
修正抽象化諾基亞手機:
package com.zsw.bridge; publicclass HandSetBrandNOKIA extends HandSetBrand { @Override publicvoid run() { System.out.print("諾基亞手機:"); soft.run(); } } |
修正抽象化三星手機:
package com.zsw.bridge; publicclass HandSetBrandSamsung extends HandSetBrand { @Override publicvoid run() { System.out.print("三星手機:"); soft.run(); } } |
用戶端:
package com.zsw.bridge; publicclass Client { publicstaticvoid main(String[] args) { HandSetBrand hsb = new HandSetBrandNOKIA(); hsb.setSoft(new HandSetSoftGame()); hsb.run(); hsb.setSoft(new HandSetSoftAddressList()); hsb.run(); hsb = new HandSetBrandSamsung(); hsb.setSoft(new HandSetSoftGame()); hsb.run(); hsb.setSoft(new HandSetSoftAddressList()); hsb.run(); } } |
顯示結果:
諾基亞手機:------運作手機遊戲!!! 諾基亞手機:------運作手機通訊錄!!! 三星手機:------運作手機遊戲!!! 三星手機:------運作手機通訊錄!!! |
4. 橋接模式的應用場景
a) 驅動器軟體都可以是橋梁模式的應用。例如:列印機驅動器使用列印機的應用系統與列印機的驅動細節分割開,使得應用系統和列印機驅動器可以相對的獨立演化。JDBC驅動器,使用JDBC驅動器的應用程式就是抽象化角色,而驅動器本身扮演實作化角色。
b) 如何一個系統需要在夠敬愛呢的抽象化角色和具體角色之間增加更多的靈活性,避免在兩個層次之間建立靜态的聯系。
c) 設計要求實作化角色的任何改變不應當影響用戶端,或者說實作化角色的改變對用戶端是完全透明的。
d) 一個構件多于一個的抽象化角色和實作化角色,系統需要他們之間進行動态耦合。
e) 雖然在系統中使用內建式沒有問題的,但是由于抽象化角色和具體化角色需要獨立變化,設計要求需要獨立管理這兩者。
f) 實作橋梁模式是從底層到高層寫。熟悉.net中三層架構的朋友,應該知道,資料通路、業務層、界面層,其實業務層依賴于資料通路層,界面層依賴于業務邏輯層,這也相當于橋接模式。UML圖如下:
看到這個例子應該更容易了解這個模式了吧。
g) 諾基亞、三星、索尼都是手機的制造商,手機當中都可以安裝一些軟體,如通訊錄、QQ、遊戲等等。現在要設計一個系統來描述這些手機品牌及手機軟體類型。設計的UML類圖可以如下:
5. 橋接模式的優缺點
優點:
将抽象化與實作化脫耦,使二者可以獨立的變化
更好的展現軟體的設計原則:開閉原則(OCP)、組合/聚合複用原則(CARP)
6. 參考文獻
a) 《Java與模式》
b) 《大話設計模式》