設(shè)計(jì)模式-橋接模式

橋接模式介紹

橋接模式(Bridge Pattern)也稱為橋梁模式,是結(jié)構(gòu)型設(shè)計(jì)模式之一。顧名思義其與現(xiàn)實(shí)中的橋梁作用相同,有連接兩岸的作用。

橋接模式定義

將抽象部分和實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地進(jìn)行變化。

橋接模式使用場景

  1. 對于那些不希望使用繼承或因?yàn)槎鄬哟卫^承導(dǎo)致系統(tǒng)類的個數(shù)急劇增加的系統(tǒng),橋接模式尤為適用。
  2. 一個類存在兩個獨(dú)立變化的維度,且這兩個維度都需要進(jìn)行擴(kuò)展。

橋接模式 UML 圖


角色介紹:

  • Abstraction:抽象部分。該類保持了一個對象實(shí)現(xiàn)部分的引用,該類或者繼承類可通過調(diào)用實(shí)現(xiàn)部分對象的方法來完成一些功能。
  • RefinedAbstraction:優(yōu)化的抽象部分。該類是對抽象部分的方法進(jìn)行完善和擴(kuò)展。
  • Implementor:實(shí)現(xiàn)部分。定義一類功能的基本操作。
  • ConcreteImplementor:具體實(shí)現(xiàn)部分。

橋接模式實(shí)現(xiàn)

現(xiàn)實(shí)生活中橋接模式的應(yīng)用很多,比如喝咖啡,杯子大小可以分為大杯、小杯,甜度可以分為加糖、不加糖,杯子大小與加糖與否是獨(dú)立變化的,但對于咖啡兩者又存在聯(lián)系;再比如筆,粗細(xì)可以分為0.7mm 筆頭,0.5mm筆頭,顏色可以分為紅色、黑色,兩者彼此獨(dú)立,也存在聯(lián)系。

看完例子可能并不能體會到橋接模式,下面就筆的生產(chǎn)為例,筆頭有0.7mm、有0.5mm筆頭,暫且我們以大、小來區(qū)分,我們設(shè)計(jì)的類結(jié)構(gòu)如下:


沒毛病,這時假如我們要生產(chǎn)兩種顏色的筆,分別為黑色、紅色。這時比較直接的設(shè)計(jì)如下:

看似沒毛病,細(xì)思極恐,這才是項(xiàng)目初期,假如我們筆的粗細(xì)還增加了0.3mm,0.25mm等,顏色增加到了 5 種顏色;再假如我們除了支持筆的粗細(xì)、筆的顏色、還支持帶筆帽、或者為自動筆等,這樣顯著問題就是類的數(shù)量爆棚,另一個問題就是牽一發(fā)而動全身。對象的繼承關(guān)系實(shí)在編譯就定義好了,所以無法運(yùn)行時改變從父類繼承的實(shí)現(xiàn)。子類的實(shí)現(xiàn)與它的父類就非常緊密的依賴,以至于父類實(shí)現(xiàn)中的任何變化都會導(dǎo)致子類的變化。這種依賴關(guān)系限制了靈活性并最終限制了復(fù)用性。

筆的大小與顏色變化獨(dú)立,屬于兩個維度,我們可以將其分離,重新設(shè)計(jì)如下:


這樣筆的大小和顏色就分離開來,兩者可以獨(dú)立變化,筆支持持有了顏色的引用,在這里是聚合的關(guān)系,當(dāng)需要生產(chǎn)黑色 0.77mm筆時,只需要在 BigPen 中通過Color的應(yīng)用設(shè)置顏色即可。這種就是橋接模式。
抽象部分(Abstraction)

public abstract class Pen {
    protected Color color;

    public Pen(Color color) {
        this.color = color;
    }

    public abstract void draw();
}

優(yōu)化的抽象部分(RefinedAbstraction)

public class BigPen extends Pen {
    public BigPen(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        System.out.println("0.7mm的" + color.makeColor() + "筆");
    }

}
public class SmallPen extends Pen {
    public SmallPen(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        System.out.println("0.5mm的" + color.makeColor() + "筆");
    }
}

實(shí)現(xiàn)部分

public interface Color {
    String makeColor();
}

具體的實(shí)現(xiàn)部分

public class BlackColor implements Color {
    @Override
    public String makeColor() {
        return "黑色";
    }
}
public class RedColor implements Color {
    @Override
    public String makeColor() {
        return "紅色";
    }
}

客戶端

public class Client {
    public static void main(String[] args) {
        Color redColor = new RedColor();
        Color blackColor = new BlackColor();
        Pen pen1 = new BigPen(redColor);
        Pen pen2 = new BigPen(blackColor);
        Pen pen3 = new SmallPen(redColor);
        Pen pen4 = new SmallPen(blackColor);
        pen1.draw();
        pen2.draw();
        pen3.draw();
        pen4.draw();
    }
}

運(yùn)行結(jié)果:

0.7mm的紅色筆
0.7mm的黑色筆
0.5mm的紅色筆
0.5mm的黑色筆

如果需要增加是否帶筆帽,只需新建接口,在 Pen 中傳入即可,只增加必要的類,并且每個維度可以獨(dú)立變化,也符合了開放-封閉原則。

總結(jié)

優(yōu)先使用對象的組合、聚合有助于保持每個類的封裝,并被集中在單個任務(wù)上。這樣類和類繼承層次可以保持較小規(guī)模。

優(yōu)點(diǎn)
1.分離抽象接口及其實(shí)現(xiàn)部分。提高了比繼承更好的解決方案。
2.橋接模式提高了系統(tǒng)的可擴(kuò)充性,在兩個變化維度中任意擴(kuò)展一個維度,都不需要修改原有系統(tǒng)。
3.實(shí)現(xiàn)細(xì)節(jié)對客戶透明,客戶無需關(guān)心實(shí)現(xiàn)細(xì)節(jié),它已經(jīng)由抽象層通過聚合關(guān)系完成了封裝。

注意事項(xiàng)
橋接模式是非常簡單的,使用該模式時主要考慮如何拆分抽象和實(shí)現(xiàn),并不是一涉及繼承就要考慮使用該模式,那還要繼承干什么呢?橋接模式的意圖還是對變化的封裝,盡量把可能變化的因素封裝到最細(xì)、最小的邏輯單元中,避免風(fēng)險(xiǎn)擴(kuò)散。因此讀者在進(jìn)行系統(tǒng)設(shè)計(jì)時,發(fā)現(xiàn)類的繼承有N層時,可以考慮使用橋接模式。

Android 源碼中的橋接模式

在應(yīng)用層,對于普通控件、View 和 Canvas 就可以看做橋接模式,View 為抽象角色,Canvas 為實(shí)現(xiàn)角色。
在 Framwork 內(nèi)部的源碼中,比較經(jīng)典的就是 Window 、WindowManager 和 WindowManagerService 之間的關(guān)系,如下圖:


  • Window 和 PhoneWindow 構(gòu)成了窗口的抽象部分,Window 為抽象接口,PhoneWindow 為抽象部分的具體實(shí)現(xiàn)及擴(kuò)展。
  • WindowManager 為實(shí)現(xiàn)部分的實(shí)現(xiàn)接口,WindowManagerImpl 為實(shí)現(xiàn)部分的具體邏輯實(shí)現(xiàn)。

這就是橋接模式。其中 WindowManagerImpl 使用 WindowManagerGlobal 對象,通過 IWindowManager 接口與 WindowManagerService 進(jìn)行交互,并有 WMS 完成具體的窗口管理工作。

Window 和 WindowManager 的橋梁搭建的主要代碼如下:
Window.java

public abstract class Window {
    private WindowManager mWindowManager;
    ...
    
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

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

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