Java橋接模式

概念

橋梁模式是對(duì)象的結(jié)構(gòu)模式。又稱為柄體(Handle and Body)模式或接口(Interface)模式扯罐。橋梁模式的用意是“將抽象化(Abstraction)與實(shí)現(xiàn)化(Implementation)脫耦,使得二者可以獨(dú)立地變化”

橋接模式的用意

橋梁模式雖然不是一個(gè)使用頻率很高的模式,但是熟悉這個(gè)模式對(duì)于理解面向?qū)ο蟮脑O(shè)計(jì)原則,包括“開-閉”原則以及組合/聚合復(fù)用原則都很有幫助偷仿。理解好這兩個(gè)原則,有助于形成正確的設(shè)計(jì)思想和培養(yǎng)良好的設(shè)計(jì)風(fēng)格宵蕉。

橋梁模式的用意是“將抽象化(Abstraction)與實(shí)現(xiàn)化(Implementation)脫耦酝静,使得二者可以獨(dú)立地變化”。這句話很短羡玛,但是第一次讀到這句話的人很可能都會(huì)思考良久而不解其意形入。

這句話有三個(gè)關(guān)鍵詞,也就是抽象化缝左、實(shí)現(xiàn)化和脫耦亿遂。理解這三個(gè)詞所代表的概念是理解橋梁模式用意的關(guān)鍵浓若。

1. 抽象化
從眾多的事物中抽取出共同的、本質(zhì)性的特征蛇数,而舍棄其非本質(zhì)的特征挪钓,就是抽象化。例如蘋果耳舅、香蕉碌上、生梨、 桃子等浦徊,它們共同的特性就是水果馏予。得出水果概念的過程,就是一個(gè)抽象化的過程盔性。要抽象霞丧,就必須進(jìn)行比較,沒有比較就無法找到在本質(zhì)上共同的部分冕香。共同特征是指那些能把一類事物與他類事物區(qū)分開來的特征蛹尝,這些具有區(qū)分作用的特征又稱本質(zhì)特征。因此抽取事物的共同特征就是抽取事物的本質(zhì)特征悉尾,舍棄非本質(zhì)的特征突那。 所以抽象化的過程也是一個(gè)裁剪的過程。在抽象時(shí)构眯,同與不同愕难,決定于從什么角度上來抽象。抽象的角度取決于分析問題的目的惫霸。

通常情況下务漩,一組對(duì)象如果具有相同的特征,那么它們就可以通過一個(gè)共同的類來描述它褪。如果一些類具有相同的特征,往往可以通過一個(gè)共同的抽象類來描述翘悉。

**2. 實(shí)現(xiàn)化
抽象化給出的具體實(shí)現(xiàn)茫打,就是實(shí)現(xiàn)化。

一個(gè)類的實(shí)例就是這個(gè)類的實(shí)例化妖混,一個(gè)具體子類是它的抽象超類的實(shí)例化老赤。

**3. 脫耦化
所謂耦合,就是兩個(gè)實(shí)體的行為的某種強(qiáng)關(guān)聯(lián)制市。而將它們的強(qiáng)關(guān)聯(lián)去掉抬旺,就是耦合的解脫,或稱脫耦祥楣。在這里开财,脫耦是指將抽象化和實(shí)現(xiàn)化之間的耦合解脫開汉柒,或者說是將它們之間的強(qiáng)關(guān)聯(lián)改換成弱關(guān)聯(lián)。

所謂強(qiáng)關(guān)聯(lián)责鳍,就是在編譯時(shí)期已經(jīng)確定的碾褂,無法在運(yùn)行時(shí)期動(dòng)態(tài)改變的關(guān)聯(lián);所謂弱關(guān)聯(lián)历葛,就是可以動(dòng)態(tài)地確定并且可以在運(yùn)行時(shí)期動(dòng)態(tài)地改變的關(guān)聯(lián)正塌。顯然,在Java語言中恤溶,繼承關(guān)系是強(qiáng)關(guān)聯(lián)乓诽,而聚合關(guān)系是弱關(guān)聯(lián)。

將兩個(gè)角色之間的繼承關(guān)系改為聚合關(guān)系咒程,就是將它們之間的強(qiáng)關(guān)聯(lián)改換成為弱關(guān)聯(lián)鸠天。因此,橋梁模式中的所謂脫耦孵坚,就是指在一個(gè)軟件系統(tǒng)的抽象化和實(shí)現(xiàn)化之間使用聚合關(guān)系而不是繼承關(guān)系粮宛,從而使兩者可以相對(duì)獨(dú)立地變化。這就是橋梁模式的用意卖宠。

橋梁模式的結(jié)構(gòu)

可以看出巍杈,這個(gè)系統(tǒng)含有兩個(gè)等級(jí)結(jié)構(gòu):

一、由抽象化角色和修正抽象化角色組成的抽象化等級(jí)結(jié)構(gòu)扛伍。

二筷畦、由實(shí)現(xiàn)化角色和兩個(gè)具體實(shí)現(xiàn)化角色所組成的實(shí)現(xiàn)化等級(jí)結(jié)構(gòu)。

橋梁模式所涉及的角色有:

●  抽象化(Abstraction)角色:抽象化給出的定義刺洒,并保存一個(gè)對(duì)實(shí)現(xiàn)化對(duì)象的引用鳖宾。

●  修正抽象化(RefinedAbstraction)角色:擴(kuò)展抽象化角色,改變和修正父類對(duì)抽象化的定義逆航。

●  實(shí)現(xiàn)化(Implementor)角色:這個(gè)角色給出實(shí)現(xiàn)化角色的接口鼎文,但不給出具體的實(shí)現(xiàn)。必須指出的是因俐,這個(gè)接口不一定和抽象化角色的接口定義相同拇惋,實(shí)際上,這兩個(gè)接口可以非常不一樣抹剩。實(shí)現(xiàn)化角色應(yīng)當(dāng)只給出底層操作撑帖,而抽象化角色應(yīng)當(dāng)只給出基于底層操作的更高一層的操作。

●  具體實(shí)現(xiàn)化(ConcreteImplementor)角色:這個(gè)角色給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn)澳眷。

抽象化角色就像是一個(gè)水杯的手柄胡嘿,而實(shí)現(xiàn)化角色和具體實(shí)現(xiàn)化角色就像是水杯的杯身。手柄控制杯身钳踊,這就是此模式別名“柄體”的來源衷敌。

對(duì)象是對(duì)行為的封裝勿侯,而行為是由方法實(shí)現(xiàn)的。在這個(gè)示意性系統(tǒng)里逢享,抽象化等級(jí)結(jié)構(gòu)中的類封裝了operation()方法罐监;而實(shí)現(xiàn)化等級(jí)結(jié)構(gòu)中的類封裝的是operationImpl()方法。當(dāng)然瞒爬,在實(shí)際的系統(tǒng)中往往會(huì)有多于一個(gè)的方法弓柱。

抽象化等級(jí)結(jié)構(gòu)中的方法通過向?qū)?yīng)的實(shí)現(xiàn)化對(duì)象的委派實(shí)現(xiàn)自己的功能,這意味著抽象化角色可以通過向不同的實(shí)現(xiàn)化對(duì)象委派侧但,來達(dá)到動(dòng)態(tài)地轉(zhuǎn)換自己的功能的目的矢空。

源代碼

/**
 * 抽象化角色類,它聲明了一個(gè)方法operation()禀横,并給出了它的實(shí)現(xiàn)屁药。
 * 這個(gè)實(shí)現(xiàn)是通過向?qū)崿F(xiàn)化對(duì)象的委派(調(diào)用operationImpl()方法)實(shí)現(xiàn)的。
 */
public abstract class Abstraction {

    //持有一個(gè)實(shí)現(xiàn)化(Implementor)角色接口
    private Implementor impl;

    public Implementor getImpl() {
        return impl;
    }

    public void setImpl(Implementor impl) {
        this.impl = impl;
    }

    //示例方法
    public void operation(){
        impl.operationImpl();
    }

}
/**
 * 修正抽象化角色柏锄,這里可以重寫父類的operation方法施逾,
 * 也可以不重寫故痊,不重寫即調(diào)用父類的operation方法
 */
public class RefinedAbstraction extends Abstraction {

    //其他操作方法
    public void otherOperation(){}

}
/**
 * 實(shí)現(xiàn)化(Implementor)角色接口
 */
public abstract class Implementor {

    /**
     * 示例方法,實(shí)現(xiàn)抽象部分需要的某些具體功能
     */
    public abstract void operationImpl();

}
/**
 * 具體實(shí)現(xiàn)化角色
 */
public class ConcreteImplementorA extends Implementor {
    @Override
    public void operationImpl() {
        System.out.println("實(shí)現(xiàn)A功能");
    }
}
/**
 * 具體實(shí)現(xiàn)化角色
 */
public class ConcreteImplementorB extends Implementor {
    @Override
    public void operationImpl() {
        System.out.println("實(shí)現(xiàn)B功能");
    }
}
/**
 * 客戶端
 */
public class Client {
    public static void main(String[] args) {
        Abstraction abstraction = new RefinedAbstraction();

        Implementor implA = new ConcreteImplementorA();

        abstraction.setImpl(implA);
        abstraction.operation();

        System.out.println("----");

        Implementor implB = new ConcreteImplementorB();

        abstraction.setImpl(implB);
        abstraction.operation();
    }
}

一般而言,實(shí)現(xiàn)化角色中的每個(gè)方法都應(yīng)當(dāng)有一個(gè)抽象化角色中的某一個(gè)方法與之對(duì)應(yīng)轨蛤,但是反過來則不一定朝氓。換言之缸逃,抽象化角色的接口比實(shí)現(xiàn)化角色的接口寬少辣。抽象化角色除了提供與實(shí)現(xiàn)化角色相關(guān)的方法之外,還有可能提供其他的方法笤成;而實(shí)現(xiàn)化角色則往往僅為實(shí)現(xiàn)抽象化角色的相關(guān)行為而存在评架。

使用橋梁模式解決發(fā)送信息問題

根據(jù)業(yè)務(wù)的功能要求,業(yè)務(wù)的變化具有兩個(gè)維度炕泳,一個(gè)維度是抽象的消息纵诞,包括普通消息、加急消息和特急消息培遵,這幾個(gè)抽象的消息本身就具有一定的關(guān)系浙芙,加急消息和特急消息會(huì)擴(kuò)展普通消息;另一個(gè)維度是在具體的消息發(fā)送方式上荤懂,包括系統(tǒng)內(nèi)短消息、郵件和手機(jī)短消息塘砸,這幾個(gè)方式是平等的节仿,可被切換的方式。



現(xiàn)在出現(xiàn)問題的根本原因掉蔬,就在于消息的抽象和實(shí)現(xiàn)是混雜在一起的廊宪,這就導(dǎo)致了一個(gè)緯度的變化會(huì)引起另一個(gè)緯度進(jìn)行相應(yīng)的變化矾瘾,從而使得程序擴(kuò)展起來非常困難。

要想解決這個(gè)問題箭启,就必須把這兩個(gè)緯度分開壕翩,也就是將抽象部分和實(shí)現(xiàn)部分分開,讓它們相互獨(dú)立傅寡,這樣就可以實(shí)現(xiàn)獨(dú)立的變化放妈,使擴(kuò)展變得簡(jiǎn)單。抽象部分就是各個(gè)消息的類型所對(duì)應(yīng)的功能荐操,而實(shí)現(xiàn)部分就是各種發(fā)送消息的方式芜抒。按照橋梁模式的結(jié)構(gòu),給抽象部分和實(shí)現(xiàn)部分分別定義接口托启,然后分別實(shí)現(xiàn)它們就可以了宅倒。


源碼

/**
 * 抽象化角色
 */
public abstract class AbstractMessage {

    //持有發(fā)送消息的實(shí)現(xiàn)化角色接口
    private MessageImplementor implementor;

    public void setImplementor(MessageImplementor implementor) {
        this.implementor = implementor;
    }

    //實(shí)際調(diào)用的是實(shí)現(xiàn)化角色的發(fā)送消息方法
    public void sendMessage(String msg){
        this.implementor.sendMessage(msg);
    }
}

/**
 * 普通消息
 */
public class SimpleMessage extends AbstractMessage {

    @Override
    public void sendMessage(String msg) {
        //普通消息不做任何處理
        super.sendMessage(msg);
    }
}

/**
 * 加急消息
 */
public class UrgencyMessage extends AbstractMessage {

    /**
     * 這里有點(diǎn)像裝飾模式,可以在
     * 實(shí)際調(diào)用發(fā)送消息之前加上具體的業(yè)務(wù)邏輯
     * @param msg
     */
    @Override
    public void sendMessage(String msg) {
        msg += "(這個(gè)是加急消息)";
        super.sendMessage(msg);
    }
}
/**
 * 實(shí)現(xiàn)消息發(fā)送的接口
 */
public interface MessageImplementor {

    /**
     * 發(fā)送消息
     * @param msg
     */
    public void sendMessage(String msg);

}

/**
 * 短信消息實(shí)現(xiàn)類
 */
public class MessageSMS implements MessageImplementor {
    @Override
    public void sendMessage(String msg) {
        System.out.println("發(fā)送短信:" + msg);
    }
}

/**
 * 郵件消息實(shí)現(xiàn)類
 */
public class MessageEmail implements MessageImplementor {
    @Override
    public void sendMessage(String msg) {
        System.out.println("郵件信息:" + msg);
    }
}
/**
 * 客戶端
 */
public class Client {
    public static void main(String[] args) {
        //發(fā)送短信
        MessageImplementor sms = new MessageSMS();
        //發(fā)送郵件
        MessageImplementor email = new MessageEmail();

        //普通消息
        AbstractMessage simpleMessage = new SimpleMessage();
        //加急消息
        AbstractMessage urgencyMessage = new UrgencyMessage();

        simpleMessage.setImplementor(sms);
        simpleMessage.sendMessage("test");
        simpleMessage.setImplementor(email);
        simpleMessage.sendMessage("test");

        urgencyMessage.setImplementor(sms);
        urgencyMessage.sendMessage("test");
        urgencyMessage.setImplementor(email);
        urgencyMessage.sendMessage("test");


    }
}

觀察上面的例子會(huì)發(fā)現(xiàn)屯耸,采用橋梁模式來實(shí)現(xiàn)拐迁,抽象部分和實(shí)現(xiàn)部分分離開了,可以相互獨(dú)立的變化疗绣,而不會(huì)相互影響线召。因此在抽象部分添加新的消息處理(特急消息),對(duì)發(fā)送消息的實(shí)現(xiàn)部分是沒有影響的持痰;反過來增加發(fā)送消息的方式(手機(jī)短消息)灶搜,對(duì)消息處理部分也是沒有影響的。

橋梁模式的優(yōu)點(diǎn)

1. 分離抽象和實(shí)現(xiàn)部分

橋梁模式分離了抽象部分和實(shí)現(xiàn)部分工窍,從而極大地提供了系統(tǒng)的靈活性割卖。讓抽象部分和實(shí)現(xiàn)部分獨(dú)立出來,分別定義接口患雏,這有助于對(duì)系統(tǒng)進(jìn)行分層鹏溯,從而產(chǎn)生更好的結(jié)構(gòu)化的系統(tǒng)。

2. 更好的擴(kuò)展性

橋梁模式使得抽象部分和實(shí)現(xiàn)部分可以分別獨(dú)立地?cái)U(kuò)展淹仑,而不會(huì)相互影響丙挽,從而大大提高了系統(tǒng)的可擴(kuò)展性。

橋梁模式在Java中的使用

橋梁模式在Java應(yīng)用中的一個(gè)非常典型的例子就是JDBC驅(qū)動(dòng)器匀借。JDBC為所有的關(guān)系型數(shù)據(jù)庫提供一個(gè)通用的界面颜阐。一個(gè)應(yīng)用系統(tǒng)動(dòng)態(tài)地選擇一個(gè)合適的驅(qū)動(dòng)器,然后通過驅(qū)動(dòng)器向數(shù)據(jù)庫引擎發(fā)出指令吓肋。這個(gè)過程就是將抽象角色的行為委派給實(shí)現(xiàn)角色的過程凳怨。
抽象角色可以針對(duì)任何數(shù)據(jù)庫引擎發(fā)出查詢指令,因?yàn)槌橄蠼巧⒉恢苯优c數(shù)據(jù)庫引擎打交道,JDBC驅(qū)動(dòng)器負(fù)責(zé)這個(gè)底層的工作肤舞。由于JDBC驅(qū)動(dòng)器的存在紫新,應(yīng)用系統(tǒng)可以不依賴于數(shù)據(jù)庫引擎的細(xì)節(jié)而獨(dú)立地演化;同時(shí)數(shù)據(jù)庫引擎也可以獨(dú)立于應(yīng)用系統(tǒng)的細(xì)節(jié)而獨(dú)立的演化李剖。兩個(gè)獨(dú)立的等級(jí)結(jié)構(gòu)如下圖所示芒率,左邊是JDBC API的等級(jí)結(jié)構(gòu),右邊是JDBC驅(qū)動(dòng)器的等級(jí)結(jié)構(gòu)篙顺。應(yīng)用程序是建立在JDBC API的基礎(chǔ)之上的偶芍。



應(yīng)用系統(tǒng)作為一個(gè)等級(jí)結(jié)構(gòu),與JDBC驅(qū)動(dòng)器這個(gè)等級(jí)結(jié)構(gòu)是相對(duì)獨(dú)立的慰安,它們之間沒有靜態(tài)的強(qiáng)關(guān)聯(lián)腋寨。應(yīng)用系統(tǒng)通過委派與JDBC驅(qū)動(dòng)器相互作用,這是一個(gè)橋梁模式的例子化焕。
JDBC的這種架構(gòu)萄窜,把抽象部分和具體部分分離開來,從而使得抽象部分和具體部分都可以獨(dú)立地?cái)U(kuò)展撒桨。對(duì)于應(yīng)用程序而言查刻,只要選用不同的驅(qū)動(dòng),就可以讓程序操作不同的數(shù)據(jù)庫凤类,而無需更改應(yīng)用程序穗泵,從而實(shí)現(xiàn)在不同的數(shù)據(jù)庫上移植;對(duì)于驅(qū)動(dòng)程序而言谜疤,為數(shù)據(jù)庫實(shí)現(xiàn)不同的驅(qū)動(dòng)程序佃延,并不會(huì)影響應(yīng)用程序。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末夷磕,一起剝皮案震驚了整個(gè)濱河市履肃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坐桩,老刑警劉巖尺棋,帶你破解...
    沈念sama閱讀 223,002評(píng)論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绵跷,居然都是意外死亡膘螟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門碾局,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荆残,“玉大人,你說我怎么就攤上這事净当∧谒梗” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評(píng)論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長嘿期。 經(jīng)常有香客問我,道長埋合,這世上最難降的妖魔是什么备徐? 我笑而不...
    開封第一講書人閱讀 60,237評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮甚颂,結(jié)果婚禮上蜜猾,老公的妹妹穿的比我還像新娘。我一直安慰自己振诬,他們只是感情好蹭睡,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赶么,像睡著了一般肩豁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辫呻,一...
    開封第一講書人閱讀 52,821評(píng)論 1 314
  • 那天清钥,我揣著相機(jī)與錄音,去河邊找鬼放闺。 笑死祟昭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的怖侦。 我是一名探鬼主播篡悟,決...
    沈念sama閱讀 41,236評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼匾寝!你這毒婦竟也來了搬葬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤旗吁,失蹤者是張志新(化名)和其女友劉穎踩萎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體很钓,經(jīng)...
    沈念sama閱讀 46,716評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡香府,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了码倦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片企孩。...
    茶點(diǎn)故事閱讀 40,928評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖袁稽,靈堂內(nèi)的尸體忽然破棺而出勿璃,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評(píng)論 5 351
  • 正文 年R本政府宣布补疑,位于F島的核電站歧沪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏莲组。R本人自食惡果不足惜诊胞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锹杈。 院中可真熱鬧撵孤,春花似錦、人聲如沸竭望。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咬清。三九已至闭专,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旧烧,已是汗流浹背喻圃。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粪滤,地道東北人斧拍。 一個(gè)月前我還...
    沈念sama閱讀 49,378評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像杖小,于是被迫代替她去往敵國和親肆汹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容