「補(bǔ)課」進(jìn)行時(shí):設(shè)計(jì)模式(22)——橋梁模式

1. 前文匯總
「補(bǔ)課」進(jìn)行時(shí):設(shè)計(jì)模式系列
2. 橋梁模式
設(shè)計(jì)模式的最后一篇文章,還是讓我這個(gè)拖延癥晚期的患者把時(shí)間拖到了 2020 年的最后一天。
雖然是最后一篇,但并不是最難的一個(gè)模式,比較復(fù)雜的兩個(gè)模式已經(jīng)在前面介紹過了,一個(gè)是訪問者模式,另一個(gè)是解釋器模式。
那么什么是橋梁模式?
2.1 定義
橋梁模式(Bridge Pattern) 也叫做橋接模式, 是一個(gè)比較簡單的模式, 其定義如下:Decouple an abstraction from its implementation so that the two can vary independently.(將抽象和實(shí)現(xiàn)解耦, 使得兩者可以獨(dú)立地變化。)
橋梁模式的重點(diǎn)是在「解耦」上, 如何讓它們兩者解耦是橋梁模式的重點(diǎn),
2.2 通用類圖

Abstraction 抽象化角色:定義出該角色的行為, 同時(shí)保存一個(gè)對實(shí)現(xiàn)化角色的引用, 該角色一般是抽象類 Implementor 實(shí)現(xiàn)化角色:它是接口或者抽象類, 定義角色必需的行為和屬性。 RefinedAbstraction 修正抽象化角色:實(shí)現(xiàn)化角色對抽象化角色進(jìn)行修正。 ConcreteImplementor 具體實(shí)現(xiàn)化角色:實(shí)現(xiàn)接口或抽象類定義的方法和屬性。
橋梁模式中的幾個(gè)名詞比較拗口,核心就一句話:抽象角色引用實(shí)現(xiàn)角色,或者說抽象角色的部分實(shí)現(xiàn)是由實(shí)現(xiàn)角色完成的。
2.3 通用代碼
實(shí)現(xiàn)化角色:
public?interface?Implementor?{
????//?基本方法
????void?doSomething();
????void?doAnything();
}
具體實(shí)現(xiàn)化角色:
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()?{
????}
}
抽象化角色:
public?abstract?class?Abstraction?{
????//?定義對實(shí)現(xiàn)化角色的引用
????private?Implementor?impl;
????//?約束子類必須實(shí)現(xiàn)該構(gòu)造函數(shù)
????public?Abstraction(Implementor?impl){
????????this.impl?=?impl;
????}
????//?自身的行為和屬性
????public?void?request(){
????????this.impl.doSomething();
????}
????//?獲得實(shí)現(xiàn)化角色
????public?Implementor?getImpl(){
????????return?impl;
????}
}
具體抽象化角色:
public?class?RefinedAbstraction?extends?Abstraction?{
????//?覆寫構(gòu)造函數(shù)
????public?RefinedAbstraction(Implementor?impl)?{
????????super(impl);
????}
????//?修正父類的行為
????@Override
????public?void?request(){
????????//?業(yè)務(wù)處理
????????super.request();
????????super.getImpl().doAnything();
????}
}
客戶端類:
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????//?定義一個(gè)實(shí)現(xiàn)化角色
????????Implementor?imp?=?new?ConcreteImplementor1();
????????//?定義一個(gè)抽象化角色
????????Abstraction?abs?=?new?RefinedAbstraction(imp);
????????//?執(zhí)行行文
????????abs.request();
????}
}
2.4 優(yōu)點(diǎn)
抽象和實(shí)現(xiàn)分離:橋梁模式完全是為了解決繼承的缺點(diǎn)而提出的設(shè)計(jì)模式。 優(yōu)秀的擴(kuò)展能力。 實(shí)現(xiàn)細(xì)節(jié)對客戶透明。客戶不用關(guān)心細(xì)節(jié)的實(shí)現(xiàn),它已經(jīng)由抽象層通過聚合關(guān)系完成了封裝。
2.5 缺點(diǎn)
會(huì)增加系統(tǒng)的理解與設(shè)計(jì)難度。由于聚合關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者針對抽象進(jìn)行設(shè)計(jì)與編程。
2.6 使用場景
不希望或不適用使用繼承的場景。 接口或抽象類不穩(wěn)定的場景。 重用性要求較高的場景。
3. 一個(gè)簡單的案例
在橋接模式中要把握的很重要的一點(diǎn)就是:類的繼承關(guān)系和類的組合/聚合關(guān)系,何時(shí)應(yīng)該考慮使用何種關(guān)系。是不是在編程過程中一味地使用類的繼承關(guān)系就代表這就是面向?qū)ο缶幊塘耍坑袝r(shí)候并不是這樣, Java 的類繼承設(shè)計(jì)成單繼承模式我想應(yīng)該就是不想把類的繼承關(guān)系搞得過于復(fù)雜,實(shí)際上我們應(yīng)該優(yōu)先使用對象組合/聚合,而不是類繼承。
類繼承不必多說,先來看看何為組合/聚合關(guān)系。

聚合體現(xiàn)的是「弱」的擁有關(guān)系,比如雁群可以包含大雁,但雁群不是大雁的一部分。組合體現(xiàn)的是「強(qiáng)」的擁有關(guān)系,或者體現(xiàn)的是部分與整體的關(guān)系,通過一對翅膀組合成大雁,翅膀是部分,大雁是整體。
我們現(xiàn)在天天使用的一個(gè)電器:手機(jī),手機(jī)有手機(jī)品牌和手機(jī)軟件等等,每個(gè)手機(jī)品牌都有多款軟件。
這個(gè)案例如果使用繼承關(guān)系的話,不管是已手機(jī)品牌還是手機(jī)軟件作為父類,都會(huì)對后續(xù)的擴(kuò)展造成很大的影響,如果我們使用橋梁模式,使用對象的組合/聚合,類圖是下面這樣:

通過UML類結(jié)構(gòu)圖我們可以看到手機(jī)品牌和手機(jī)軟件成功解耦,新增功能并不影響其手機(jī)品牌,新增手機(jī)品牌也不會(huì)影響到手機(jī)軟件,其中的奧秘就在于利用了聚合而不是繼承。
代碼如下:
手機(jī)品牌抽象類:
public?abstract?class?HandsetBrand?{
????protected?HandsetSoft?soft;
????//?設(shè)置手機(jī)軟件
????public?void?setHandsetSoft(HandsetSoft?soft)?{
????????this.soft?=?soft;
????}
????//?運(yùn)行
????abstract?void?run();
}
手機(jī)軟件抽象類:
public?abstract?class?HandsetSoft?{
????abstract?void?run();
}
各類手機(jī)品牌實(shí)現(xiàn)類:
public?class?HandsetBrandA?extends?HandsetBrand?{
????@Override
????void?run()?{
????????super.soft.run();
????}
}
public?class?HandsetBrandB?extends?HandsetBrand{
????@Override
????void?run()?{
????????super.soft.run();
????}
}
手機(jī)軟件實(shí)現(xiàn)類:
public?class?HandsetGame?extends?HandsetSoft{
????@Override
????void?run()?{
????????System.out.println("運(yùn)行手機(jī)游戲");
????}
}
public?class?HandsetAddressList?extends?HandsetSoft{
????@Override
????void?run()?{
????????System.out.println("運(yùn)行手機(jī)通訊錄");
????}
}
客戶端類:
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????HandsetBrand?ab;
????????//?使用?A?品牌手機(jī)
????????ab?=?new?HandsetBrandA();
????????System.out.println("A品牌手機(jī):");
????????ab.setHandsetSoft(new?HandsetGame());
????????ab.run();
????????ab.setHandsetSoft(new?HandsetAddressList());
????????ab.run();
????????//?使用?B?品牌手機(jī)
????????ab?=?new?HandsetBrandB();
????????System.out.println("B品牌手機(jī):");
????????ab.setHandsetSoft(new?HandsetGame());
????????ab.run();
????????ab.setHandsetSoft(new?HandsetAddressList());
????????ab.run();
????}
}
執(zhí)行結(jié)果如下:
A品牌手機(jī):
運(yùn)行手機(jī)游戲
運(yùn)行手機(jī)通訊錄
B品牌手機(jī):
運(yùn)行手機(jī)游戲
運(yùn)行手機(jī)通訊錄

