重學設計模式之橋接模式

橋接模式

定義

將抽象部分與它的實現部分分離,使它們都可以獨立地變化。它是一種對象結構型模式,又稱為柄體(Handle and Body)模式或接口(Interface)模式。

上面的定義太簡單了點,并不能很好的解釋什么是橋接模式,看了很多文章覺得還是 LoveLin的解釋最為直接。

試想一下,當我們在繪畫時需要大中小三種型號的畫筆,并且能繪制12種顏色。當我們選擇蠟筆時,為了滿足這個需求,我們需要 12*3=36 支蠟筆。而同樣的情況,如果我們選擇油彩筆時,我們僅需3支不同型號的油彩筆,配合12種不同的顏料就可以了,總共需要 3+12=15 個物品。而且當我們需要增加一種型號的畫筆并且也需要繪制12種顏色,蠟筆需要增加12支,而油彩筆僅需要增加一支不同型號的筆就行。為什么同樣一個需求,選擇不同的畫筆會有不同的結果呢?

這里我們注意到繪畫需求中對畫筆有兩個屬性的需求,型號與顏色,這兩個屬性都是可變可拓展的,選擇蠟筆時每一支蠟筆上這兩個屬性都非常明確,這就導致了兩種屬性有多少種組合,我們就需要多少支蠟筆。而相對的,選擇油彩筆時,這兩個屬性是分開的,油彩筆僅僅具有型號的屬性,而顏色的屬性由顏料提供。

這就是橋接模式最生動的演示,當我們在軟件開發時,某一個類存在兩個獨立變化的維度時,通過橋接模式,可以將這兩個維度分離出來,使兩者可以單獨擴展變化,讓系統更符合“單一職責”原則。

UML圖

上面是橋接模式最常見的結構圖,它包含下面幾個角色:

  • Abstraction(抽象類):用于定義抽象類的接口,它一般是抽象類而不是接口,其中定義了一個Implementor(實現類接口)類型的對象并可以維護該對象,它與Implementor之間具有關聯關系,它既可以包含抽象業務方法,也可以包含具體業務方法。
  • RefinedAbstraction(擴充抽象類):擴充由Abstraction定義的接口,通常情況下它不再是抽象類而是具體類,它實現了在Abstraction中聲明的抽象業務方法,在RefinedAbstraction中可以調用在Implementor中定義的業務方法。
  • Implementor(實現類接口):定義實現類的接口,這個接口不一定要與Abstraction的接口完全一致,事實上這兩個接口可以完全不同,一般而言,Implementor接口僅提供基本操作,而Abstraction定義的接口可能會做更多更復雜的操作。Implementor接口對這些基本操作進行了聲明,而具體實現交給其子類。通過關聯關系,在Abstraction中不僅擁有自己的方法,還可以調用到Implementor中定義的方法,使用關聯關系來替代繼承關系。
  • ConcreteImplementor(具體實現類):具體實現Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同實現,在程序運行時,ConcreteImplementor對象將替換其父類對象,提供給抽象類具體的業務操作方法。

PS: 上面的介紹 copy的,解釋的很書面,讀不慣的還是直接看代碼吧 ??

具體到我們前面的例子,Abstraction對應的就是油彩筆的抽象對象,具有型號這個屬性,RefinedAbstraction對應的就是具體三種型號的油彩筆,Implementor對應的就是顏料的抽象對象,ConcreteImplementor對應的就是具體的有不同顏色的顏料。

代碼

//Abstraction抽象類,保持Implementor的引用
public abstract class Abstraction {

    protected Implementor impl;

    public void setImpl(Implementor impl){
        this.impl = impl;
    }
    
    //抽象操作方法
    public abstract void operation();
}


//Implementor 接口
public interface Implementor {

    void operationImpl();
}


//Abstraction抽象類的具體實現
public class DefindAbstraction extends Abstraction {
    @Override
    public void operation() {
        impl.operationImpl();
    }
}


//Implementor 接口的具體實現
public class ConcreteImplementorA implements Implementor {
    @Override
    public void operationImpl() {
        System.out.println("this is ConcreteImplementorA operation!");
    }
}

//Implementor 接口的具體實現
public class ConcreteImplementorB implements Implementor {
    @Override
    public void operationImpl() {
        System.out.println("this is ConcreteImplementorB operation!");
    }
}

客戶端調用代碼

public class Client {

    public static void main(String[] args) {
        Abstraction abstraction;
        Implementor implementor;

        abstraction = new DefindAbstraction();
        implementor = new ConcreteImplementorA();
        abstraction.setImpl(implementor);
        abstraction.operation();

        implementor = new ConcreteImplementorB();
        abstraction.setImpl(implementor);
        abstraction.operation();
    }
}

調用結果

this is ConcreteImplementorA operation!
this is ConcreteImplementorB operation!

創建不同維度的具體實現,通過橋接模式配合使用極大的簡化了系統復雜度。

實例

老規矩,還是來一個實例演示一下

某軟件公司欲開發一個數據轉換工具,可以將數據庫中的數據轉換成多種文件格式,例如txt、xml、pdf等格式,同時該工具需要支持多種不同的數據庫。試使用橋接模式對其進行設計。

分析需求可知,這里數據轉換的類型是一個維度,支持的數據庫類型也是一個維度,分離兩個維度進行設計。

UML圖

代碼

//抽象類 保持一個DaProviderImp的引用
public abstract class DataParser {

    protected DataProviderImp dpi;

    public void setDpi(DataProviderImp dpi) {
        this.dpi = dpi;
    }

    public abstract void parseData();
}



//DataProviderImp 接口
public interface DataProviderImp {

    String readData();
}

具體實現如下

public class TXTDataParser extends DataParser {
    @Override
    public void parseData() {
        String str = dpi.readData();
        System.out.println("Parse "+str+" to TXT");
        System.out.println("---------------------------------------");
    }
}


public class XMLDataParser extends DataParser {
    @Override
    public void parseData() {
        String str = dpi.readData();
        System.out.println("Parse "+str+" to XML");
        System.out.println("---------------------------------------");
    }
}

...


public class OracleDataProvider implements DataProviderImp {

    @Override
    public String readData() {
        System.out.println("Connect DB ---- Oracle");
        System.out.println("Read Data from Oracle");
        return "Data from Oracle";
    }
}


public class MysqlDataProvider implements DataProviderImp {
    @Override
    public String readData() {
        System.out.println("Connect DB ---- Mysql");
        System.out.println("Read Data from Mysql");
        return "Data from Mysql";
    }
}

...

客戶端代碼如下

public class Client {

    public static void main(String[] args){
        DataParser dataParser;
        DataProviderImp dataProviderImp;

        dataParser = new TXTDataParser();
        dataProviderImp = new OracleDataProvider();
        dataParser.setDpi(dataProviderImp);
        dataParser.parseData();

        dataParser = new XMLDataParser();
        dataProviderImp = new MysqlDataProvider();
        dataParser.setDpi(dataProviderImp);
        dataParser.parseData();

        dataParser = new PDFDataParser();
        dataProviderImp = new SqlServerDataProvider();
        dataParser.setDpi(dataProviderImp);
        dataParser.parseData();
    }
}

運行結果如下

Connect DB ---- Oracle
Read Data from Oracle
Parse Data from Oracle to TXT
---------------------------------------
Connect DB ---- Mysql
Read Data from Mysql
Parse Data from Mysql to XML
---------------------------------------
Connect DB ---- SqlServer
Read Data from SqlServer
Parse Data from SqlServer to PDF
---------------------------------------

小結

LovaLin的文章中曾提出過如果有兩個以上的維度該怎么解決,相信看完橋接模式的所有代碼后,大家應該有個答案,兩個維度或多個維度,多出的維度都可以分離出一個實現部分,通過抽象部分關聯來解決。

橋接模式極大的提高了提供的擴展性,且大大減少了系統代碼量,分離多個維度更符合“單一職責”原則,且各個維度的擴展都不用修改原系統代碼,符合“開閉原則”。但橋接模式的使用也會一定程度上增加系統的理解與設計難度,需要有一定的經驗才能很好的分別出系統的不同維度。


源碼:https://github.com/lichenming0516/DesignPattern

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • 在正式介紹橋接模式之前,我先跟大家談談兩種常見文具的區別,它們是毛筆和蠟筆。假如我們需要大中小3種型號的畫筆,能夠...
    justCode_閱讀 1,782評論 0 7
  • 1 場景問題# 1.1 發送提示消息## 考慮這樣一個實際的業務功能:發送提示消息。基本上所有帶業務流程處理的系統...
    七寸知架構閱讀 5,042評論 5 63
  • 目錄 本文的結構如下: 引言 什么是橋接模式 模式的結構 典型代碼 代碼示例 優點和缺點 適用環境 模式應用 一、...
    w1992wishes閱讀 1,765評論 0 6
  • 1.介紹 橋接模式,又稱為橋梁模式,是結構型設計模式之一。在現實生活中大家都知道“橋梁”是連接河道兩岸的主要交通樞...
    四會歌神陳子豪閱讀 5,360評論 3 18
  • 設計模式匯總 一、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用、多...
    MinoyJet閱讀 3,960評論 1 15