天天看點

橋接模式

橋接模式要把握的很重要的一點就是:類的繼承關系和類的組合/聚合關系,何時應該考慮使用何種關系。是不是在程式設計過程中一味地使用類的繼承關系就代表這就是面向對象程式設計了?有時候并不是這樣,Java的類繼承設計成單繼承模式我想應該就是不想把類的繼承關系搞得過于複雜,實際上我們應該優先使用對象組合/聚合,而不是類繼承。類繼承不必我們多說,我們來看看何為組合/聚合關系。

橋接模式

當我們看到上圖過後應該能明白什麼是組合和聚合了。聚合展現的是“弱”的擁有關系,比如雁群可以包含大雁,但雁群不是大雁的一部分。組合展現的是“強”的擁有關系,或者展現的是部分與整體的關系,通過一對翅膀組合成大雁,翅膀是部分,大雁是整體。

在了解了什麼是組合/聚合過後,我們來看看什麼是橋接模式。同樣我們通過《大話設計模式》書中的例子來說明。

在N多年前手機還未像現在的手機市場一樣,由Android和iOS一統天下。N年前各個手機廠商的軟體幾乎是互不相容,更嚴重的可能是同一個手機廠商不同型号的手機也互不相容。如果我們考慮這種場景應該如何來設計我們的代碼呢?我們通過手機品牌來分類。

橋接模式
這就是我們通過類繼承造成的結果。如果現在需要新增一個手機品牌S的話,就得再增加手機品牌S的下屬功能子類,手機品牌千千萬萬那要增加的類就不計其數。我們再換一種通過軟體分類來呢?
橋接模式
其實這不也一樣嗎?其實質還是通過類的繼承來實作。新增一個功能過後同樣要新增N個手機品牌對應的軟體。從上面兩種設計中,其實我們可以發現可以将“手機”這個抽象的概念将其剝離為“手機軟體”和“手機品牌”這兩個實作,具體是怎麼做到的呢?我們來看橋接模式下的UML類結構圖。
橋接模式

通過UML類結構圖我們可以看到手機品牌和手機軟體成功解耦,新增功能并不影響其手機品牌,新增手機品牌也不會影響到手機軟體,其中的奧秘就在于利用了聚合而不是繼承。(但其實,我覺得在這個地方所舉的這個例子有失偏頗。從開始我們假定的場景是各個手機品牌互不相容各自的手機軟體,最開始兩種“壞”的設計是滿足這個場景的,但是所使用的橋接模式實際上更加符合現在Android手機的應用場景。手機硬體廠商隻負責生産手機硬體,而軟體廠商隻負責釋出功能軟體,通過将軟體組合成一個智能手機)

接下來我們還是通過代碼來感受一下橋接模式。

根據我們所畫的UML類圖先寫手機軟體。

1 package day_7_bridge;
 2 
 3 /**
 4  * 手機軟體接口
 5  * @author 餘林豐
 6  *
 7  * 2016年10月7日
 8  */
 9 public interface Software {
10     void run();
11 }      
1 package day_7_bridge;
 2 
 3 /**
 4  * 通訊錄軟體,實作手機軟體接口
 5  * @author 餘林豐
 6  *
 7  * 2016年10月7日
 8  */
 9 public class Contacts implements Software {
10 
11     /* (non-Javadoc)
12      * @see day_7_bridge.Software#run()
13      */
14     @Override
15     public void run() {
16         System.out.println("手機通訊錄");
17     }
18 
19 }      
1 package day_7_bridge;
 2 
 3 /**
 4  * 遊戲軟體,實作手機軟體接口
 5  * @author 餘林豐
 6  *
 7  * 2016年10月7日
 8  */
 9 public class Game implements Software {
10 
11     /* (non-Javadoc)
12      * @see day_7_bridge.Software#run()
13      */
14     @Override
15     public void run() {
16         System.out.println("遊戲軟體");
17     }
18 
19 }      

我們再來實作手機品牌的實作。

1 package day_7_bridge;
 2 
 3 /**
 4  * 手機品牌抽象類
 5  * @author 餘林豐
 6  *
 7  * 2016年10月7日
 8  */
 9 public abstract class AbstractPhone {
10     protected Software software;
11     
12     public void setSoftware(Software software){
13         this.software = software;
14     }
15     
16     public abstract void run();
17 }      
1 package day_7_bridge;
 2 
 3 /**
 4  * 手機品牌M
 5  * @author 餘林豐
 6  *
 7  * 2016年10月7日
 8  */
 9 public class PhoneM extends AbstractPhone {
10 
11     /* (non-Javadoc)
12      * @see day_7_bridge.AbstractPhone#run()
13      */
14     @Override
15     public void run() {
16         this.software.run();
17     }
18 }      
1 package day_7_bridge;
 2 
 3 /**
 4  * @author 餘林豐
 5  *
 6  * 2016年10月7日
 7  */
 8 public class PhoneN extends AbstractPhone {
 9 
10     /* (non-Javadoc)
11      * @see day_7_bridge.AbstractPhone#run()
12      */
13     @Override
14     public void run() {
15         this.software.run();
16     }
17 }      

用戶端測試代碼。

1 package day_7_bridge;
 2 
 3 /**
 4  * @author 餘林豐
 5  *
 6  * 2016年10月7日
 7  */
 8 public class Main {
 9 
10     /**
11      * @param args
12      */
13     public static void main(String[] args) {
14         PhoneM phoneM = new PhoneM();
15         phoneM.setSoftware(new Contacts());
16         phoneM.run();
17         phoneM.setSoftware(new Game());
18         phoneM.run();
19     }
20 
21 }      

就像上面所提到的,其實這個例子不怎麼合适,用現在Android手機一統天下,将軟硬體分離才契合這個橋接模式。說了那麼多,還是給一個橋接模式的定義:将抽象部分與它的實作部分分離,使它們都可以獨立地變化。注意,這裡的抽象與實作分離,并不是指的抽象類和派生類的分離,在這個例子中指的就是“手機”這個抽象,應把“手機品牌”和“手機軟體”做分離,而不是一味地使用繼承關系。

不積跬步,無以至千裡;不積小流,無以成江海。

上一篇: 疊代器模式
下一篇: 擴充卡模式