橋接模式

簡介

Decouple an abstraction from its implementation so that the two can vary independently.
解耦抽象和實(shí)現(xiàn),使得兩者可以獨(dú)立的變化。

橋接模式(Bridge Pattern) 也稱為 橋梁模式、接口(Interfce)模式柄體(Handle and Body)模式,隸屬對(duì)象結(jié)構(gòu)型模式。

橋接模式 類似于多重繼承方案,但是多重繼承方案往往違背了類得單一職責(zé)原則,其復(fù)用性比較差,橋接模式 是比多重繼承更好的替代方案。

橋接模式 的核心在于 解耦抽象和實(shí)現(xiàn)

:此處的 抽象 并不是指 抽象類接口 這種高層概念,實(shí)現(xiàn) 也不是 繼承接口實(shí)現(xiàn) 。抽象實(shí)現(xiàn) 其實(shí)指的是兩種獨(dú)立變化的維度。其中,抽象 包含 實(shí)現(xiàn),因此,一個(gè) 抽象 類的變化可能涉及到多種維度的變化導(dǎo)致的。

主要解決

當(dāng)一個(gè)類內(nèi)部具備兩種或多種變化維度時(shí),使用 橋接模式 可以解耦這些變化的維度,使高層代碼架構(gòu)穩(wěn)定。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 抽象和實(shí)現(xiàn)分離:這是 橋接模式 的主要特點(diǎn),也是避免使用 繼承 的主要原因。使用 橋接模式,解耦了 抽象實(shí)現(xiàn),使得兩者的變化不會(huì)對(duì)另一方產(chǎn)生影響,使得系統(tǒng)擴(kuò)展性大大增強(qiáng)。
  • 優(yōu)秀的擴(kuò)展能力橋接模式 的出現(xiàn)就是為了解決多個(gè)獨(dú)立變化的維度的耦合,其高層模塊聚合關(guān)系已確定(穩(wěn)定)。因此,無論是 抽象 變化還是 實(shí)現(xiàn) 變化,只要對(duì)其進(jìn)行擴(kuò)展即可,高層代碼無需任何更改即可接收擴(kuò)展。高層代碼依賴抽象,嚴(yán)格遵循了 依賴倒置原則。
  • 實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶透明:由于 橋接模式 在高層模塊(抽象層)通過聚合關(guān)系構(gòu)建了穩(wěn)定的架構(gòu)(封裝)。因此,客戶只需與高層模塊交互,對(duì) 抽象實(shí)現(xiàn) 的細(xì)節(jié)完全不需關(guān)注。

缺點(diǎn)

  • 橋接模式 由于聚合關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者針對(duì)抽象進(jìn)行設(shè)計(jì)與編程,因此會(huì)增加系統(tǒng)的理解與設(shè)計(jì)難度。

使用場景

  • 一個(gè)類存在兩個(gè)(或多個(gè))獨(dú)立變化的維度,且這兩個(gè)維度都需要進(jìn)行擴(kuò)展;
  • 不希望或不適合使用繼承的場景;

橋接模式 的一個(gè)常用使用場景就是為了替換 繼承。我們知道,繼承 擁有很多優(yōu)點(diǎn),比如 抽象封裝,多態(tài) 等,父類封裝共性,子類實(shí)現(xiàn)特性。繼承 可以很好地幫助我們實(shí)現(xiàn)代碼復(fù)用(封裝)的功能,但是同時(shí),這也是 繼承 的一大缺點(diǎn)。因?yàn)楦割悡碛械姆椒?,子類也?huì)繼承得到,無論子類需不需要,這說明了 繼承 具備 強(qiáng)侵入性(父類代碼侵入子類),同時(shí)會(huì)導(dǎo)致子類臃腫······因此,在設(shè)計(jì)模式中,有一個(gè)原則為:優(yōu)先使用組合/聚合的方式,而不是繼承。
但是,設(shè)計(jì)模式是死的,人是活的。很多時(shí)候,你分不清該使用 繼承 還是 組合/聚合 或其他方式等,可以從 現(xiàn)實(shí)語義 進(jìn)行思考,因?yàn)檐浖ùa)最終還是提供給現(xiàn)實(shí)生活中的人用的,是服務(wù)于人類社會(huì)的,軟件(代碼)是具備現(xiàn)實(shí)場景的,單你從純代碼角度無法看清問題時(shí),現(xiàn)實(shí)角度可能會(huì)給你提供更加開闊的思路。

模式講解

首先來看下 橋接模式 的通用 UML 類圖:

橋接模式

從 UML 類圖中,我們可以看到,橋接模式 主要包含四種角色:

  • 抽象(Abstraction):該類持有一個(gè)對(duì) 實(shí)現(xiàn) 角色的引用,抽象 角色中的方法需要 實(shí)現(xiàn) 角色來實(shí)現(xiàn)。抽象 角色一般為抽象類(構(gòu)造函數(shù)規(guī)定子類要傳入一個(gè) 實(shí)現(xiàn) 對(duì)象);
  • 修正抽象(RefinedAbstraction)Abstraction 的具體實(shí)現(xiàn),對(duì) Abstraction 的方法進(jìn)行完善和擴(kuò)展;
  • 實(shí)現(xiàn)(Implementor):定義 實(shí)現(xiàn) 維度的基本操作,提供給 Abstraction 使用。該類一般為接口或抽象類;
  • 具體實(shí)現(xiàn)(ConcreteImplementor)Implementor 的具體實(shí)現(xiàn);

下面是 橋接模式 的通用代碼:

package com.yn.design_pattern.bridge.universal;

class Client {
    public static void main(String[] args) {
        // 來一個(gè)實(shí)現(xiàn)化角色
        IImplementor imp = new ConcreteImplementor();
        // 來一個(gè)抽象化角色,聚合實(shí)現(xiàn)
        Abstraction abs = new RefinedAbstraction(imp);
        // 執(zhí)行操作
        abs.operation();
    }

    // 抽象實(shí)現(xiàn)
    interface IImplementor {
        void operationImpl();
    }

    // 具體實(shí)現(xiàn)
    static class ConcreteImplementor implements IImplementor {

        @Override
        public void operationImpl() {
            System.out.println("I'm ConcreteImplementor A");
        }
    }

    // 抽象
    abstract static class Abstraction {
        protected IImplementor mImplementor;

        public Abstraction(IImplementor implementor) {
            this.mImplementor = implementor;
        }

        public void operation() {
            this.mImplementor.operationImpl();
        }
    }

    // 修正抽象
    static class RefinedAbstraction extends Abstraction {
        public RefinedAbstraction(IImplementor implementor) {
            super(implementor);
        }

        @Override
        public void operation() {
            super.operation();
            System.out.println("refined operation");
        }
    }
}

舉個(gè)例子

下面給個(gè)示例,讓我們能更好地理解 橋接模式。
例子:我們?nèi)タХ瑞^喝咖啡,一般有4種選擇:大杯加糖,大杯不加糖,小杯加糖,小杯不加糖。那么怎樣用代碼模擬選擇咖啡呢?

分析:按上面的例子,很簡單,咖啡就只有4種類型,因此我們建立一個(gè)“大杯加糖類”,“大杯不加糖類”,“小杯加糖類“,”小杯不加糖類“就可以覆蓋上面例子所列舉的咖啡了。但是,這樣子的話咖啡杯和味道(糖)就耦合在一起了,后續(xù)如果有其他形式的咖啡,我們只能采用繼承修改的方式擴(kuò)展,繼承類之間存在冗余部分,因此并不是很好的處理方案。
我們?cè)倩仡櫳厦娴睦樱槐Х劝瑑煞N選擇:大小杯,有無糖(味道)。也就是通過這兩個(gè)維度就可以決定一杯咖啡。涉及多個(gè)獨(dú)立維度,我們很清楚應(yīng)該使用 橋接模式。這里有兩個(gè)獨(dú)立維度:大小杯,有無糖(味道)。按 橋接模式 來說,應(yīng)該有兩個(gè) 實(shí)現(xiàn):大小杯,有無糖(味道)。但是兩個(gè) 實(shí)現(xiàn) 有點(diǎn)冗余,完全可以把 “大小杯” 這個(gè)維度歸并到咖啡本身屬性中,從而這個(gè)例子的維度變成:咖啡大小杯,有無糖(味道)。

代碼如下所示:

package com.yn.design_pattern.bridge.exam;

class Client {
    public static void main(String[] args) {
        // 選擇咖啡味道:原味
        ICoffeeFlavor flavor = new PlainFlavor();
        // 選擇大杯咖啡
        Coffee coffee = new LargeCoffee(flavor);
        // 選擇完畢:大杯咖啡:原味
        coffee.makeCoffee();

        // 大杯咖啡:加糖
        new LargeCoffee(new SugarFlavor()).makeCoffee();
        // 小杯咖啡:原味
        new SmallCoffee(new PlainFlavor()).makeCoffee();
        // 小杯咖啡:加糖
        new SmallCoffee(new SugarFlavor()).makeCoffee();
    }

    // Implementor:有無糖
    interface ICoffeeFlavor {
        String addWhat();
    }

    // ConcreteImplementor:原味
    static class PlainFlavor implements ICoffeeFlavor {
        @Override
        public String addWhat() {
            return "原味";
        }
    }

    // ConcreteImplementor:加糖
    static class SugarFlavor implements ICoffeeFlavor {
        @Override
        public String addWhat() {
            return "加糖";
        }
    }

    // Abstraction:咖啡
    static abstract class Coffee {
        protected ICoffeeFlavor mFlavor;

        public Coffee(ICoffeeFlavor flavor) {
            this.mFlavor = flavor;
        }

        public abstract void makeCoffee();
    }

    // RefinedAbstraction:大杯咖啡
    static class LargeCoffee extends Coffee {
        public LargeCoffee(ICoffeeFlavor flavor) {
            super(flavor);
        }

        @Override
        public void makeCoffee() {
            System.out.println("大杯咖啡: " + this.mFlavor.addWhat());
        }
    }

    // RefinedAbstraction:小杯咖啡
    static class SmallCoffee extends Coffee {
        public SmallCoffee(ICoffeeFlavor flavor) {
            super(flavor);
        }

        @Override
        public void makeCoffee() {
            System.out.println("小杯咖啡:" + this.mFlavor.addWhat());
        }
    }
}

上面這個(gè)例子,我們采用 橋接模式 解耦了 “咖啡大小杯” 和 “有無糖(味道)” 這兩個(gè)獨(dú)立變化的維度。后續(xù)如果咖啡館提供更多的服務(wù),比如中杯咖啡,那么直接新建一個(gè)中杯類繼承Coffee即可;如果是咖啡味道更改,比如可以加牛奶,蜂蜜等,那么同樣只需新建一個(gè)類實(shí)現(xiàn)ICoffeeFlavor即可。

通過上面的例子,我們能很好地感知到 橋接模式 遵循了設(shè)計(jì)模式 里氏替換原則依賴倒置原則,最終實(shí)現(xiàn)了 開閉原則,對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 場景問題# 1.1 發(fā)送提示消息## 考慮這樣一個(gè)實(shí)際的業(yè)務(wù)功能:發(fā)送提示消息。基本上所有帶業(yè)務(wù)流程處理的系統(tǒng)...
    七寸知架構(gòu)閱讀 5,227評(píng)論 5 63
  • 介紹 橋接模式(Bridge Pattern) 也稱為橋梁模式,是結(jié)構(gòu)型設(shè)計(jì)模式之一。橋接模式的作用就是連接 "兩...
    任教主來也閱讀 393評(píng)論 0 1
  • 1.初識(shí)橋接模式 將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化。 Abstraction:抽象部分的接口。...
    王偵閱讀 1,017評(píng)論 0 7
  • 在正式介紹橋接模式之前,我先跟大家談?wù)剝煞N常見文具的區(qū)別,它們是毛筆和蠟筆。假如我們需要大中小3種型號(hào)的畫筆,能夠...
    justCode_閱讀 1,872評(píng)論 0 7
  • 【學(xué)習(xí)難度:★★★☆☆,使用頻率:★★★☆☆】直接出處:橋接模式梳理和學(xué)習(xí):https://github.com/...
    BruceOuyang閱讀 1,066評(píng)論 0 2

友情鏈接更多精彩內(nèi)容