設計模式 | 橋接模式(bridge)

定義:

將抽象部分與它的實現部分分離,使它們都可以獨立地變化

結構:(書中圖,侵刪)

 

一個抽象類,用於聚合實現
若干個實現抽象類的類
一個實現的父類
若干個實現了父類的具體實現類

實例:

我想到了一個工作中的例子,通常我們需要去對接一些第三方平台。
假如最開始你需要對接淘寶平台,去創建銷售單。然後過段時間又需要去對接京東平台,也是要創建銷售單。
你的類結構大概是這樣的:

 

出於面向對象考慮,你可能會給平台加個父類,變成這樣:

 

這時候又告訴你,你需要給兩個平台都添加獲取訂單的功能,然後變成這樣:

 

假設接下來又要給每個平台添加創建退貨單,抓取退貨單,然後又新加了兩個平台,amazon,ebay什麼的。
那簡直就是災難。代碼沒法復用,複製粘貼可不算復用,雖然每個平台都會有些不一樣,但是功能本身是大同小異的。
於是呢,就引出了我們的橋接模式,按照我的理解來說,就是將系統按照各個維度拆分出來,每個維度都是獨立的部分,互不影響,同時通過一座橋(這裡指聚合,後面會細講)把他們連接起來。
修改之後的類結構是這樣:

 

操作父類:

package designpattern.bridge;

public abstract class Operation {
    public abstract void operate();
}

創建訂單類:

package designpattern.bridge;

public class CreateOrder extends Operation {
    @Override
    public void operate() {
        System.out.println("創建訂單");
    }
}

獲取訂單類:

package designpattern.bridge;

public class GetOrders extends Operation {
    @Override
    public void operate() {
        System.out.println("獲取訂單");
    }
}

平台父類(聚合類):

package designpattern.bridge;

public abstract class Platform {
    protected Operation operation;

    public void setOperation(Operation operation) {
        this.operation = operation;
    }

    public abstract void operate();
}

淘寶類:

package designpattern.bridge;

public class Taobao extends Platform {
    @Override
    public void operate() {
        System.out.print("淘寶->");
        operation.operate();
    }
}

京東類:

package designpattern.bridge;

public class JingDong extends Platform {
    @Override
    public void operate() {
        System.out.print("京東->");
        operation.operate();
    }
}

客戶端:

package designpattern.bridge;

public class Client {
    public static void main(String[] args) {
        Operation createOrder=new CreateOrder();
        Operation getOrder=new GetOrders();
        
        Platform tb=new Taobao();
        tb.setOperation(createOrder);
        tb.operate();
        tb.setOperation(getOrder);
        tb.operate();
        
        System.out.println("==============================");
        
        Platform jd=new JingDong();
        jd.setOperation(createOrder);
        jd.operate();
        jd.setOperation(getOrder);
        jd.operate();
    }
}

結果輸出:

淘寶->創建訂單
淘寶->獲取訂單
==============================
京東->創建訂單
京東->獲取訂單

 

 番外:

書中還提到一個原則:

 

然後在《effective java》這本書也看到了類似的內容,角度又有些不同:

 

大概說的是,子類必須依附於父類,如果父類修改了,子類必須得跟着一起改。甚至父類新增了一個和子類簽名相同,但是返回類型不同的方法,會導致你的編譯不通過。
解決方法,就是複合,也是上面說的合成-》新類不繼承父類,而是包含一個擁有父類(雖然沒有繼承,為了前後統一,還是稱之為父類)實例的域,通過這個實例去訪問父類的方法。

上文提到的包裝類是指的裝飾模式(decorator),這裡就不展開了,感興趣的可以看看之前寫的文章:設計模式 | 裝飾模式(decorator)

總結:

核心還是解耦,以及開放-封閉原則。要想方設法的讓新增功能的時候不影響之前的代碼。設計模式只是表現形式不一樣,內核都差不多。
橋接模式在系統出現多維度的時候就可以考慮使用,其實這種是在最開始就能預見到的,比如文中舉的例子,通常都能想到未來可能增加功能和平台,所以在寫第一個平台的第一個功能的時候就應該有意識的去設計。
最後就是在使用繼承前要再三的確認是不是「is a」關係,即便是也不要輕易的使用繼承,複合往往是更優先的選擇。