IOS设计模式浅析之桥接模式(Bridge)
引言
在項目開發中,我們會遇到這樣的一種場景:某些類型由于自身的邏輯,往往具有兩個或多個維度的變化,比如說大話設計模式書中所說的手機,它有兩個變化的維度:一是手機的品牌,可能有三星、蘋果等;二是手機上的軟件,可能有QQ、微信等。如何應對這種“多維度的變化”?怎樣利用面向對象的技術來使得該類型能夠輕松的沿著多個方向進行變化,而又不引入額外的復雜度?這就是本章橋接模式所要解決的問題。
定義
“將抽象部分與它的實現部分分離,使它們都可以獨立地變化”
最初的定義出現于《設計模式》(Addison-Wesley,1994)。
光看這個定義,很抽象,也很不好理解。我們換一種容易理解的方式來說明一下:假設有一個系統,它可以使用多種方式來進行分類,并且每一種分類都有可能變化(比如說上面說的手機,既可以按照手機品牌來分類,也可以按照手機軟件來分類,兩者都是有可能變化的),那么就把這些分類方式分離出來讓它們獨立的變化,以減少它們之間的耦合。
結構圖
我們先來說明一下結構圖中的各個部分:
Abstraction:定義中所說的抽象部分,通常在這個對象里面,要維護一個實現部分的對象引用,在抽象對象里面的方法,需要調用實現部分的對象來完成。這個對象里面的方法,通常都是跟具體的業務相關的方法。在上面手機的例子中,可以理解為手機品牌接口;
Implementor:定義中所說的實現部分,這個接口不用和Abstraction里面的方法一致,通常是由Implementor接口提供基本的操作,而Abstraction里面定義的是基于這些基本操作的業務方法,也就是說Abstraction定義了基于這些基本操作的較高層次的操作。在上面手機的例子中,可以理解為手機軟件接口(也可以是類);
RefinedAbstraction:抽象部分的具體實現,通常在這個對象里面,定義跟實際業務相關的方法,這些方法的實現通常會使用Abstraction中定義的方法,也可能需要調用實現部分的對象來完成。在上面手機的例子中,可以理解為具體的手機品牌,它實現了Abstraction接口;
ConcreteImplementatorA:實現部分的具體實現,在上面手機的例子中,可以理解為具體的手機軟件,它實現了(或繼承了) Implementor。
這樣角色一一對照之后,是不是比較清楚了?如果不清楚,那么下面給出手機這個例子用橋接模式實現的結構圖,如下所示:
有人可能馬上發現這個圖和上面的結構圖不太一樣,這正是橋接模式的優點:它把抽象部分從實現部分中分離出來,使得兩部分能夠獨立變更。這樣,添加新的RefinedAbstraction(抽象部分的具體實現),對Implementor(實現部分)不會有任何影響;同樣,添加新的ConcreteImplementatorC(實現部分的具體實現),也能做到不影響Abstraction(抽象部分)。
假設不使用橋接模式,那么我們做出的結構圖可能有下面兩種(來自大話設計模式):
1.按品牌分類:
2.按軟件分類:
當我們增加一個手機品牌HTC,按照品牌分類的話我們需要增加手機品牌類HTC,還需要增加兩個手機軟件類HTC的QQ、HTC的微信;同樣,如果需要增加一個手機軟件,那么按照手機軟件分類的話,我們也是需要增加三個類。當我們需要增加更多的手機品牌和手機軟件時,我們會發現類會越來越多,以致無法維護。另外,采用繼承的方式,子類和父類之間的耦合度是很高的,以至于父類中的任何變化必然會導致子類發生變化。這種依賴關系限制了靈活性并最終限制了復用性(《設計模式》)
示例
上面手機的示例,這里就不用代碼進行說明了。大話設計模式書上有C#版本的示例代碼,很好理解。這里還是繼續抽象工廠模式中的應用場景:繪圖有兩個變化維度,一是工具,可以用HTML5、OWC等;另一個是圖形的種類,我們可能需要繪制餅狀圖、線形圖等。下面給出采用橋接模式實現的結構圖,如下所示:
這里給出部分源碼,完整代碼可以自行下載附件。
Chart.h,這是一個協議,里面只定義了一個方法,用于繪制圖形:
1 @protocol Chart <NSObject> 2 3 - (void)draw;
LineChart.m(PieChart.m類似),實現了Chart協議:
1 - (void)draw
2 {
3 NSLog(@"繪制線形圖");
4 }
Tool.h,一個協議,這個協議里面定義了一個Chart類型的屬性和一個繪圖的方法(當然,這個方法不是必須的,如果這個方法里面,你只是調用Chart的繪圖方法,那么久可以直接用Chart類型的屬性去調用Chart的繪圖方法,這里加這個方法是考慮還有其他業務邏輯要處理的情況):
1 #import "Chart.h" 2 3 @protocol Tool <NSObject> 4 5 @property (nonatomic,assign) id<Chart> chart; 6 7 - (void)drawing;
HTML5.m(Owc.m類似),實現了Tool協議:
1 @synthesize chart = _chart;
2
3 - (void)drawing
4 {
5 NSLog(@"HTML5 繪圖開始......");
6
7 [_chart draw];
8
9 NSLog(@"HTML5 繪圖結束......");
10 }
客戶端調用代碼:
1 id<Tool> tool = [[[NSClassFromString(@"HTML5") alloc] init] autorelease]; 2 3 tool.chart = [[[NSClassFromString(@"LineChart") alloc] init] autorelease]; 4 5 [tool drawing];
輸出結果如下(省略時間及項目名):
HTML5 繪圖開始......
繪制線形圖
HTML5 繪圖結束......
前面引言里面說了,橋接模式解決了兩維或多維變化的問題,結構圖和上面的示例所講述的都是兩維,那么多維變化的又是怎么樣的呢?假設現在繪圖這個功能,需要支持不同的平臺,比如說要支持Windows平臺和Mac平臺,那么結構圖又是怎么樣的?下面給出這種情況下的橋接模式的結構圖:
小結
橋接模式的優點:
橋接模式使用聚合關系,解耦了抽象和實現之間固有的綁定關系,使得抽象和實現可以沿著各自的維度來變化。
提高了系統的可擴展性,可以獨立地對抽象部分和實現部分進行擴展。
可減少子類的個數,這個在前面講手機示例的時候進行分析了。
橋接模式的缺點:
橋接模式的引入會增加系統的理解與設計難度,由于聚合關系建立在抽象層,要求開發者針對抽象進行設計與編程。
橋接模式要求正確識別出系統中兩個獨立變化的維度,因此其使用范圍具有一定的局限性。
通過優缺點的分析,我們可以在如下的情形下使用橋接模式:
不想在抽象與其實現之間形成固定的綁定關系;
抽象及其實現都應可以通過子類化獨立進行擴展;
對抽象的實現進行修改不應影響客戶端代碼;
如果每個實現需要額外的子類以細化抽象,則說明有必要把它們分成兩個部分;
想在帶有不同抽象接口的多個對象之間共享一個實現。
總的來說,橋接模式的本質在于“分離抽象和實現”。
下載源碼 返回目錄
總結
以上是生活随笔為你收集整理的IOS设计模式浅析之桥接模式(Bridge)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CF思维联系–CodeForces -
- 下一篇: CF思维联系–CodeForces -