一火本、橋接模式(Bridge Pattern)
在現(xiàn)實(shí)生活中灭贷,某些類具有兩個(gè)或多個(gè)維度的變化温学,如圖形既可按形狀分,又可按顏色分甚疟。如何設(shè)計(jì)類似于 Photoshop 這樣的軟件仗岖,能畫不同形狀和不同顏色的圖形呢?如果用繼承方式览妖,m 種形狀和 n 種顏色的圖形就有 m×n 種轧拄,不但對(duì)應(yīng)的子類很多,而且擴(kuò)展困難讽膏。
當(dāng)然檩电,這樣的例子還有很多,如不同顏色和字體的文字府树、不同品牌和功率的汽車俐末、不同性別和職業(yè)的男女、支持不同平臺(tái)和不同文件格式的媒體播放器等奄侠。如果用橋接模式就能很好地解決這些問(wèn)題卓箫。
1. 什么是橋接模式
橋接(Bridge)模式的定義如下:將抽象與實(shí)現(xiàn)分離,使它們可以獨(dú)立變化垄潮。它是用組合關(guān)系代替繼承關(guān)系來(lái)實(shí)現(xiàn)烹卒,從而降低了抽象和實(shí)現(xiàn)這兩個(gè)可變維度的耦合度。
橋接(Bridge)模式的優(yōu)點(diǎn)是:
- 由于抽象與實(shí)現(xiàn)分離弯洗,所以擴(kuò)展能力強(qiáng)旅急;
- 其實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶透明。
缺點(diǎn)是:由于聚合關(guān)系建立在抽象層牡整,要求開發(fā)者針對(duì)抽象化進(jìn)行設(shè)計(jì)與編程藐吮,這增加了系統(tǒng)的理解與設(shè)計(jì)難度。
2. 橋接模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 分離抽象部分及其實(shí)現(xiàn)部分,提供了比集成更好解決方案炎码;
- 提高了系統(tǒng)的可擴(kuò)展性盟迟,某種程度上可以避免子類爆炸
- 符合開閉原則
- 符合合成復(fù)用原則秋泳,通過(guò)組合方式實(shí)現(xiàn)關(guān)聯(lián)
缺點(diǎn):
- 增加了系統(tǒng)的理解與設(shè)計(jì)難度潦闲,由于聚合關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者針對(duì)抽象進(jìn)行設(shè)計(jì)和編程
- 需要正確地識(shí)別出系統(tǒng)中兩個(gè)獨(dú)立變化的緯度迫皱,因此其使用范圍具有一定的局限性
3. 橋接模式的適用場(chǎng)景
- 如果一個(gè)系統(tǒng)需要再構(gòu)建的抽象化角色和具體化角色之間增加更多的靈活性歉闰,避免在兩個(gè)層次之間建立靜態(tài)的繼承聯(lián)系,通過(guò)橋接模式可以使他們?cè)诔橄髮咏⒁粋€(gè)關(guān)聯(lián)關(guān)系
- 對(duì)于那些不希望使用繼承或因?yàn)槎鄬哟卫^承導(dǎo)致類的個(gè)數(shù)急劇增加的系統(tǒng)卓起,橋接模式尤為適用和敬;
- 一個(gè)類存在兩個(gè)(或多個(gè))獨(dú)立變化的緯度,且這兩個(gè)(或多個(gè))緯度都需要獨(dú)立進(jìn)行擴(kuò)展
- 不希望使用繼承戏阅,或因?yàn)槎鄬永^承導(dǎo)致系統(tǒng)類的個(gè)數(shù)劇增
4. 相關(guān)設(shè)計(jì)模式
橋接模式與組合模式的區(qū)別
- 組合模式:強(qiáng)調(diào)部分與整體的組合
- 橋接模式:強(qiáng)調(diào)平行類之間的組合
橋接模式與適配器模式的區(qū)別
- 適配器:改變已有接口昼弟,使他們相互配合,把功能相似但接口不同的類適配起來(lái)
- 橋接:類的抽象和實(shí)現(xiàn)分離開奕筐,目的就是分離舱痘,讓多個(gè)類能夠自由組合
- 二者的共同點(diǎn)是這兩個(gè)模式都是為了讓兩個(gè)類配合工作,但二者目的不同
二离赫、橋接模式的結(jié)構(gòu)與實(shí)現(xiàn)
可以將抽象化部分與實(shí)現(xiàn)化部分分開芭逝,取消二者的繼承關(guān)系,改用組合關(guān)系渊胸。
1. 橋接模式的結(jié)構(gòu)
橋接(Bridge)模式包含以下主要角色旬盯。
- 抽象化(Abstraction)角色:定義抽象類,并包含一個(gè)對(duì)實(shí)現(xiàn)化對(duì)象的引用翎猛。
- 擴(kuò)展抽象化(Refined Abstraction)角色:是抽象化角色的子類胖翰,實(shí)現(xiàn)父類中的業(yè)務(wù)方法,并通過(guò)組合關(guān)系調(diào)用實(shí)現(xiàn)化角色中的業(yè)務(wù)方法切厘。
- 實(shí)現(xiàn)化(Implementor)角色:定義實(shí)現(xiàn)化角色的接口萨咳,供擴(kuò)展抽象化角色調(diào)用。
- 具體實(shí)現(xiàn)化(Concrete Implementor)角色:給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn)迂卢。
其結(jié)構(gòu)圖如圖 1 所示某弦。
2. 橋接模式的實(shí)現(xiàn)
橋接模式的實(shí)現(xiàn)步驟
- 定義實(shí)現(xiàn)化角色接口 Implementor
- 定義具體實(shí)現(xiàn)化角色類 ConcreteImplementorA、ConcreteImplementorB
- 定義抽象化角色抽象類 Abstraction而克,以組合方式關(guān)聯(lián)實(shí)現(xiàn)化角色接口
- 定義擴(kuò)展抽象化角色類 RefinedAbstraction
- 編寫客戶端類 BridgePatternTest靶壮,調(diào)用具體實(shí)現(xiàn)化角色類、擴(kuò)展抽象化角色類
實(shí)現(xiàn)代碼示例:
-
定義實(shí)現(xiàn)化角色接口 Implementor
/** * Implementor - 實(shí)現(xiàn)化角色 * 實(shí)現(xiàn)化(Implementor)角色:定義實(shí)現(xiàn)化角色的接口员萍,供擴(kuò)展抽象化角色調(diào)用 * * @author dongrui * @date 2020/2/25 11:03 */ public interface Implementor { void operationImpl(); }
-
定義具體實(shí)現(xiàn)化角色類 ConcreteImplementorA腾降、ConcreteImplementorB
/** * ConcreteImplementorA - 具體實(shí)現(xiàn)化角色 A * 具體實(shí)現(xiàn)化(Concrete Implementor)角色:給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn) * * @author dongrui * @date 2020/2/25 11:05 */ public class ConcreteImplementorA implements Implementor{ @Override public void operationImpl() { System.out.println("使用 A 方式實(shí)現(xiàn)"); } }
/** * ConcreteImplementorB * 具體實(shí)現(xiàn)化(Concrete Implementor)角色:給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn) * * @author dongrui * @date 2020/2/25 11:08 */ public class ConcreteImplementorB implements Implementor { @Override public void operationImpl() { System.out.println("使用 B 方式實(shí)現(xiàn)"); } }
-
定義抽象化角色抽象類 Abstraction,以組合方式關(guān)聯(lián)實(shí)現(xiàn)化角色接口
/** * Abstraction - 抽象化角色 * 抽象化(Abstraction)角色:定義抽象類碎绎,并包含一個(gè)對(duì)實(shí)現(xiàn)化對(duì)象的引用 * * @author dongrui * @date 2020/2/25 11:09 */ public abstract class Abstraction { //使用組合方式關(guān)聯(lián)實(shí)現(xiàn)化角色 protected Implementor implementor; protected Abstraction(Implementor implementor) { this.implementor = implementor; } /** * 供客戶端調(diào)用的業(yè)務(wù)方法 */ abstract void operation(); }
-
定義擴(kuò)展抽象化角色類 RefinedAbstraction
/** * RefinedAbstraction - 擴(kuò)展抽象化角色螃壤,實(shí)現(xiàn)父類中的業(yè)務(wù)方法 * 擴(kuò)展抽象化(Refined Abstraction)角色:是抽象化角色的子類抗果,實(shí)現(xiàn)父類中的業(yè)務(wù)方法, * 并通過(guò)組合關(guān)系調(diào)用實(shí)現(xiàn)化角色中的業(yè)務(wù)方法奸晴。 * * @author dongrui * @date 2020/2/25 11:12 */ public class RefinedAbstraction extends Abstraction { public RefinedAbstraction(Implementor implementor) { super(implementor); } /** * 供客戶端調(diào)用的業(yè)務(wù)方法 */ @Override void operation() { System.out.println(this.implementor.getClass()); this.implementor.operationImpl(); } }
-
編寫客戶端類 BridgePatternTest冤馏,調(diào)用具體實(shí)現(xiàn)化角色類、擴(kuò)展抽象化角色類
/** * BridgePatternTest - 客戶端類 * * @author dongrui * @date 2020/2/25 11:17 */ public class BridgePatternTest { public static void main(String[] args) { Implementor implementorA = new ConcreteImplementorA(); Implementor implementorB = new ConcreteImplementorB(); Abstraction abs = new RefinedAbstraction(implementorA); abs.operation(); } }
三寄啼、橋接模式的案例實(shí)戰(zhàn)
1. 案例1—銀行存款
銀行存款業(yè)務(wù)場(chǎng)景:小張有一筆錢想用于理財(cái)投資逮光,為此做了一番功課了解到,現(xiàn)在有中國(guó)工商銀行墩划、中國(guó)農(nóng)業(yè)銀行兩家銀行涕刚,每家銀行都提供了活期存儲(chǔ)和定期存款,活期存款存取靈活但利息低乙帮,定期存款利息相對(duì)較高但存取不夠靈活杜漠,同時(shí)了解到中國(guó)工商銀行的定期存款利率相對(duì)較高,中國(guó)農(nóng)業(yè)銀行的活期存款利率相對(duì)較高察净,于是小張計(jì)劃將投資款項(xiàng)分為2部分驾茴,一部分存儲(chǔ)到中國(guó)工商銀行的定期賬戶,一部分存儲(chǔ)到中國(guó)農(nóng)業(yè)銀行的活期賬戶塞绿。
請(qǐng)根據(jù)以上業(yè)務(wù)設(shè)計(jì)一個(gè)銀行存款系統(tǒng)沟涨,提供靈活的配置功能。
1.1 業(yè)務(wù)場(chǎng)景分析
從業(yè)務(wù)場(chǎng)景可以知道异吻,有多家銀行裹赴,每家銀行都有自己的定期和活期賬戶,而客戶可以對(duì)銀行诀浪、存款類型做隨機(jī)組合棋返,因此,這種多緯度組合的場(chǎng)景非常適合使用橋接模式解決雷猪。
橋接模式的核心是將抽象與實(shí)現(xiàn)分離睛竣,使他們可以獨(dú)立變化,在該模式中求摇,活期存款射沟、定期存款是銀行存款的最終實(shí)現(xiàn)部分,是橋接模式的實(shí)現(xiàn)部分与境,中國(guó)工商銀行验夯、中國(guó)農(nóng)業(yè)銀行等銀行是抽象部分,不提供實(shí)現(xiàn)操作摔刁,通過(guò)組合存款賬戶方式實(shí)現(xiàn)存款的實(shí)現(xiàn)操作挥转。
因此,結(jié)合橋接模式設(shè)計(jì)銀行存款:
- 定義實(shí)現(xiàn):存款賬戶是實(shí)現(xiàn)部分,抽象出存款賬戶類為實(shí)現(xiàn)化接口绑谣,活期存儲(chǔ)党窜、定期存款為其具體實(shí)現(xiàn)
- 定義抽象:銀行是抽象部分,抽象出銀行類為抽象類借宵,以成員屬性方式組合存儲(chǔ)賬戶幌衣,中國(guó)工商銀行、中國(guó)農(nóng)業(yè)銀行為其擴(kuò)展實(shí)現(xiàn)
- 定義客戶端:在客戶端類中選擇具體賬戶和具體銀行暇务,實(shí)現(xiàn)存款業(yè)務(wù)邏輯
1.2 UML類圖
1.3 代碼實(shí)現(xiàn)示例
代碼實(shí)現(xiàn)步驟:
- 定義存款賬戶接口
- 定義活期存款賬戶實(shí)現(xiàn)類泼掠、活期存款賬戶實(shí)現(xiàn)類怔软,都實(shí)現(xiàn)存款賬戶接口并重寫接口的方法
- 定義銀行抽象類垦细,將存款賬戶作為成員屬性,實(shí)現(xiàn)對(duì)存款操作的組合方式調(diào)用
- 定義中國(guó)工商銀行挡逼、中國(guó)農(nóng)業(yè)銀行的擴(kuò)展類括改,實(shí)現(xiàn)具體的存款操作
- 定義客戶端測(cè)試類,選擇中國(guó)工商銀行存儲(chǔ)定期存款家坎,選擇中國(guó)農(nóng)業(yè)銀行存儲(chǔ)活期存款
代碼實(shí)現(xiàn)示例:
-
定義存款賬戶接口
/** * Account - 賬戶接口 * * @author dongrui * @date 2020/2/20 16:20 */ public interface Account { void saveMoney(Double money); void printAccountType(); }
-
定義活期存款賬戶實(shí)現(xiàn)類嘱能、活期存款賬戶實(shí)現(xiàn)類,都實(shí)現(xiàn)存款賬戶接口并重寫接口的方法
/** * DepositAccount - 定期存款賬戶 * * @author dongrui * @date 2020/2/20 16:23 */ public class DepositAccount implements Account { public void saveMoney(Double money) { System.out.println("打開定期賬號(hào)"); System.out.printf("存款:%f 元\n", money); } public void printAccountType() { System.out.println("這是一個(gè)定期賬號(hào)"); } }
/** * SavingsAccount - 活期存儲(chǔ)賬戶 * * @author dongrui * @date 2020/2/20 16:24 */ public class SavingsAccount implements Account { public void saveMoney(Double money) { System.out.println("打開活期賬號(hào)"); System.out.printf("存款:%f 元\n", money); } public void printAccountType() { System.out.println("這是一個(gè)活期賬號(hào)"); } }
-
定義銀行抽象類虱疏,將存款賬戶作為成員屬性惹骂,實(shí)現(xiàn)對(duì)存款操作的組合方式調(diào)用
/** * Bank - 銀行抽象類,以組合方式調(diào)用存款賬戶接口 * * @author dongrui * @date 2020/2/20 16:25 */ public abstract class Bank { protected Account account; public Bank(Account account) { this.account = account; } abstract void saveMoney(Double money); }
-
定義中國(guó)工商銀行做瞪、中國(guó)農(nóng)業(yè)銀行的擴(kuò)展類对粪,實(shí)現(xiàn)具體的存款操作
/** * ICBCBank * * @author dongrui * @date 2020/2/20 16:27 */ public class ICBCBank extends Bank { public ICBCBank(Account account) { super(account); } void saveMoney(Double money) { System.out.println("打開中國(guó)工商銀行賬號(hào)"); this.account.saveMoney(money); } }
/** * ABCBank * * @author dongrui * @date 2020/2/20 16:26 */ public class ABCBank extends Bank { public ABCBank(Account account) { super(account); } void saveMoney(Double money) { System.out.println("打開中國(guó)農(nóng)業(yè)銀行賬號(hào)"); account.saveMoney(money); } }
-
定義客戶端測(cè)試類,選擇中國(guó)工商銀行存儲(chǔ)定期存款装蓬,選擇中國(guó)農(nóng)業(yè)銀行存儲(chǔ)活期存款
public class BankSavingsTest { public static void main(String[] args) { //定期賬號(hào)和活期賬號(hào) Account depositAccount = new DepositAccount(); Account savingsAccount = new SavingsAccount(); //工商銀行定期賬號(hào) Bank icbc = new ICBCBank(depositAccount); icbc.saveMoney(20000.00); System.out.println("------"); //農(nóng)業(yè)銀行活期賬號(hào) Bank abc = new ABCBank(savingsAccount); abc.saveMoney(3000.00); } }
附錄
參考資料:
- 橋接模式(Bridge模式)詳解 : http://c.biancheng.net/view/1364.html
- 橋接模式 : https://www.runoob.com/w3cnote/bridge-pattern2.html
- 設(shè)計(jì)模式讀書筆記-----橋接模式: https://www.cnblogs.com/chenssy/p/3317866.html