設計模式之(10)——橋接模式

2022-09-09 15:00:36

     Hello,大家好,我是你們的新朋友小烤鴨,我們的設計模式系列中斷了幾天,今天我們繼續給它續上,那麼我們下面繼續來說一種結構型設計模式,那就是大名鼎鼎的「橋接模式」。

  定義:橋接模式的官方定義是將抽象部分與它的實現部分分離,使得他們都可以獨立變化,是一種結構型物件設計模式;上面這個定義比較拗口,且晦澀難懂,那麼我們舉個實際生活中的例子:橋使我們大家所熟知的,它的作用就是將兩岸聯絡起來,我們兩岸的老百姓可以通過這座橋自由流通,雲遊四方,橋接模式也差不多也就是這個意思;

  適用場景:「橋接模式主要用於在一個系統中需要在抽象化和具體化之間增加更多的靈活性,避免在兩個層次建立靜態的繼承關係,通過橋接模式可以使他們在抽象層建立一個關聯關係」,這句話並不好懂,需要有一定的程式設計工作的經驗積累才能慢慢理解,而其中所「抽象部分」和「實現部分」可以以繼承的方式獨立擴充套件而互不影響,在程式執行的時候可以動態將一個抽象化子類的物件和一個實現化子類的物件進行組合,即系統需要對抽象化角色和實現化角色進行動態耦合;「一個類存在兩個(或多個)獨立變化的維度,並且這些維度都需要獨立擴充套件的」,理解這句話橋接模式就不難搞懂了,對於那些不希望使用繼承或者因為多層繼承導致系統類的數量急劇增加造成「類爆炸」的情況(違反程式設計的「單一職責原則」),橋接模式更合適;

  優點:1、將抽象部分和實現部分分離,使他們可以獨立變化(符合程式設計的「開閉原則」和「單一職責」);2、增加了程式的擴充套件能力;

  缺點:會增加系統的理解和實際難度,由於聚合關聯關係建立在抽象層,需要開發者針對抽象進行設計;

  角色分析:

    1、抽象化角色(Abstraction):給出抽象化角色的定義,並儲存一個對實現化物件的參照;

    2、修正抽象化角色(RefinedAbstraction):擴充套件抽象化角色,改變和修正父類別抽象化角色的定義;

    3、實現化角色(Implementor):實現化角色的定義,不給出具體實現;

    4、具體實現化角色(ConcreteImplementor):給出實現化角色的具體實現;

  模式結構圖:

  

  重點:說了這麼多,簡單的講橋接就是將需要「M*N」個類解決的問題,變成「M+N"個類就能解決的問題;

  範例程式碼:

package cn.com.pep.model.bridge;
/**
 * 
 * @Title: Bridge  
 * @Description: 橋介面,實現化角色,給出介面定義,不提供具體實現
 * @author wwh 
 * @date 2022-9-9 10:30:15
 */
public interface Bridge {
    
    /**
     * @Title: target 
     * @Description:
     */
    public void target();
}
package cn.com.pep.model.bridge;
/**
 * 
 * @Title: BeijingTarget  
 * @Description:  具體實現化角色,給出實現化角色的具體定義,通過實現介面的方式與橋介面進行繫結
 * @author wwh 
 * @date 2022-9-9 10:58:38
 */
public class BeijingTarget implements Bridge{

    @Override
    public void target() {
        System.err.println("我要去北京......");
   }
}
package cn.com.pep.model.bridge;
/**
 * 
 * @Title: ShanghaiTarget  
 * @Description:  具體實現化角色,給出實現化角色的具體定義,通過實現介面的方式與橋介面進行繫結
 * @author wwh 
 * @date 2022-9-9 10:56:14
 */
public class ShanghaiTarget implements Bridge{

    @Override
    public void target() {
        System.err.println("我要去上海......");
    }
}
package cn.com.pep.model.bridge;
/**
 * 
 * @Title: AbstractSrc  
 * @Description:  抽象化角色(Abstraction):給出抽象化角色的定義,並儲存一個對實現化物件的參照;
 * @author wwh 
 * @date 2022-9-9 11:07:37
 */
public abstract class AbstractSrc {
    
    Bridge bridge;
    
    public AbstractSrc(Bridge bridge) {
        this.bridge = bridge;
    }
    
    /**
     * 
     * @Title: src 
     * @Description:
     */
    public abstract void src();
    
    /**
     * @Title: fromTo 
     * @Description:
     */
    public void to() {
        src();
        bridge.target();
    }
}
package cn.com.pep.model.bridge;
/**
 * @Title: NanjingSrc  
 * @Description:  修正抽象化角色(RefinedAbstraction):擴充套件抽象化角色,改變和修正父類別抽象化角色的定義;
 * @author wwh 
 * @date 2022-9-9 11:11:44
 */
public class NanjingSrc extends AbstractSrc{

    public NanjingSrc(Bridge bridge) {
        super(bridge);
    }

    @Override
    public void src() {
        System.err.println("我來自南京......");
    }
}
package cn.com.pep.model.bridge;
/**
 * 
 * @Title: WuhanSrc  
 * @Description:  修正抽象化角色(RefinedAbstraction):擴充套件抽象化角色,改變和修正父類別抽象化角色的定義;
 * @author wwh 
 * @date 2022-9-9 11:14:48
 */
public class WuhanSrc extends AbstractSrc{

    public WuhanSrc(Bridge bridge) {
        super(bridge);
    }

    @Override
    public void src() {
        System.err.println("我來自武漢......");
    }
}
package cn.com.pep.model.bridge;
/**
 * 
 * @Title: BridgeDemo  
 * @Description:  測試類
 * @author wwh 
 * @date 2022-9-9 11:21:47
 */
public class BridgeDemo {
    
    public static void main(String[] args) {
        Bridge b = new BeijingTarget();
        AbstractSrc as = new NanjingSrc(b);
        as.to();
        
        b = new ShanghaiTarget();
        as.bridge = b;
        as.to();
        
        as = new WuhanSrc(b);
        as.to();
        
        as.bridge = new BeijingTarget();
        as.to();  
    }
}

  此模式是將有關聯關係的兩個類,通過橋接介面,一方通過介面實現,另一方通過類聚合來實現這兩個類之間的解耦的,使得兩方可以獨立擴充套件,互不影響。

  橋接模式在JDK中的應用:

  java.util.logging是JDK自帶的紀錄檔包,可以將紀錄檔輸出到檔案、資料庫或者控制檯,作用與我們常用的log4j類似;這個包中的Handler類和Formatter類就利用了橋接模式,UML類圖如下:

  

  Handler和Formatter是兩個抽象類,兩者都有子類繼承,可以獨立變化,其中Handler包含了一個對Formatter類的參照,Handler類物件可以從一個logger中獲取資訊,並輸出到控制檯、檔案或者呼叫其他api傳送到網路中,並且還可以通過setLevel方法關閉或開啟,它通常是利用LogManager去設定自身的Filter、Formatter等屬性,Formatter是一個支援格式化的類,我們可以通過它定義輸出紀錄檔的格式為簡單格式,還是XML格式等等;

  橋接模式與裝飾模式、介面卡模式的比較:

    此三種模式都屬於結構型模式、都存在物件關聯聚合的情況,但是側重點各有不同。

   介面卡模式重點強調的是適配。實現的關鍵點是:目標類和介面卡類都實現了相同的介面A,目標類關聯了介面卡類,介面卡類聚合了抽象介面B ,被適配的類實現了抽象介面B,在具體使用的時候在介面卡類中通過構造方法注入被適配類的參照,最終的結果是,目標類可以使用之前不相關的被適配類中的某些功能,類似於「改變介面的功能」;

  橋接模式重點強調的是多維度的變化組合。實現的關鍵點是:主體類A聚合了抽象類B,主體類A有多個不同的子類實現,抽象類B有多個不同的子類實現,最終的效果使的主體類A的實現類和抽象類B的實現類可以分別在多個維度上進行變化組合,而不相互耦合, 方便擴充套件;

  裝飾設計模式強調的是類功能的增強。實現的關鍵點是:抽象類A具有多個具體的子類,裝飾器類B聚合了抽象類A,並且繼承了抽象類A具有和抽象類A一致的行為,但是裝飾器類B還有不同的子類實現,最終的效果就是裝飾器類B的子類實現,可以對抽象類A的子類進行某些方法的功能增強。

  介面卡模式和裝飾設計模式可以具體參見我之前的文章:

  設計模式之(9)—介面卡模式;

    設計模式之(7)—裝飾設計模式;

   好了,本期就說到這裡啦,歡迎各位大佬批評指正,我們下次再見!