步步为营 .NET 设计模式学习笔记 十三、Bridge (桥接模式)
概述
在軟件系統中,某些類型由于自身的邏輯,它具有兩個或多個維度的變化,那么如何應對這種“多維度的變化”?如何利用面向對象的技術來使得該類型能夠輕松的沿著多個方向進行變化,而又不引入額外的復雜度?這就要使用Bridge模式。
橋梁模式是一個非常有用的模式,也是比較復雜的一個模式。熟悉這個模式對于理解面向對象的設計原則,包括"開-閉"原則(OCP)以及組合/聚合復用原則(CARP)都很有幫助。理解好這兩個原則,有助于形成正確的設計思想和培養良好的設計風格。
意圖
將抽象部分與實現部分分離,使它們都可以獨立的變化。[GOF 《設計模式》],這里的抽象和實現并不一定是同一層次的概念,例如數據庫操作可以歸結為“增加、刪除和修改”。很多業務過程都是通過對數據庫的操作實現的,例如“庫存管理”中的“入庫”,這個業務動作的軟件實現可以描述為“在庫存表中增加一條記錄”,而“入庫”和“插入記錄”處于不同的業務層次。
<Design Pattern>結構圖
圖1 Bridge模式結構圖
可以看出,這個系統含有兩個等級結構,也就是:
- 由抽象化角色和修正抽象化角色組成的抽象化等級結構。
- 由實現化角色和兩個具體實現化角色所組成的實現化等級結構。
橋梁模式所涉及的角色有:
- 抽象化(Abstraction)角色:抽象化給出的定義,并保存一個對實現化對象的引用。
- 修正抽象化(Refined Abstraction)角色:擴展抽象化角色,改變和修正父類對抽象化的定義。
- 實現化(Implementor)角色:這個角色給出實現化角色的接口,但不給出具體的實現。必須指出的是,這個接口不一定和抽象化角色的接口定義相同,實際上,這兩個接口可以非常不一樣。實現化角色應當只給出底層操作,而抽象化角色應當只給出基于底層操作的更高一層的操作。
- 具體實現化(Concrete Implementor)角色:這個角色給出實現化角色接口的具體實現。
生活中的例子
橋接模式將抽象部分與它的實現分離,使它們能夠獨立地變化。一個普通的開關控制的電燈、電風扇等等,都是橋接的例子。開關的目的是將設備打開或關閉。實際的開關可以是簡單的雙刀拉鏈開關,也可以是調光開關。
圖2 使用電子開關例子的橋接對象圖
?
形象比喻
小時候我們都用蠟筆畫畫,一盒蠟筆12種顏色。一開始我都是用最小號的蠟筆畫個太陽公公、月亮婆婆足夠了。后來開始畫一些抽象派的作品,就得換中號的了,要不然畫個背景都要描半天,好一盒中號的也是12種顏色。再后來我開始轉向豪放派,中號就有些捉襟見肘了,只好換大號的了,好一盒大號的也只有12種顏色。你看,像我這樣不太出名的畫家就需要36種畫筆,哇,太麻煩了。但是據我觀察,另一些比我出名的畫家倒是沒有這么多筆,他們只有幾把刷子和一些顏料,這樣就解決了蠟筆的“種類爆炸”問題。如下圖所示:
我要用36種蠟筆
齊白石老先生只用3種毛筆和12種顏料
?
示例用例圖
控制程序開和關,與哪種類型的控制,如:電視,燈等等.正好符合橋接模式,用例圖如下:
代碼設計
先創建CntrlControl.cs:
/// <summary>/// control class/// </summary>public abstract class CntrlControl {/// <summary>/// turn on/// </summary>/// <returns></returns>public abstract string TurnOn();/// <summary>/// turn off/// </summary>/// <returns></returns>public abstract string TurnOff();}?
再創建Lamp.cs:
public class Lamp:CntrlControl{public override string TurnOn(){return "燈亮了";}public override string TurnOff(){return "燈關了";}}?
再創建Television.cs:
public class Television:CntrlControl{public override string TurnOn(){return "電視開了";}public override string TurnOff(){return "電視關了";}}?
再創建ControlCenter.cs:
/// <summary>/// control center/// </summary>public abstract class ControlCenter{private CntrlControl centerControl;public CntrlControl CenterControl{get {return centerControl;}set {centerControl = value;}}public ControlCenter(){ }public ControlCenter(CntrlControl cntrlControl){this.centerControl = cntrlControl;}public abstract string TurnOn();public abstract string TurnOff();}?
再創建LampControl.cs:
public class LampControl:ControlCenter{public override string TurnOn(){return "我房間的燈控制"+ CenterControl.TurnOn();}public override string TurnOff(){return "我房間的燈控制" + CenterControl.TurnOff();}public LampControl(){ }public LampControl(CntrlControl cntrlControl): base(cntrlControl){ }}再創建TVControl.cs:
public class TVControl:ControlCenter{public override string TurnOn(){return "客廳的電視控制"+ CenterControl.TurnOn();}public override string TurnOff(){return "客廳的電視控制" + CenterControl.TurnOff();}public TVControl(){ }public TVControl(CntrlControl cntrlControl): base(cntrlControl){ }}最后再調用:
public partial class Run : Form{public Run(){InitializeComponent();}private void btnRun_Click(object sender, EventArgs e){//-------------------------------------ControlCenter lampControl = new LampControl(new Lamp());rtbResult.AppendText("節能燈控制開始.\n");rtbResult.AppendText(lampControl.TurnOn() + "\n");rtbResult.AppendText(lampControl.TurnOff() + "\n");rtbResult.AppendText("節能燈控制結束.\n\n\n");ControlCenter tvControl = new TVControl(new Television());rtbResult.AppendText("電視控制開始.\n");rtbResult.AppendText(tvControl.TurnOn() + "\n");rtbResult.AppendText(tvControl.TurnOff() + "\n");rtbResult.AppendText("電視控制結束.\n");//-------------------------------------}}?
運行結果如下圖:
?
效果及實現要點
1.Bridge模式使用“對象間的組合關系”解耦了抽象和實現之間固有的綁定關系,使得抽象和實現可以沿著各自的維度來變化。
2.所謂抽象和實現沿著各自維度的變化,即“子類化”它們,得到各個子類之后,便可以任意它們,從而獲得不同平臺上的不同型號。
3.Bridge模式有時候類似于多繼承方案,但是多繼承方案往往違背了類的單一職責原則(即一個類只有一個變化的原因),復用性比較差。Bridge模式是比多繼承方案更好的解決方法。
4.Bridge模式的應用一般在“兩個非常強的變化維度”,有時候即使有兩個變化的維度,但是某個方向的變化維度并不劇烈——換言之兩個變化不會導致縱橫交錯的結果,并不一定要使用Bridge模式。
5.選擇合適的類型作為抽象化角色(第一維度)。
6.抽象化角色和實現化角色通過組合進行關聯。
7.抽象和實現不綁定,允許客戶端作切換。
適用性
在以下的情況下應當使用橋梁模式:
1.如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的聯系。
2.設計要求實現化角色的任何改變不應當影響客戶端,或者說實現化角色的改變對客戶端是完全透明的。
3.一個構件有多于一個的抽象化角色和實現化角色,系統需要它們之間進行動態耦合。
4.雖然在系統中使用繼承是沒有問題的,但是由于抽象化角色和具體化角色需要獨立變化,設計要求需要獨立管理這兩者。
5.從代碼角度來說,如果類型的繼承是處于2個目的(違背單一職責原則)的話可以使用Bridge模式避免過多的子類。
6.從應用角度來說, 如果應用會在多個維度上進行變化,客戶端希望兩個維度(場景、游戲模式)的對象相對獨立,動態耦合(客戶端決定哪個場景和哪個游戲模式耦合)的時候可以考慮Bridge模式。
總結
Bridge模式是一個非常有用的模式,也非常復雜,它很好的符合了開放-封閉原則和優先使用對象,而不是繼承這兩個面向對象原則。
轉載于:https://www.cnblogs.com/springyangwc/archive/2011/04/20/2023030.html
總結
以上是生活随笔為你收集整理的步步为营 .NET 设计模式学习笔记 十三、Bridge (桥接模式)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Razor.js,基于JavaScrip
- 下一篇: Tomcat源代码阅读系列之八:Tomc