天天看點

設計模式-橋接模式前言橋接模式

前言

結構型設計模式

結構型模式主要總結了一些類和對象組合在一起的經典結構,這些經典結構可以解決對應特定場景的問題.

一共包括七種:代理模式、橋接模式、裝飾者模式、擴充卡模式、門面(外觀)模式、組合模式、和享元模式。

橋接模式

概念

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

橋接模式用一種巧妙的方式處理多層繼承存在的問題,用抽象關聯來取代傳統的多層繼承,将類之間的靜态繼承關系轉變為動态的組合關系,使得系統更加靈活,并易于擴充,有效的控制了系統中類的個數 (避免了繼承層次的指數級爆炸).
設計模式-橋接模式前言橋接模式

橋接(Bridge)模式包含以下主要角色:

  • 抽象化(Abstraction)角色 :主要負責定義出該角色的行為 ,并包含一個對實作化對象的引用。
  • 擴充抽象化(RefinedAbstraction)角色 :是抽象化角色的子類,實作父類中的業務方法,并通過組合關系調用實作化角色中的業務方法。
  • 實作化(Implementor)角色 :定義實作化角色的接口,包含角色必須的行為和屬性,并供擴充抽象化角色調用。
  • 具體實作化(Concrete Implementor)角色 :給出實作化角色接口的具體實作。

橋接模式原理的核心是: 首先有要識别出一個類所具有的的兩個獨立變化次元,将它們設計為兩個獨立的繼承等級結構,為兩個次元都提供抽象層,并建立抽象耦合.總結一句話就是: 抽象角色引用實作角色

示例

支付操作

設計模式-橋接模式前言橋接模式

不使用橋接模式時

public class PayController {

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

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

如果後期需要添加或删除支付方式、支付管道,則需要改動代碼,後期維護和擴充将會變得非常複雜

通過橋接模式

重構

提取兩個獨立次元:支付方式和支付管道

  • Pay抽象類
  • 支付管道子類: 微信支付
  • 支付管道子類: 支付寶支付
  • IPayMode接口
  • 支付模式實作: 刷臉支付
  • 支付模式實作: 指紋支付
  • 支付管道*支付模式 = 相對應的組合.
設計模式-橋接模式前言橋接模式

代碼

支付模式接口

/**
 * 支付模式接口
 **/
public interface IPayMode {

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

支付模式實作

實作支付模式接口,實作具體支付模式

//指紋支付及風控校驗
public class PayFingerprintMode implements IPayMode {

    @Override
    public boolean security(String uId) {
        System.out.println("指紋支付,風控校驗-指紋資訊");
        return true;
    }
}

//刷臉支付及風控校驗
public class PayFaceMode implements IPayMode {

    @Override
    public boolean security(String uId) {
        System.out.println("人臉支付,風控校驗-臉部識别");
        return true;
    }
}
//密碼支付及風控校驗
public class PayCypher implements IPayMode {

    @Override
    public boolean security(String uId) {
        System.out.println("密碼支付,風控校驗-環境安全");
        return true;
    }
}
           

支付抽象類

橋接支付模式,定義抽象角色功能

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

    //橋接對象
    protected IPayMode payMode;

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

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

}
           

支付管道

實作具體支付管道,調用支付模式驗證

/**
 * 支付管道-微信劃賬
 **/
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";
    }
}

/**
 * 支付管道-支付寶劃賬
 **/
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";
    }
}
           

測試

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

    System.out.println();

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

擴充

使用橋接後,如果後續需要進行擴充,比如新增一個支付管道為積分支付

擴充步驟如下

/**
 * 支付管道-積分劃賬
 **/
public class CodePay extends Pay {


    public CodePay(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";
    }
}
           

橋接模式總結

橋接模式的優點:

  1. 分離抽象接口及其實作部分.橋接模式使用"對象間的關聯關系"解耦了抽象和實作之間固有的綁定關系,使得抽象和實作可以沿着各自的次元來變化.
  1. 在很多情況下,橋接模式可以取代多層繼承方案.多層繼承方案違背了單一職責原則,複用性差,類的個數多.橋接模式很好的解決了這些問題.
  1. 橋接模式提高了系統的擴充性,在兩個變化次元中任意擴充一個次元都不需要修改原有系統,符合開閉原則.

橋接模式的缺點:

  1. 橋接模式的使用會增加系統的了解和設計難度,由于關聯關系建立在抽象層,要求開發者一開始就要對抽象層進行設計和程式設計
  1. 橋接模式要求正确識别出系統中的兩個獨立變化的次元,是以具有一定的局限性,并且如果正确的進行次元的劃分,也需要相當豐富的經驗.

橋接模式使用場景

  1. 需要提供平台獨立性的應用程式時。 比如,不同資料庫的 JDBC 驅動程式、硬碟驅動程式等。
  1. 需要在某種統一協定下增加更多元件時。 比如,在支付場景中,我們期望支援微信、支付寶、各大銀行的支付元件等。這裡的統一協定是收款、支付、扣款,而元件就是微信、支付寶等。
  1. 基于消息驅動的場景。 雖然消息的行為比較統一,主要包括發送、接收、處理和回執,但其實具體用戶端的實作通常卻各不相同,比如,手機短信、郵件消息、QQ 消息、微信消息等。
  1. 拆分複雜的類對象時。 當一個類中包含大量對象和方法時,既不友善閱讀,也不友善修改。
  1. 希望從多個獨立次元上擴充時。 比如,系統功能性和非功能性角度,業務或技術角度等。