主要內容
1.結構型模式概述
2.結構型模式區別與比較
3.對變化的封裝
結構型模式概述
結構型模式,顧名思義討論的是類和對象的結構,它采用繼承機制來組合接口或實現(類結構型模式),或者通過組合一些對象,從而實現新的功能(對象結構型模式)。這些結構型模式,它們在某些方面具有很大的相似性,仔細推敲,側重點卻各有不同。
Adapter模式通過類的繼承或者對象的組合側重於轉換已有的接口;Bridge模式通過將抽象和實現相分離,讓它們可以分別獨立的變化,它強調的是系統沿著多個方向的變化;Decorator模式采用對象組合而非繼承的手法,實現了在運行時動態的擴展對象功能的能力,它強調的是擴展接口;Composite模式模糊了簡單元素和復雜元素的概念,它強調的是一種類層次式的結構;Façade 模式將復雜系統的內部子系統與客戶程序之間的依賴解耦,它側重於簡化接口,更多的是一種架構模式;Flyweight模式解決的是由於大量的細粒度對象所造成的內存開銷的問題,它與Façade模式恰好相反,關注的重點是細小的對象;Proxy模式為其他對象提供一種代理以控制對這個對象的訪問,它注重於增加間接層來簡化復雜的問題。
結構型模式區別與比較
1.橋接模式與裝飾模式
這兩個模式在一定程度上都是為了減少子類的數目,避免出現復雜的繼承關系。但是它們解決的方法卻各有不同,裝飾模式把子類中比基類中多出來的部分放到單獨的類裡面,以適應新功能增加的需要,當我們把描述新功能的類封裝到基類的對象裡面時,就得到了所需要的子類對象,這些描述新功能的類通過組合可以實現很多的功能組合,裝飾模式的簡略圖如下:
圖1 裝飾模式簡略圖
橋接模式則把原來的基類的實現化細節抽象出來,在構造到一個實現化的結構中,然後再把原來的基類改造成一個抽象化的等級結構,這樣就可以實現系統在多個維度上的獨立變化,橋接模式的簡略圖如下:
圖2 橋接模式簡略圖
2.外觀模式和代理模式
外觀模式和代理模式解決問題的側重點不同,但是它們解決問題的手法卻是一樣的,即都是引入了間接層的手法,這也是我們軟件系統中經常用的一種手法。外觀模式雖然側重於簡化接口,但是在某些情況下,外觀模式也可以兼任代理模式的責任,例如外觀對象有可能是另一個位於另一個地址空間對象的遠程代理,這時候我們可以叫做外觀代理模式,或者代理外觀模式。它們的類簡略圖如下:
圖3 代理模式簡略圖
圖4 外觀模式簡略圖
3.適配器模式
適配器模式重在轉換接口,它能夠使原本不能在一起工作的兩個類一起工作,所以經常用在類庫復用,代碼遷移等方面,有一種亡羊補牢的味道。類適配器和對象適配器可以根據具體實際情況來選用,但一般情況建議使用對象適配器模式,如下圖所示,左邊是類適配器模式,右邊是對象適配器模式:
圖5 適配器模式簡略圖
對變化的封裝
如何應對變化,是軟件開發的一個永恆的主題,也許我們不能夠杜絕變化的發生,但至少我們可以通過一些手段讓變化降到最低。“找到系統可變的因素,將之封裝起來”,通常就叫做對變化的封裝。關於這個問題的解釋在《Java與模式》中講的很清晰,抽象化與實現化的簡單實現,也就是“開-閉”原則在類層次上的最簡單實現,如下圖所示:
圖6
在這個繼承結構中,第一層是抽象化,它封裝了抽象的業務邏輯,這是系統中不變的部分;第二層是實現化,它是具體的業務邏輯的實現,封裝了系統中變化的部分,這個實現允許實現化角色多態性的變化:
圖7
也就是說,客戶端依賴的是業務邏輯的抽象化類型的對象,而與抽象化的具體實現無關,不在乎它到底是“實現化”,“實現化2”還是“實現化3”,如下圖所示:
圖8
每一種繼承關系都封裝了一個變化因素,而一個繼承關系不應當處理兩個變化因素,換言之,這種簡單繼承關系不能處理抽象化與實現化都變化的情況,如下圖所示:
圖9
上圖中的兩個變化因素應當是獨立的,可以在不影響另一者的情況下獨立的變化,如下面這兩個等級結構分別封裝了自己的變化因素,由於每一個變化因素都是可以通過靜態關系表達的,因此分別使用繼承關系實現,如下圖:
圖10
在抽象化和實現化之間的聯系怎麼辦呢?好的設計只有一個,不好的設計卻有很多中,下面這種設計就是繼續使用繼承進行靜態關系設計的類圖:
圖11
這樣的設計其實存在著很多的問題,首先出現的是多重的繼承關系,隨著具體實現化的增多,子類的繼承關系會變得異常復雜;其次如果出現新的抽象化修正或者新的具體實現角色,就只好重新修改現有系統中的靜態關系,以適應新的角色,這就違背了開放-封閉原則。正確是設計應該是使用兩個獨立的等級結構封裝兩個獨立的變化因素,並在它們之間使用聚合關系,以達到功能復用的目的,這就回到了我們的橋接模式上,如下圖所示:
圖12
從另一個角度講,一個好的設計通常沒有多於兩層的繼承等級結構,或者說,如果出現兩個以上的變化因素,就需要找出哪一個因素是靜態的,可以使用靜態關系,哪一個是動態的,必須使用聚合關系。