「補課」進行時:設計模式(22)——橋樑模式

1. 前文彙總

「補課」進行時:設計模式系列java

2. 橋樑模式

設計模式的最後一篇文章,仍是讓我這個拖延症晚期的患者把時間拖到了 2020 年的最後一天。編程

雖然是最後一篇,但並非最難的一個模式,比較複雜的兩個模式已經在前面介紹過了,一個是訪問者模式,另外一個是解釋器模式。設計模式

那麼什麼是橋樑模式?ide

2.1 定義

橋樑模式(Bridge Pattern) 也叫作橋接模式, 是一個比較簡單的模式, 其定義以下:Decouple an abstraction from its implementation so that the two can vary independently.(將抽象和實現解耦, 使得二者能夠獨立地變化。 )函數

橋樑模式的重點是在「解耦」上, 如何讓它們二者解耦是橋樑模式的重點,this

2.2 通用類圖

  • Abstraction 抽象化角色:定義出該角色的行爲, 同時保存一個對實現化角色的引用, 該角色通常是抽象類
  • Implementor 實現化角色:它是接口或者抽象類, 定義角色必需的行爲和屬性。
  • RefinedAbstraction 修正抽象化角色:實現化角色對抽象化角色進行修正。
  • ConcreteImplementor 具體實現化角色:實現接口或抽象類定義的方法和屬性。

橋樑模式中的幾個名詞比較拗口,核心就一句話:抽象角色引用實現角色,或者說抽象角色的部分實現是由實現角色完成的。設計

2.3 通用代碼

實現化角色:code

public interface Implementor {
    // 基本方法
    void doSomething();
    void doAnything();
}

具體實現化角色:對象

public class ConcreteImplementor1 implements Implementor {
    @Override
    public void doSomething() {

    }

    @Override
    public void doAnything() {

    }
}

public class ConcreteImplementor2 implements Implementor {
    @Override
    public void doSomething() {

    }

    @Override
    public void doAnything() {

    }
}

抽象化角色:blog

public abstract class Abstraction {
    // 定義對實現化角色的引用
    private Implementor impl;
    // 約束子類必須實現該構造函數
    public Abstraction(Implementor impl){
        this.impl = impl;
    }
    // 自身的行爲和屬性
    public void request(){
        this.impl.doSomething();
    }
    // 得到實現化角色
    public Implementor getImpl(){
        return impl;
    }
}

具體抽象化角色:

public class RefinedAbstraction extends Abstraction {
    // 覆寫構造函數
    public RefinedAbstraction(Implementor impl) {
        super(impl);
    }
    // 修正父類的行爲
    @Override
    public void request(){
        // 業務處理
        super.request();
        super.getImpl().doAnything();
    }
}

客戶端類:

public class Client {
    public static void main(String[] args) {
        // 定義一個實現化角色
        Implementor imp = new ConcreteImplementor1();
        // 定義一個抽象化角色
        Abstraction abs = new RefinedAbstraction(imp);
        // 執行行文
        abs.request();
    }
}

2.4 優勢

  1. 抽象和實現分離:橋樑模式徹底是爲了解決繼承的缺點而提出的設計模式。
  2. 優秀的擴展能力。
  3. 實現細節對客戶透明。客戶不用關心細節的實現,它已經由抽象層經過聚合關係完成了封裝。

2.5 缺點

會增長系統的理解與設計難度。因爲聚合關聯關係創建在抽象層,要求開發者針對抽象進行設計與編程。

2.6 使用場景

  • 不但願或不適用使用繼承的場景。
  • 接口或抽象類不穩定的場景。
  • 重用性要求較高的場景。

3. 一個簡單的案例

在橋接模式中要把握的很重要的一點就是:類的繼承關係和類的組合/聚合關係,什麼時候應該考慮使用何種關係。是否是在編程過程當中一味地使用類的繼承關係就表明這就是面向對象編程了?有時候並非這樣, Java 的類繼承設計成單繼承模式我想應該就是不想把類的繼承關係搞得過於複雜,實際上咱們應該優先使用對象組合/聚合,而不是類繼承。

類繼承沒必要多說,先來看看何爲組合/聚合關係。

聚合體現的是「弱」的擁有關係,好比雁羣能夠包含大雁,但雁羣不是大雁的一部分。組合體現的是「強」的擁有關係,或者體現的是部分與總體的關係,經過一對翅膀組合成大雁,翅膀是部分,大雁是總體。

咱們如今每天使用的一個電器:手機,手機有手機品牌和手機軟件等等,每一個手機品牌都有多款軟件。

這個案例若是使用繼承關係的話,無論是已手機品牌仍是手機軟件做爲父類,都會對後續的擴展形成很大的影響,若是咱們使用橋樑模式,使用對象的組合/聚合,類圖是下面這樣:

經過UML類結構圖咱們能夠看到手機品牌和手機軟件成功解耦,新增功能並不影響其手機品牌,新增手機品牌也不會影響到手機軟件,其中的奧祕就在於利用了聚合而不是繼承。

代碼以下:

手機品牌抽象類:

public abstract class HandsetBrand {
    protected HandsetSoft soft;
    // 設置手機軟件
    public void setHandsetSoft(HandsetSoft soft) {
        this.soft = soft;
    }
    // 運行
    abstract void run();
}

手機軟件抽象類:

public abstract class HandsetSoft {
    abstract void run();
}

各種手機品牌實現類:

public class HandsetBrandA extends HandsetBrand {
    @Override
    void run() {
        super.soft.run();
    }
}

public class HandsetBrandB extends HandsetBrand{
    @Override
    void run() {
        super.soft.run();
    }
}

手機軟件實現類:

public class HandsetGame extends HandsetSoft{
    @Override
    void run() {
        System.out.println("運行手機遊戲");
    }
}

public class HandsetAddressList extends HandsetSoft{
    @Override
    void run() {
        System.out.println("運行手機通信錄");
    }
}

客戶端類:

public class Client {
    public static void main(String[] args) {
        HandsetBrand ab;

        // 使用 A 品牌手機
        ab = new HandsetBrandA();
        System.out.println("A品牌手機:");

        ab.setHandsetSoft(new HandsetGame());
        ab.run();

        ab.setHandsetSoft(new HandsetAddressList());
        ab.run();

        // 使用 B 品牌手機
        ab = new HandsetBrandB();
        System.out.println("B品牌手機:");

        ab.setHandsetSoft(new HandsetGame());
        ab.run();

        ab.setHandsetSoft(new HandsetAddressList());
        ab.run();
    }
}

執行結果以下:

A品牌手機:
運行手機遊戲
運行手機通信錄
B品牌手機:
運行手機遊戲
運行手機通信錄
相關文章
相關標籤/搜索