天天看點

橋接模式(Bridge Pattern)-(最通俗易懂的案例)1.定義2.适用環境3.模式結構圖4.執行個體5.優缺點分析

1.定義

橋接模式(Bridge Pattern):将抽象部分與它的實作部分分離,使它們都可以獨立地變化。

問題:

這裡的抽象與實作是什麼意思呢?先來看一個例子:

假如你有一個幾何形狀Shape類,從它能擴充出兩個子類: ​ 圓形Circle和 方形Square 。 你希望對這樣的類層次結構進行擴充以使其包含顔色,是以你打算建立名為紅色Red和藍色Blue的形狀子類。 但是, 由于你已有兩個子類, 是以總共需要建立四個類才能覆寫所有組合, 例如 藍色圓形Blue­Circle和 紅色方形Red­Square 。

橋接模式(Bridge Pattern)-(最通俗易懂的案例)1.定義2.适用環境3.模式結構圖4.執行個體5.優缺點分析

在層次結構中新增形狀和顔色将導緻代碼複雜程度指數增長。 例如添加三角形狀, 你需要新增兩個子類, 也就是每種顔色一個; 此後新增一種新顔色需要新增三個子類, 即每種形狀一個。 照這樣下去,所有組合類的數量将以幾何級數增長,情況會越來越糟糕。

解決方案:

問題的根本原因在于我們試圖在兩個獨立的次元——形狀與顔色上進行擴充。這在處理繼承時是很常見的問題。

橋接模式 通過将繼承改為組合的方式來解決這個問題。 具體來說, 就是抽取其中一個次元并使之成為獨立的類層次, 這樣就可以在初始類中引用這個新層次的對象, 進而使得一個類不必擁有所有的狀态和行為。

橋接模式(Bridge Pattern)-(最通俗易懂的案例)1.定義2.适用環境3.模式結構圖4.執行個體5.優缺點分析

根據該方法, 我們可以将顔色相關的代碼抽取到擁有 紅色和 藍色兩個子類的顔色類中, 然後在 形狀類中添加一個指向某一顔色對象的引用成員變量。 現在, 形狀類可以将所有與顔色相關的工作委派給連入的顔色對象。 這樣的引用就成為了 形狀和 顔色之間的橋梁。 此後, 新增顔色将不再需要修改形狀的類層次, 反之亦然。

2.适用環境

  • 一個類存在兩個獨立變化的次元,且這兩個次元都需要進行擴充。
  • 如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜态的繼承聯系,通過橋接模式可以使它們在抽象層建立一個關聯關系。
  • 對于那些不希望使用繼承或因為多層次繼承導緻系統類的個數急劇增加的系統,橋接模式尤為适用。

3.模式結構圖

橋接模式(Bridge Pattern)-(最通俗易懂的案例)1.定義2.适用環境3.模式結構圖4.執行個體5.優缺點分析

其中包含如下角色:

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

4.執行個體

我們就以上述形狀與顔色這兩個獨立的次元來實作給不同的形狀刷上不同顔色的例子來講解:

ColorAPI :用于畫各種顔色的接口

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public interface ColorAPI {
    public void paint();
}
           

BlueColorAPI :畫藍色的實作類

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public class BlueColorAPI implements ColorAPI {
    @Override
    public void paint() {
        System.out.println("畫上藍色");
    }
}
           

RedColorAPI :畫紅色的實作類

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public class RedColorAPI implements ColorAPI
{
    @Override
    public void paint() {
        System.out.println("畫上紅色");
    }
}
           

Shape :抽象形狀類

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public abstract class Shape {
    protected ColorAPI colorAPI;    //添加一個顔色的成員變量以調用ColorAPI 的方法來實作給不同的形狀上色

    public void setDrawAPI(ColorAPI colorAPI) {      //注入顔色成員變量
        this.colorAPI= colorAPI;
    }
 
    public abstract void draw();        
}
           

Circle :圓形類

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public class Circle extends Shape {
    @Override
    public void draw() {
        System.out.print("我是圓形");
        colorAPI.paint();
    }
}
           

Rectangle :長方形類

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public class Rectangle extends Shape {
    @Override
    public void draw() {
        System.out.print("我是長方形");
        colorAPI.paint();
    }
}
           

Client:用戶端

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public class Client {

    public static void main(String[] args) {
        //建立一個圓形
        Shape shape = new Circle();
        //給圓形藍色的顔料
        shape.setDrawAPI(new BlueColorAPI());
        //上色
        shape.draw();


        //建立一個長方形
        Shape shape1 = new Rectangle();
        //給長方形紅色的顔料
        shape1.setDrawAPI(new RedColorAPI());
        //上色
        shape1.draw();

    }
}

           

列印輸出:

我是圓形畫上藍色
我是長方形畫上紅色
           

假如現在客戶讓我們增了一個三角形,我們隻需要新增一個三角形類就可以了,而無需把每一種顔色都增加一個,我們在用戶端調用時隻需按照需求來挑選即可:

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("我是三角形");
        colorAPI.paint();
    }
}
           

增加顔色也是一樣,我們隻需要增加一個新的顔色并實作ColorAPI的接口即可,而無需更改類的層次,例如增加一個綠色:

/**
 * Created on 2020/3/18
 * Package com.design_pattern.bridge
 *
 * @author dsy
 */
public class GreenColorAPI implements ColorAPI {
    @Override
    public void paint() {
        System.out.println("畫上綠色");
    }
}
           

現在再來看“将抽象部分與他的實作部分分離”這句話,實際上就是在說實作系統可能有多個角度分類(例如例子中的形狀與顔色),每一種分類都有可能變化,那麼把這種多角度分離出來讓他們獨立變化,減少他們之間的耦合。

5.優缺點分析

優點:

  • 實作抽象和實作的分離
  • 橋接模式提高了系統的可擴充性,在兩個變化次元中任意擴充一個次元,都不需要修改原有系統
  • 橋接模式有時類似于多繼承方案,但是多繼承方案違背了類的單一職責原則(即一個類隻有一個變化的原因),複用性比較差,而且多繼承結構中類的個數非常龐大,橋接模式是比多繼承方案更好的解決方法

缺點:

  • 橋接模式的引入會增加系統的了解與設計難度,由于聚合關聯關系建立在抽象層,要求開發者針對抽象進行設計與程式設計。
  • 橋接模式要求正确識别出系統中兩個獨立變化的次元,是以其使用範圍具有一定的局限性。