這裡以電視遙控器的一個例子來引出橋接模式解決的問題,首先,我們每個牌子的電視機都有一個遙控器,此時我們能想到的一個設計是——把遙控器做為一個抽象類,抽象類中提供遙控器的所有實作,其他具體電視品牌的遙控器都繼承這個抽象類,具體設計類圖如下:

這樣的實作使得每部不同型号的電視都有自己遙控器實作,這樣的設計對于電視機的改變可以很好地應對,隻需要添加一個派生類就搞定了,但随着時間的推移,使用者需要改變遙控器的功能,如:使用者可能後面需要對遙控器添加傳回上一個台等功能時,此時上面的設計就需要修改抽象類RemoteControl的提供的接口了,此時可能隻需要向抽象類中添加一個方法就可以解決了,但是這樣帶來的問題是我們改變了抽象的實作,如果使用者需要同時改變電視機品型号和遙控器功能時,上面的設計就會導緻相當大的修改,顯然這樣的設計并不是好的設計。然而使用橋接模式可以很好地解決這個問題,下面讓我具體看看橋接模式是如何實作的。
橋接模式即将抽象部分與實作部分脫耦,使它們可以獨立變化。對于上面的問題中,抽象化也就是RemoteControl類,實作部分也就是On()、Off()、NextChannel()等這樣的方法(即遙控器的實作),上面的設計中,抽象化和實作部分在一起,橋接模式的目的就是使兩者分離,根據面向對象的封裝變化的原則,我們可以把實作部分的變化(也就是遙控器功能的變化)封裝到另外一個類中,這樣的一個思路也就是橋接模式的實作,大家可以對照橋接模式的實作代碼來解決我們的分析思路。
上面定義部分已經給出了我們橋接模式的目的以及實作思路了,下面讓我們具體看看橋接模式是如何解決引言部分設計的不足。
抽象化部分的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<code>/// <summary></code>
<code> </code><code>/// 抽象概念中的遙控器,扮演抽象化角色</code>
<code> </code><code>/// </summary></code>
<code> </code><code>public</code> <code>class</code> <code>RemoteControl</code>
<code> </code><code>{</code>
<code> </code><code>// 字段</code>
<code> </code><code>private</code> <code>TV implementor;</code>
<code> </code><code>// 屬性</code>
<code> </code><code>public</code> <code>TV Implementor</code>
<code> </code><code>{</code>
<code> </code><code>get</code> <code>{ </code><code>return</code> <code>implementor; }</code>
<code> </code><code>set</code> <code>{ implementor = value; }</code>
<code> </code><code>}</code>
<code> </code><code>/// <summary></code>
<code> </code><code>/// 開電視機,這裡抽象類中不再提供實作了,而是調用實作類中的實作</code>
<code> </code><code>/// </summary></code>
<code> </code><code>public</code> <code>virtual</code> <code>void</code> <code>On()</code>
<code> </code><code>implementor.On();</code>
<code> </code><code>/// 關電視機</code>
<code> </code><code>public</code> <code>virtual</code> <code>void</code> <code>Off()</code>
<code> </code><code>implementor.Off();</code>
<code> </code><code>/// 換頻道</code>
<code> </code><code>public</code> <code>virtual</code> <code>void</code> <code>SetChannel()</code>
<code> </code><code>implementor.tuneChannel();</code>
<code> </code><code>}</code>
<code> </code><code>/// <summary></code>
<code> </code><code>/// 具體遙控器</code>
<code> </code><code>public</code> <code>class</code> <code>ConcreteRemote : RemoteControl</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>SetChannel()</code>
<code> </code><code>Console.WriteLine(</code><code>"---------------------"</code><code>);</code>
<code> </code><code>base</code><code>.SetChannel();</code>
遙控器的實作方法部分代碼,即實作化部分代碼,此時我們用另外一個抽象類TV封裝了遙控器功能的變化,具體實作交給具體型号電視機去完成:
<code> </code><code>/// 電視機,提供抽象方法</code>
<code> </code><code>public</code> <code>abstract</code> <code>class</code> <code>TV</code>
<code> </code><code>public</code> <code>abstract</code> <code>void</code> <code>On();</code>
<code> </code><code>public</code> <code>abstract</code> <code>void</code> <code>Off();</code>
<code> </code><code>public</code> <code>abstract</code> <code>void</code> <code>tuneChannel();</code>
<code> </code><code>/// 長虹牌電視機,重寫基類的抽象方法</code>
<code> </code><code>/// 提供具體的實作</code>
<code> </code><code>public</code> <code>class</code> <code>ChangHong : TV</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>On()</code>
<code> </code><code>Console.WriteLine(</code><code>"長虹牌電視機已經打開了"</code><code>);</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>Off()</code>
<code> </code><code>Console.WriteLine(</code><code>"長虹牌電視機已經關掉了"</code><code>);</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>tuneChannel()</code>
<code> </code><code>Console.WriteLine(</code><code>"長虹牌電視機換頻道"</code><code>);</code>
<code> </code><code>/// 三星牌電視機,重寫基類的抽象方法</code>
<code> </code><code>public</code> <code>class</code> <code>Samsung : TV</code>
<code> </code><code>Console.WriteLine(</code><code>"三星牌電視機已經打開了"</code><code>);</code>
<code> </code><code>Console.WriteLine(</code><code>"三星牌電視機已經關掉了"</code><code>);</code>
<code> </code><code>Console.WriteLine(</code><code>"三星牌電視機換頻道"</code><code>);</code>
采用橋接模式的用戶端調用代碼:
<code> </code><code>/// 以電視機遙控器的例子來示範橋接模式</code>
<code> </code><code>class</code> <code>Client</code>
<code> </code><code>static</code> <code>void</code> <code>Main(</code><code>string</code><code>[] args)</code>
<code> </code><code>// 建立一個遙控器</code>
<code> </code><code>RemoteControl remoteControl = </code><code>new</code> <code>ConcreteRemote();</code>
<code> </code><code>// 長虹電視機</code>
<code> </code><code>remoteControl.Implementor = </code><code>new</code> <code>ChangHong();</code>
<code> </code><code>remoteControl.On();</code>
<code> </code><code>remoteControl.SetChannel();</code>
<code> </code><code>remoteControl.Off();</code>
<code> </code><code>Console.WriteLine();</code>
<code> </code><code>// 三星牌電視機</code>
<code> </code><code>remoteControl.Implementor = </code><code>new</code> <code>Samsung();</code>
<code> </code><code>Console.Read();</code>
上面橋接模式的實作中,遙控器的功能實作方法不在遙控器抽象類中去實作了,而是把實作部分用來另一個電視機類去封裝它,然而遙控器中隻包含電視機類的一個引用,同時這樣的設計也非常符合現實生活中的情況(我認為的現實生活中遙控器的實作——遙控器中并不包含換台,打開電視機這樣的功能的實作,遙控器隻是包含了電視機上這些功能的引用,然後紅外線去找到電視機上對應功能的的實作)。通過橋接模式,我們把抽象化和實作化部分分離開了,這樣就可以很好應對這兩方面的變化了。
看完橋接模式的實作後,為了幫助大家理清對橋接模式中類之間關系,這裡給出橋接模式的類圖結構:
介紹完橋接模式,讓我們看看橋接模式具體哪些優缺點。
優點:
把抽象接口與其實作解耦。
抽象和實作可以獨立擴充,不會影響到對方。
實作細節對客戶透明,對用于隐藏了具體實作細節。
缺點: 增加了系統的複雜度
我們再來看看橋接模式的使用場景,在以下情況下應當使用橋接模式:
如果一個系統需要在構件的抽象化角色和具體化角色之間添加更多的靈活性,避免在兩個層次之間建立靜态的聯系。
設計要求實作化角色的任何改變不應當影響用戶端,或者實作化角色的改變對用戶端是完全透明的。
需要跨越多個平台的圖形和視窗系統上。
一個類存在兩個獨立變化的次元,且兩個次元都需要進行擴充。
橋接模式也經常用于具體的系統開發中,對于三層架構中就應用了橋接模式,三層架構中的業務邏輯層BLL中通過橋接模式與資料操作層解耦(DAL),其實作方式就是在BLL層中引用了DAL層中一個引用。這樣資料操作的實作可以在不改變用戶端代碼的情況下動态進行更換,下面看一個簡單的示例代碼:
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<code>// 用戶端調用</code>
<code> </code><code>// 類似Web應用程式</code>
<code> </code><code>BusinessObject customers = </code><code>new</code> <code>CustomersBusinessObject(</code><code>"ShangHai"</code><code>);</code>
<code> </code><code>customers.Dataacces = </code><code>new</code> <code>CustomersDataAccess();</code>
<code> </code><code>customers.Add(</code><code>"小六"</code><code>);</code>
<code> </code><code>Console.WriteLine(</code><code>"增加了一位成員的結果:"</code><code>);</code>
<code> </code><code>customers.ShowAll();</code>
<code> </code><code>customers.Delete(</code><code>"王五"</code><code>);</code>
<code> </code><code>Console.WriteLine(</code><code>"删除了一位成員的結果:"</code><code>);</code>
<code> </code><code>Console.WriteLine(</code><code>"更新了一位成員的結果:"</code><code>);</code>
<code> </code><code>customers.Update(</code><code>"Learning_Hard"</code><code>);</code>
<code> </code><code>// BLL 層</code>
<code> </code><code>public</code> <code>class</code> <code>BusinessObject</code>
<code> </code><code>private</code> <code>DataAccess dataacess;</code>
<code> </code><code>private</code> <code>string</code> <code>city;</code>
<code> </code><code>public</code> <code>BusinessObject(</code><code>string</code> <code>city)</code>
<code> </code><code>this</code><code>.city = city;</code>
<code> </code><code>public</code> <code>DataAccess Dataacces</code>
<code> </code><code>get</code> <code>{ </code><code>return</code> <code>dataacess; }</code>
<code> </code><code>set</code> <code>{ dataacess = value; }</code>
<code> </code><code>// 方法</code>
<code> </code><code>public</code> <code>virtual</code> <code>void</code> <code>Add(</code><code>string</code> <code>name)</code>
<code> </code><code>Dataacces.AddRecord(name);</code>
<code> </code><code>public</code> <code>virtual</code> <code>void</code> <code>Delete(</code><code>string</code> <code>name)</code>
<code> </code><code>Dataacces.DeleteRecord(name);</code>
<code> </code><code>public</code> <code>virtual</code> <code>void</code> <code>Update(</code><code>string</code> <code>name)</code>
<code> </code><code>Dataacces.UpdateRecord(name);</code>
<code> </code><code>public</code> <code>virtual</code> <code>string</code> <code>Get(</code><code>int</code> <code>index)</code>
<code> </code><code>return</code> <code>Dataacces.GetRecord(index);</code>
<code> </code><code>public</code> <code>virtual</code> <code>void</code> <code>ShowAll()</code>
<code> </code><code>Console.WriteLine(</code><code>"{0}的顧客有:"</code><code>, city);</code>
<code> </code><code>Dataacces.ShowAllRecords();</code>
<code> </code><code>public</code> <code>class</code> <code>CustomersBusinessObject : BusinessObject</code>
<code> </code><code>public</code> <code>CustomersBusinessObject(</code><code>string</code> <code>city)</code>
<code> </code><code>: </code><code>base</code><code>(city) { }</code>
<code> </code><code>// 重寫方法</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>ShowAll()</code>
<code> </code><code>Console.WriteLine(</code><code>"------------------------"</code><code>);</code>
<code> </code><code>base</code><code>.ShowAll();</code>
<code> </code><code>/// 相當于三層架構中資料通路層(DAL)</code>
<code> </code><code>public</code> <code>abstract</code> <code>class</code> <code>DataAccess</code>
<code> </code><code>// 對記錄的增删改查操作</code>
<code> </code><code>public</code> <code>abstract</code> <code>void</code> <code>AddRecord(</code><code>string</code> <code>name);</code>
<code> </code><code>public</code> <code>abstract</code> <code>void</code> <code>DeleteRecord(</code><code>string</code> <code>name);</code>
<code> </code><code>public</code> <code>abstract</code> <code>void</code> <code>UpdateRecord(</code><code>string</code> <code>name);</code>
<code> </code><code>public</code> <code>abstract</code> <code>string</code> <code>GetRecord(</code><code>int</code> <code>index);</code>
<code> </code><code>public</code> <code>abstract</code> <code>void</code> <code>ShowAllRecords();</code>
<code> </code><code>public</code> <code>class</code> <code>CustomersDataAccess:DataAccess</code>
<code> </code><code>private</code> <code>List<</code><code>string</code><code>> customers =</code><code>new</code> <code>List<</code><code>string</code><code>>();</code>
<code> </code><code>public</code> <code>CustomersDataAccess()</code>
<code> </code><code>// 實際業務中從資料庫中讀取資料再填充清單</code>
<code> </code><code>customers.Add(</code><code>"Learning Hard"</code><code>);</code>
<code> </code><code>customers.Add(</code><code>"張三"</code><code>);</code>
<code> </code><code>customers.Add(</code><code>"李四"</code><code>);</code>
<code> </code><code>customers.Add(</code><code>"王五"</code><code>);</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>AddRecord(</code><code>string</code> <code>name)</code>
<code> </code><code>customers.Add(name);</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>DeleteRecord(</code><code>string</code> <code>name)</code>
<code> </code><code>customers.Remove(name);</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>UpdateRecord(</code><code>string</code> <code>updatename)</code>
<code> </code><code>customers[0] = updatename;</code>
<code> </code><code>public</code> <code>override</code> <code>string</code> <code>GetRecord(</code><code>int</code> <code>index)</code>
<code> </code><code>return</code> <code>customers[index];</code>
<code> </code><code>public</code> <code>override</code> <code>void</code> <code>ShowAllRecords()</code>
<code> </code><code>foreach</code> <code>(</code><code>string</code> <code>name </code><code>in</code> <code>customers)</code>
<code> </code><code>{</code>
<code> </code><code>Console.WriteLine(</code><code>" "</code> <code>+ name);</code>
<code> </code><code>}</code>
<code> </code>
到這裡,橋接模式的介紹就介紹,橋接模式實作了抽象化與實作化的解耦,使它們互相獨立互不影響到對方。
<a href="http://down.51cto.com/data/2363628" target="_blank">附件:http://down.51cto.com/data/2363628</a>
本文轉自LearningHard 51CTO部落格,原文連結:http://blog.51cto.com/learninghard/1310140,如需轉載請自行聯系原作者