10.橋接模式設計思想

杨充發表於2024-11-06

10.橋接模式設計思想

目錄介紹
  • 01.橋接模式基礎

    • 1.1 橋接模式由來
    • 1.2 橋接模式定義
    • 1.3 橋接模式場景
    • 1.4 橋接模式思考
    • 1.5 解決的問題
  • 02.橋接模式實現

    • 2.1 羅列一個場景
    • 2.2 橋接結構
    • 2.3 橋接基本實現
    • 2.4 有哪些注意點
  • 03.橋接例項演示

    • 3.1 需求分析
    • 3.2 程式碼案例實現
    • 3.3 是否可以最佳化
    • 3.4 橋接設計
    • 3.5 演變程式碼案例
  • 04.橋接實現方式

    • 4.1 繼承和組合
    • 4.2 介面和內部類
  • 05.橋接模式分析

    • 5.1 橋接模式優點
    • 5.2 橋接模式缺點
    • 5.3 適用環境
    • 5.4 模式擴充
  • 06.外觀代理總結

    • 6.1 總結一下學習
    • 6.2 更多內容推薦

推薦一個好玩網站

一個最純粹的技術分享網站,打造精品技術程式設計專欄!程式設計進階網

https://yccoding.com/

01.橋接模式基礎

1.0 本部落格AI摘要

本文介紹了橋接模式的設計思想和實現方法。橋接模式透過將抽象部分與實現部分分離,使它們可以獨立變化,解決了多層繼承帶來的複雜性和耦合性問題。文章詳細講解了橋接模式的由來、定義、應用場景和實現步驟,並透過具體例項演示瞭如何在支付場景中使用橋接模式。此外,還討論了橋接模式的優缺點及其適用環境,提供了豐富的程式碼示例和進一步學習的資源連結。

1.1 橋接模式由來

設想如果要繪製矩形、圓形、橢圓、正方形,我們至少需要4個形狀類,但是如果繪製的圖形需要具有不同的顏色,如紅色、綠色、藍色等,此時至少有如下兩種設計方案:

  1. 第一種設計方案是為每一種形狀都提供一套各種顏色的版本。
  2. 第二種設計方案是根據實際需要對形狀和顏色進行組合。

對於有兩個變化維度(即兩個變化的原因)的系統,採用方案二來進行設計系統中類的個數更少,且系統擴充套件更為方便。設計方案二即是橋接模式的應用。橋接模式將繼承關係轉換為關聯關係,從而降低了類與類之間的耦合,減少了程式碼編寫量。

1.2 橋接模式定義

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

1.3 橋接模式場景

在實際應用中有許多場景,以下是一些常見的應用場景:更多內容

  1. 圖形介面庫:橋接模式可以用於圖形介面庫中的控制元件和主題的組合。
  2. 資料庫訪問層:橋接模式可以用於資料庫訪問層中的資料庫連線和資料庫驅動的組合。
  3. 訊息傳輸協議:橋接模式可以用於訊息傳輸協議中的訊息和傳輸方式的組合。訊息可以作為抽象部分,傳輸方式可以作為實現部分,透過橋接模式可以實現不同訊息和傳輸方式的組合,而不需要修改訊息傳輸協議的程式碼。
  4. 音訊和影片播放器:橋接模式可以用於音訊和影片播放器中的播放器和解碼器的組合。

1.4 橋接模式思考

當我們思考橋接模式時,以下幾個方面值得考慮:

  1. 系統的抽象和實現:首先,我們需要確定系統中的抽象部分和實現部分。抽象部分是指高層次的業務邏輯或功能,而實現部分是指底層的具體實現或細節。確定這兩個部分有助於我們理解系統的結構和關係。
  2. 動態選擇和切換:橋接模式允許在執行時動態地選擇和切換不同的實現。我們需要思考如何實現這種動態性,以及如何在系統中進行實際的選擇和切換。
  3. 解耦和靈活性:橋接模式的主要目標是解耦抽象和實現,使它們可以獨立地變化。我們需要思考如何透過橋接模式來實現解耦,以及如何提高系統的靈活性和可維護性。

1.5 解決的問題

橋接模式用一種巧妙的方式處理多層繼承存在的問題,用抽象關聯來取代傳統的多層繼承,將類之間的靜態繼承關係轉變為動態的組合關係,使得系統更加靈活,並易於擴充套件,有效的控制了系統中類的個數 (避免了繼承層次的指數級爆炸)。更多內容

02.橋接模式實現

2.1 羅列一個場景

假設有一個幾何形狀Shape類,從它能擴充套件出兩個子類:圓形Circle和方形Square。

你希望對這樣的類層次結構進行擴充套件以使其包含顏色,所以你打算建立名為紅色Red和藍色Blue的形狀子類。

但是,由於你已有兩個子類,所以總共需要建立四個類才能覆蓋所有組合,例如藍色圓形Blue-Circle和紅色方形Red-Square。在層次結構中新增形狀和顏色將導致程式碼複雜程度指數增長。在這種情況下,橋接模式就能起到作用,它將形狀和顏色解耦,使得兩者可以相對獨立地變化。

2.2 橋接結構

橋接模式包含如下角色:

  1. Abstraction:抽象類。主要負責定義出該角色的行為,幷包含一個對實現化物件的引用。
  2. RefinedAbstraction:擴充抽象類。是抽象化角色的子類,實現父類中的業務方法,並透過組合關係呼叫實現化角色中的業務方法。
  3. Implementor:實現類介面。定義實現化角色的介面,包含角色必須的行為和屬性,並供擴充套件抽象化角色呼叫。
  4. ConcreteImplementor:具體實現類。給出實現化角色介面的具體實現。

2.3 橋接基本實現

首先,我們建立一個抽象類 Shape,它有一個抽象方法 draw():更多內容

public abstract class Shape {
    public abstract void draw();
}

然後,我們建立兩個實現了 Shape 介面的具體類:Circle 和 Square:

public class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("畫一個圓形");
    }
}

public class Square extends Shape {
    @Override
    public void draw() {
        System.out.println("畫一個正方形");
    }
}

接下來,我們建立一個橋接類 Color,它也實現了 Shape 介面,並持有一個 Shape 型別的引用:

public class Color extends Shape {
    private Shape shape;

    public Color(Shape shape) {
        this.shape = shape;
    }

    @Override
    public void draw() {
        setColor();
        shape.draw();
        resetColor();
    }

    private void setColor() {
        System.out.println("設定顏色");
    }

    private void resetColor() {
        System.out.println("重置顏色");
    }
}

最後,我們在主函式中測試這個橋接模式:

private void test() {
    Shape circle = new Circle();
    Shape square = new Square();
    Shape coloredCircle = new Color(circle);
    Shape coloredSquare = new Color(square);
    coloredCircle.draw();
    coloredSquare.draw();
}

2.4 有哪些注意點

使用橋接模式時需要注意以下幾點:更多內容

  • 1、抽象部分和實現部分應該分離,不應該有過多的耦合。
  • 2、橋接模式適用於多個維度的變化,如果只有一兩個維度的變化,使用繼承會更加簡單。
  • 3、橋接模式會增加系統的複雜度,需要謹慎使用。
  • 4、橋接模式要求正確選擇和使用抽象類和介面,避免過度抽象或過於具體化。
  • 5、橋接模式的實現需要考慮物件的建立和管理,需要合理設計物件之間的關係和依賴關係。

03.橋接例項演示

3.1 需求分析

以當下支付場景為例,看下不同支付模式中橋接模式的應用。

如微信和支付寶都可以完成支付操作,而支付操作又可以有掃碼支付、密碼支付、人臉支付等,那麼關於支付操作其實就有兩個維度,包括:支付渠道和支付方式。

3.2 程式碼案例實現

不使用設計模式來模擬實現不同模式的支付場景。不使用設計模式缺點:維護和擴充套件都會變得非常複雜,需要修改原來程式碼,風險較大。更多內容

public class PayController {

    /**
     * @param uId         使用者id
     * @param tradeId     交易流水號
     * @param amount      交易金額
     * @param channelType 渠道型別 1 微信, 2 支付寶
     * @param modeType    支付模式 1 密碼,2 人臉,3 指紋
     * @return: boolean
     */
    public void doPay(String uId, String tradeId, BigDecimal amount, int channelType, int modeType) {
        //微信支付
        if (1 == channelType) {
            System.out.println("微信渠道支付劃賬開始......");
            if (1 == modeType) {
                System.out.println("密碼支付");
            } else if (2 == modeType) {
                System.out.println("人臉支付");
            } else if (3 == modeType) {
                System.out.println("指紋支付");
            }
        }

        //支付寶支付
        if (2 == channelType) {
            System.out.println("支付寶渠道支付劃賬開始......");
            if (1 == modeType) {
                System.out.println("密碼支付");
            } else if (2 == modeType) {
                System.out.println("人臉支付");
            } else if (3 == modeType) {
                System.out.println("指紋支付");
            }
        }
    }
}

使用一下,如下所示:

private void test1() {
    PayController payController = new PayController();
    System.out.println("測試: 微信支付、人臉支付方式");
    payController.doPay("weixin", "1000112333333", new BigDecimal(100), 1, 2);
    System.out.println("\n測試: 支付寶支付、指紋支付方式");
    payController.doPay("zhifubao", "1000112334567", new BigDecimal(100), 2, 3);
}

3.3 是否可以最佳化

雖然不使用設計模式也能實現該支付場景需求,但以後若增加支付渠道或修改支付方式,比如增加了京東支付,抖音支付,或者增加一個微信刷掌支付,則成本比較高,不利於後邊的擴充套件和維護。

使用橋接模式重構支付場景程式碼。

橋接模式原理的核心是: 首先有要識別出一個類所具有的的兩個獨立變化維度,將它們設計為兩個獨立的繼承等級結構,為兩個維度都提供抽象層,並建立抽象耦合。更多內容

針對該支付場景上邊已經抽出了2個維度,即:支付渠道 和 支付方式;這裡我們可以把支付渠道作為抽象化角色,支付方式作為實現化角色,支付渠道*支付模式 = 相對應的支付組合;

3.4 橋接設計

1)Pay抽象類(支付渠道)

  1. I)支付渠道子類: 微信支付
  2. II)支付渠道子類: 支付寶支付

2)IPayMode介面(支付方式)

  1. I)支付模式實現: 刷臉支付
  2. II)支付模式實現: 指紋支付
  3. III)密碼支付

3.5 演變程式碼案例

Implementor:實現類介面。定義實現化角色的介面,包含角色必須的行為和屬性,並供擴充套件抽象化角色呼叫。更多內容,這個定義支付方式介面!

/**
 * 支付模式介面
 */
public interface IPayMode {

    //安全校驗功能: 對各種支付模式進行風控校驗
    boolean security(String uId);
}

ConcreteImplementor:具體實現類。給出實現化角色介面的具體實現。這裡有密碼支付,刷臉支付,指紋支付等。

/**
 * 密碼支付及風控校驗
 * 具體實現化(Concrete Implementor)角色
 */
public class PayCypher implements IPayMode {
    @Override
    public boolean security(String uId) {
        return false;
    }
}


/**
 * 刷臉支付及風控校驗
 * 具體實現化(Concrete Implementor)角色
 */
public class PayFaceMode implements IPayMode {
    @Override
    public boolean security(String uId) {
        return true;
    }
}

/**
 * 指紋支付及風控校驗
 * 具體實現化(Concrete Implementor)角色
 */
public class PayFingerprintMode implements IPayMode {

    @Override
    public boolean security(String uId) {
        return false;
    }
}

Abstraction:抽象類。主要負責定義出該角色的行為,幷包含一個對實現化物件的引用。這裡針對渠道抽象出角色。更多內容

/**
 * 支付抽象化類
 * 抽象化(Abstraction)角色
 */
public abstract class Pay {

    protected IPayMode payMode;

    public Pay(IPayMode payMode){
        this.payMode = payMode;
    }

    //劃賬功能
    public abstract String transfer(String uId, String tradeId, BigDecimal amount);

}

RefinedAbstraction:擴充抽象類。是抽象化角色的子類,實現父類中的業務方法,並透過組合關係呼叫實現化角色中的業務方法。

/**
 * 支付渠道-微信
 * 擴充套件抽象化(RefinedAbstraction)角色
 */
public class WxPay extends Pay{

    public WxPay(IPayMode payMode) {
        super(payMode);
    }

    @Override
    public String transfer(String uId, String tradeId, BigDecimal amount) {
        System.out.println("微信渠道支付劃賬開始......");

        //支付方式校驗
        boolean security = payMode.security(uId);
        System.out.println("微信渠道支付風險校驗: " + uId + " , " + tradeId +" , " + security);

        if(!security){
            System.out.println("微信渠道支付劃賬失敗!");
            return "500";
        }

        System.out.println("微信渠道劃賬成功! 金額: "+ amount);
        return "200";
    }
}


/**
 * 支付渠道--支付寶
 * 擴充套件抽象化(RefinedAbstraction)角色
 */
public class ZfbPay extends Pay{

    public ZfbPay(IPayMode payMode) {
        super(payMode);
    }

    @Override
    public String transfer(String uId, String tradeId, BigDecimal amount) {
        System.out.println("支付寶渠道支付劃賬開始......");

        //支付方式校驗
        boolean security = payMode.security(uId);
        System.out.println("支付寶渠道支付風險校驗: " + uId + " , " + tradeId +" , " + security);

        if(!security){
            System.out.println("支付寶渠道支付劃賬失敗!");
            return "500";
        }

        System.out.println("支付寶渠道劃賬成功! 金額: "+ amount);
        return "200";
    }
}

最後測試一下橋接模式,如下

private void test1() {
    System.out.println("測試場景1: 微信支付、人臉方式.");
    Pay wxpay = new WxPay(new PayFaceMode());
    wxpay.transfer("weixin","10001900",new BigDecimal(100));

    System.out.println();

    System.out.println("測試場景2: 支付寶支付、指紋方式");
    Pay zfbPay = new ZfbPay(new PayFingerprintMode());
    zfbPay.transfer("zhifubao","567689999999",new BigDecimal(200));
}

04.橋接實現方式

4.1 繼承和組合

使用繼承和組合的方式實現橋接模式。更多內容

這種方式需要建立兩個類,一個作為抽象類,另一個作為具體類。抽象類中定義了對抽象部分和實現部分的引用,具體類中實現了抽象部分的具體邏輯。

// 抽象部分
abstract class Abstraction {
    protected Implementation implementation;

    public void setImplementation(Implementation implementation) {
        this.implementation = implementation;
    }

    public abstract void operation();
}

// 具體部分
class ConcreteAbstraction extends Abstraction {
    @Override
    public void operation() {
        System.out.println("具體操作");
    }
}

// 實現部分
interface Implementation {
    void operationImpl();
}

class ConcreteImplementationA implements Implementation {
    @Override
    public void operationImpl() {
        System.out.println("實現A的操作");
    }
}

class ConcreteImplementationB implements Implementation {
    @Override
    public void operationImpl() {
        System.out.println("實現B的操作");
    }
}

// 客戶端程式碼
private void test() {
    Abstraction abstraction = new ConcreteAbstraction();
    Implementation implementationA = new ConcreteImplementationA();
    Implementation implementationB = new ConcreteImplementationB();

    abstraction.setImplementation(implementationA);
    abstraction.operation(); // 輸出:具體操作

    abstraction.setImplementation(implementationB);
    abstraction.operation(); // 輸出:具體操作
}

4.2 介面和內部類

使用介面和內部類的方式實現橋接模式

這種方式需要建立一個介面,一個抽象類和一個內部類。抽象類中定義了對介面的引用,內部類中實現了抽象類的具體邏輯。

// 介面
interface Shape {
    void draw();
}

// 抽象部分
abstract class AbstractShape {
    protected Shape shape;

    public void setShape(Shape shape) {
        this.shape = shape;
    }

    public abstract void draw();
}

// 具體部分
class Rectangle extends AbstractShape {
    @Override
    public void draw() {
        shape.draw();
    }
}

class Circle extends AbstractShape {
    @Override
    public void draw() {
        shape.draw();
    }
}

// 內部類實現介面
class ShapeImpl implements Shape {
    @Override
    public void draw() {
        System.out.println("繪製形狀");
    }
}

// 客戶端程式碼
private void test() {
    AbstractShape abstractShape = new Rectangle();
    Shape shapeA = new ShapeImpl();
    Shape shapeB = new ShapeImpl();

    abstractShape.setShape(shapeA);
    abstractShape.draw(); // 輸出:繪製形狀

    abstractShape.setShape(shapeB);
    abstractShape.draw(); // 輸出:繪製形狀
}

05.橋接模式分析

5.1 橋接模式優點

橋接模式的優點:

  1. 分離抽象介面及其實現部分。
  2. 橋接模式有時類似於多繼承方案,但是多繼承方案違背了類的單一職責原則(即一個類只有一個變化的原因),複用性比較差,而且多繼承結構中類的個數非常龐大,橋接模式是比多繼承方案更好的解決方法。
  3. 橋接模式提高了系統的可擴充性,在兩個變化維度中任意擴充套件一個維度,都不需要修改原有系統。
  4. 實現細節對客戶透明,可以對使用者隱藏實現細節。

5.2 橋接模式缺點

橋接模式的缺點:

  1. 橋接模式的引入會增加系統的理解與設計難度,由於聚合關聯關係建立在抽象層,要求開發者針對抽象進。更多內容

5.3 適用環境

在以下情況下可以使用橋接模式:

  1. 如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承聯絡,透過橋接模式可以使它們在抽象層建立一個關聯關係。
  2. 抽象化角色和實現化角色可以以繼承的方式獨立擴充套件而互不影響,在程式執行時可以動態將一個抽象化子類的物件和一個實現化子類的物件進行組合,即系統需要對抽象化角色和實現化角色進行動態耦合。
  3. 一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴充套件。
  4. 雖然在系統中使用繼承是沒有問題的,但是由於抽象化角色和具體化角色需要獨立變化,設計要求需要獨立管理這兩者。
  5. 對於那些不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用。

5.4 模式擴充

介面卡模式與橋接模式的聯用:更多內容

橋接模式和介面卡模式用於設計的不同階段,橋接模式用於系統的初步設計,對於存在兩個獨立變化維度的類可以將其分為抽象化和實現化兩個角色,使它們可以分別進行變化;而在初步設計完成之後,當發現系統與已有類無法協同工作時,可以採用介面卡模式。但有時候在設計初期也需要考慮介面卡模式,特別是那些涉及到大量第三方應用介面的情況。

06.外觀代理總結

6.1 總結一下學習

01.橋接模式基礎

橋接模式的由來是為了解決軟體系統中的複雜性和耦合性問題。在大型軟體系統中,各個子系統之間可能存在複雜的依賴關係和互動邏輯,這導致了系統的可維護性和可擴充套件性變得困難。為了簡化客戶端與子系統之間的互動,橋接模式被引入。

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

主要解決的問題:橋接模式用一種巧妙的方式處理多層繼承存在的問題,用抽象關聯來取代傳統的多層繼承,將類之間的靜態繼承關係轉變為動態的組合關係,使得系統更加靈活,並易於擴充套件。

02.橋接模式實現

假設有一個幾何形狀Shape類,從它能擴充套件出兩個子類:圓形Circle和方形Square。對這樣的類層次結構進行擴充套件以使其包含顏色,所以你打算建立名為紅色Red和藍色Blue的形狀子類。

橋接模式實現如下所示:

  1. 建立一個抽象類 Shape,它有一個抽象方法 draw()。
  2. 抽象化角色的子類,建立兩個實現了 Shape 介面的具體類:Circle 和 Square。
  3. 建立一個橋接類 Color,它也實現了 Shape 介面,並持有一個 Shape 型別的引用。
  4. 在主函式中測試這個橋接模式。

03.橋接例項演示

如微信和支付寶都可以完成支付操作,而支付操作又可以有掃碼支付、密碼支付、人臉支付等,那麼關於支付操作其實就有兩個維度,包括:支付渠道和支付方式。

不使用設計模式來模擬實現不同模式的支付場景。不使用設計模式缺點:維護和擴充套件都會變得非常複雜,需要修改原來程式碼,風險較大。

以後若增加支付渠道或修改支付方式,比如增加了京東支付,抖音支付,或者增加一個微信刷掌支付,則成本比較高,不利於後邊的擴充套件和維護。

橋接模式原理的核心是: 首先有要識別出一個類所具有的的兩個獨立變化維度,將它們設計為兩個獨立的繼承等級結構,為兩個維度都提供抽象層,並建立抽象耦合。

6.2 更多內容推薦

模組描述備註
GitHub多個YC系列開源專案,包含Android元件庫,以及多個案例GitHub
部落格彙總匯聚Java,Android,C/C++,網路協議,演算法,程式設計總結等YCBlogs
設計模式六大設計原則,23種設計模式,設計模式案例,物件導向思想設計模式
Java進階資料設計和原理,物件導向核心思想,IO,異常,執行緒和併發,JVMJava高階
網路協議網路實際案例,網路原理和分層,Https,網路請求,故障排查網路協議
計算機原理計算機組成結構,框架,儲存器,CPU設計,記憶體設計,指令程式設計原理,異常處理機制,IO操作和原理計算機基礎
學習C程式設計C語言入門級別系統全面的學習教程,學習三到四個綜合案例C程式設計
C++程式設計C++語言入門級別系統全面的教學教程,併發程式設計,核心原理C++程式設計
演算法實踐專欄,陣列,連結串列,棧,佇列,樹,雜湊,遞迴,查詢,排序等Leetcode
Android基礎入門,開源庫解讀,效能最佳化,Framework,方案設計Android

23種設計模式

23種設計模式 & 描述 & 核心作用包括
建立型模式
提供建立物件用例。能夠將軟體模組中物件的建立和物件的使用分離
工廠模式(Factory Pattern)
抽象工廠模式(Abstract Factory Pattern)
單例模式(Singleton Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern)
結構型模式
關注類和物件的組合。描述如何將類或者物件結合在一起形成更大的結構
介面卡模式(Adapter Pattern)
橋接模式(Bridge Pattern)
過濾器模式(Filter、Criteria Pattern)
組合模式(Composite Pattern)
裝飾器模式(Decorator Pattern)
外觀模式(Facade Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)
行為型模式
特別關注物件之間的通訊。主要解決的就是“類或物件之間的互動”問題
責任鏈模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
直譯器模式(Interpreter Pattern)
迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)
備忘錄模式(Memento Pattern)
觀察者模式(Observer Pattern)
狀態模式(State Pattern)
空物件模式(Null Object Pattern)
策略模式(Strategy Pattern)
模板模式(Template Pattern)
訪問者模式(Visitor Pattern)

6.3 更多內容

  • GitHub:https://github.com/yangchong211
  • 我的程式設計網站:https://yccoding.com
  • 部落格彙總:https://github.com/yangchong211/YCBlogs
  • 設計模式專欄:https://github.com/yangchong211/YCDesignBlog
  • Java高階進階專欄:https://github.com/yangchong211/YCJavaBlog
  • 網路協議專欄:https://github.com/yangchong211/YCNetwork
  • 計算機基礎原理專欄:https://github.com/yangchong211/YCComputerBlog

相關文章