簡說設計模式——橋接模式


一、什么是橋接模式

  橋接模式,又叫橋梁模式,顧名思義,就是有座“橋”,那這座橋是什么呢?就是一條聚合線(下方UML圖),比如我們下面會舉的例子,手機有手機品牌和手機游戲等等,每個手機品牌都有多款游戲,那是不是二者之間就是聚合關系了,這是合成/聚合復用原則的體現,當我們發現類有多層繼承時就可以考慮使用橋接模式,用聚合代替繼承。

       橋接模式(Bridge),將抽象部分與它的實現部分分離,使它們都可以獨立地變化。UML結構圖如下:

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

       是不是感覺上面這段話很難懂,其實說簡單點就是在Abstraction和Implementor之間架了一座橋(聚合線),這里體現了一個原則就是合成/聚合復用原則,具體看目錄篇對基本原則的講解及舉例。下面放上模板代碼。

    1. Abstraction抽象類

 1 public abstract class Abstraction {
 2 
 3     private Implementor imp;
 4 
 5     //約束子類必須實現該構造函數
 6     public Abstraction(Implementor imp) {
 7         this.imp = imp;
 8     }
 9     
10     public Implementor getImp() {
11         return imp;
12     }
13 
14     //自身的行為和屬性
15     public void request() {
16         this.imp.doSomething();
17     }
18     
19 }

    2. Implementor抽象類

1 public abstract class Implementor {
2 
3     public abstract void doSomething();
4     public abstract void doAnything();
5     
6 }

    3. ConcreteImplementor

       這里可以編寫多個具體實現類。

 1 public class ConcreteImplementorA extends Implementor {
 2 
 3     @Override
 4     public void doSomething() {
 5         System.out.println("具體實現A的doSomething執行");
 6     }
 7     
 8     @Override
 9     public void doAnything() {
10         System.out.println("具體實現A的doAnything執行");
11     }
12 }

    4. RefinedAbstraction

 1 public class RefinedAbstraction extends Abstraction {
 2 
 3     //覆寫構造函數
 4     public RefinedAbstraction(Implementor imp) {
 5         super(imp);
 6     }
 7     
 8     //修正父類行為
 9     @Override
10     public void request() {
11         super.request();
12         super.getImp().doAnything();
13     }
14     
15 }

    5. Client客戶端

1 public class Client {
2 
3     public static void main(String[] args) {
4         Implementor imp = new ConcreteImplementorA();
5         Abstraction abs = new RefinedAbstraction(imp);
6         abs.request();
7     }
8     
9 }

       運行結果如下:

二、橋接模式的應用

    1. 何時使用

  • 系統可能有多個角度分類,每一種角度都可能變化時

    2. 方法

  • 把這種角度分類分離出來,讓它們單獨變化,減少它們之間的耦合(合成/聚合復用原則)

    3. 優點

  • 抽象和實現分離。橋梁模式完全是為了解決繼承的缺點而提出的設計模式
  • 優秀的擴展能力
  • 實現細節對客戶透明。客戶不用關心細節的實現,它已經由抽象層通過聚合關系完成了封裝

    4. 缺點

  • 會增加系統的理解與設計難度。由於聚合關聯關系建立在抽象層,要求開發者針對抽象進行設計與編程

    5. 使用場景

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

    6. 應用實例

  • 開關。我們可以看到的開關是抽象的,不用管里面具體怎么實現
  • 手機品牌與手機軟件。兩者間有一條聚合線,一個手機品牌可以有多個手機軟件

    7. 注意事項

  • 不要一涉及繼承就考慮該模式,盡可能把變化的因素封裝到最細、最小的邏輯單元中,避免風險擴散
  • 當發現類的繼承有n層時,可以考慮使用該模式

三、橋接模式的實現

       下面我們舉一個例子,就拿上面說的手機品牌與手機軟件為例,我們可以讓手機既可以按照手機品牌來分類,也可以按手機軟件來分類。由於實現的方式有多種,橋接模式的核心意圖就是把這些實現獨立出來,讓它們各自地變化,這就使得沒中實現的變化不會影響其他實現,從而達到應對變化的目的。

       UML圖如下:

    1. 手機品牌抽象類

       橋梁的一頭。

 1 public abstract class HandsetBrand {
 2 
 3     protected HandsetSoft soft;
 4     
 5     //設置手機軟件
 6     public void setHandsetSoft(HandsetSoft soft) {
 7         this.soft = soft;
 8     }
 9     
10     //運行
11     public abstract void run();
12     
13 }

    2. 手機軟件抽象類

       橋梁的另一頭。兩者通過一條聚合線連接,表示一個手機品牌可以有多個軟件。

1 public abstract class HandsetSoft {
2 
3     public abstract void run();
4     
5 }

    3. 各類手機品牌

       這里寫一個,多余的不再贅述。

1 public class HandsetBrandA extends HandsetBrand {
2 
3     @Override
4     public void run() {
5         soft.run();
6     }
7     
8 }

    4. 各類手機軟件

       有游戲、通訊錄等等,這里寫一個,多余不再贅述。

1 public class HandsetGame extends HandsetSoft {
2 
3     @Override
4     public void run() {
5         System.out.println("運行手機游戲");
6     }
7 
8 }

    5. Client客戶端

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         HandsetBrand ab;
 5         
 6         //使用A品牌手機
 7         ab = new HandsetBrandA();
 8         System.out.println("A品牌手機:");
 9         
10         ab.setHandsetSoft(new HandsetGame());
11         ab.run();
12         
13         ab.setHandsetSoft(new HandsetAddressList());
14         ab.run();
15         
16         //分隔符
17         System.out.println("---------------");
18         
19         //使用B品牌手機
20         ab = new HandsetBrandB();
21         System.out.println("B品牌手機:");
22         
23         ab.setHandsetSoft(new HandsetGame());
24         ab.run();
25         
26         ab.setHandsetSoft(new HandsetAddressList());
27         ab.run();
28     }
29     
30 }

       運行結果如下:

       這樣我現在如果想要增加一個功能,比如音樂播放器,那么只有增加這個類就可以了,不會影響到其他任何類,類的個數增加也只是一個;如果是要增加S品牌,只需要增加一個品牌的子類就可以了,個數也是一個,不會影響到其他類。這顯然符合開放-封閉原則。

       而這里用到的合成/聚合復用原則是一個很有用處的原則,即優先使用對象的合成或聚合,而不是繼承。究其原因是因為繼承是一種強耦合的結構,父類變,子類就必須變。

 

       源碼地址:https://gitee.com/adamjiangwh/GoF


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM