設計模式:橋接模式及程式碼示例、橋接模式在jdbc中的體現、注意事項

Life_Goes_On發表於2020-08-15

0、背景

加入一個手機分為多種款式,不同款式分為不同品牌。這些詳細分類下分別進行操作。

如果傳統做法,需要將手機,分為不同的子類,再繼續分,基本屬於一個龐大的多叉樹,然後每個葉子節點進行相同名稱、但是細節不同的功能實現。

設計模式:橋接模式及程式碼示例、橋接模式在jdbc中的體現、注意事項

問題

  1. 類爆炸:類的增加基本沒有任何優化,多一個就要妥妥的增加類;
  2. 違反單一原則:增加一個品牌,影響每種型別下的這個品牌,增加一個型別,影響每個品牌的這個型別。

一、橋接模式

解決上面說的問題的方式就是使用橋接模式。

橋接(Bridge)模式是指,將實現和抽象放在兩個不同的類層次中,使得兩個層次可以獨立改變。

橋接模式是一種結構型設計模式,基於類的最小設計原則,讓不同的類承擔不同的職責。(單一原則)

橋接模式的主要特點是,把抽象 Abstraction 和行為實現 Implementation 分離開,從而保持各部分的獨立和應對他們的功能擴充套件。

類圖如下所示:

設計模式:橋接模式及程式碼示例、橋接模式在jdbc中的體現、注意事項

其中,承擔 橋接功能 的抽象類就是 Abstraction。

看起來還是很抽象,結合上面的手機問題,畫出對應的類圖,再結合來看:

設計模式:橋接模式及程式碼示例、橋接模式在jdbc中的體現、注意事項

也就是說,比起原來的樹狀結構,用橋接模式,將行為實現放在一邊,有自己的介面;將抽象出的另一個維度放在一邊,有自己的抽象父類和子類,然後將上層的抽象類和實現介面相關聯。

這樣的話,前面遇到的問題:

  • 新增手機樣式,就在左邊新增抽象類定義就可以;
  • 新增品牌,在右邊新增實現類就可以。
  • 其他部分都不會被影響。

觀察上面的圖,橋接的意味也很明顯,當 QuanPingPhone 使用一個 call 方法的時候,繼承的其實是父類的方法,而這裡面組合了另一個性質,比如是 Oppo 手機的 call 方法,那麼就是通過橋接一路引過去的。 (忽略我的英語)

按照這個思路我們來寫一個實現:

/*
    品牌,介面
*/
public interface Brand {
    void open();
    void close();
    void call();
}
public class Xiaomi implements Brand{
    @Override
    public void open() {
        System.out.println("小米手機開機");
    }
    @Override
    public void close() {
        System.out.println("小米手機關機");
    }
    @Override
    public void call() {
        System.out.println("小米手機打電話");
    }
}
public class Vivo implements Brand{
    @Override
    public void open() {
        System.out.println("vivo手機開機");
    }
    @Override
    public void close() {
        System.out.println("vivo手機關機");
    }
    @Override
    public void call() {
        System.out.println("vivo手機打電話");
    }
}
/*
    抽象層:手機,通過品牌介面聚合不同品牌
*/
public abstract class Phone {
    //品牌
    private Brand brand;

    public Phone(Brand brand) {
        this.brand = brand;
    }
    protected void open(){
        this.brand.open();
    }
    protected void close(){
        this.brand.close();
    }
    protected void call(){
        this.brand.call();
    }
}
/*
    摺疊手機,繼承抽象類手機
*/
public class BoldPhone extends Phone{
    //構造器
    public BoldPhone(Brand brand) {
        super(brand);
    }
    public void open(){
        super.open();//實際上通過父類的open,橋接到了brand下面的某個子類的open
        System.out.println("開啟的是摺疊手機");
    }
    public void close(){
        super.close();
        System.out.println("關閉的是摺疊手機");
    }
    public void call(){
        super.call();
        System.out.println("打電話的的是摺疊手機");


    }
}
/*
    全面屏手機,繼承抽象類手機
*/
public class ScreenPhone extends Phone{
    public ScreenPhone(Brand brand) {
        super(brand);
    }
    public void open(){
        super.open();//實際上通過父類的open,橋接到了brand下面的某個子類的open
        System.out.println("開啟的是全面屏手機");
    }
    public void close(){
        super.close();
        System.out.println("關閉的是全面屏手機");
    }
    public void call(){
        super.call();
        System.out.println("打電話的的是全面屏手機");
    }
}

呼叫試試:

/*
    客戶端
*/
public class Client {
    public static void main(String[] args) {
        //獲取一個手機,需要的是樣式+手機兩個實現類
        //通過Phone這個抽象類,增加一個樣式為BoldPhone的手機,橋接到品牌為Xiaomi
        Phone phone = new BoldPhone(new Xiaomi());
        phone.open();
        phone.call();
        phone.close();
    }
}

這樣的話,如果擴充套件起來,比如新增加一種樣式的手機,我們只需要繼承Phone再寫一個子類,其餘都不需要更改,如果新增加一個品牌的手機,只需要實現Band介面再寫一個實現類,其餘都不需要更改。

二、使用橋接模式的原始碼:JDBC

JDBC 原始碼裡:

MySQL 的 Connection 介面實現的是 java.sql.Connection 介面,同時 Oracle 資料庫也一樣可以實現 java.sql.Connection 介面,他們向下都可以有更多的實現子類。

然後 DriverManager 相當於橋接模組,依賴和聚合 java.sql.Connection 介面,供客戶端呼叫。

和我們上面所說的略微不同,就是 DriverManager 不是抽象類,而是直接的具體實現。

三、橋接模式的注意事項

  1. 橋接模式實現了抽象和實現部分的分離,提高了系統的靈活性,讓抽象部分和實現部分獨立開來,有助於系統的分層設計。
  2. 高層只需要知道抽象部分和實現部分的介面,而不需要知道其他具體實現;
  3. 替代了多層繼承,大大減少了子類的數量;
  4. 橋接模式的引入,增加了系統的設計和理解難度,由於聚合關聯關係建立在抽象層,要求開發者針能對抽象層進行設計和程式設計;
  5. 適用範圍有侷限性

應用場景:

  1. jdbc:多種驅動,多種資料庫;
  2. 銀行轉賬:因為轉賬動作要分很多類,使用者也分很多類,所以他們之間不適合多層繼承;
  3. 訊息管理:訊息型別不同,訊息傳送方式不同。

相關文章