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

當我們看到上圖過後應該能明白什麼是組合和聚合了。聚合展現的是“弱”的擁有關系,比如雁群可以包含大雁,但雁群不是大雁的一部分。組合展現的是“強”的擁有關系,或者展現的是部分與整體的關系,通過一對翅膀組合成大雁,翅膀是部分,大雁是整體。
在了解了什麼是組合/聚合過後,我們來看看什麼是橋接模式。同樣我們通過《大話設計模式》書中的例子來說明。
在N多年前手機還未像現在的手機市場一樣,由Android和iOS一統天下。N年前各個手機廠商的軟體幾乎是互不相容,更嚴重的可能是同一個手機廠商不同型号的手機也互不相容。如果我們考慮這種場景應該如何來設計我們的代碼呢?我們通過手機品牌來分類。
通過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手機一統天下,将軟硬體分離才契合這個橋接模式。說了那麼多,還是給一個橋接模式的定義:将抽象部分與它的實作部分分離,使它們都可以獨立地變化。注意,這裡的抽象與實作分離,并不是指的抽象類和派生類的分離,在這個例子中指的就是“手機”這個抽象,應把“手機品牌”和“手機軟體”做分離,而不是一味地使用繼承關系。
不積跬步,無以至千裡;不積小流,無以成江海。