新聞中心
本篇和大家一起來(lái)學(xué)習(xí)橋接模式相關(guān)內(nèi)容。

創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括紫金網(wǎng)站建設(shè)、紫金網(wǎng)站制作、紫金網(wǎng)頁(yè)制作以及紫金網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃等。多年來(lái),我們專(zhuān)注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,紫金網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶(hù)以成都為中心已經(jīng)輻射到紫金省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶(hù)的支持與信任!
模式定義
將抽象與實(shí)現(xiàn)分離,使它們可以獨(dú)立變化。它是用組合關(guān)系代替繼承關(guān)系來(lái)實(shí)現(xiàn),從而降低了抽象和實(shí)現(xiàn)這兩個(gè)可變維度的耦合度。
模式實(shí)現(xiàn)如下:
- package com.niuh.designpattern.bridge.v1;
- /**
- * 橋接模式
- */
- public class BridgePattern {
- public static void main(String[] args) {
- Implementor imple=new ConcreteImplementorA();
- Abstraction abs=new RefinedAbstraction(imple);
- abs.Operation();
- }
- }
- //實(shí)現(xiàn)化角色
- interface Implementor {
- void OperationImpl();
- }
- //具體實(shí)現(xiàn)化角色
- class ConcreteImplementorA implements Implementor {
- public void OperationImpl() {
- System.out.println("具體實(shí)現(xiàn)化(Concrete Implementor)角色被訪(fǎng)問(wèn)");
- }
- }
- //抽象化角色
- abstract class Abstraction {
- protected Implementor imple;
- protected Abstraction(Implementor imple) {
- this.imple = imple;
- }
- public abstract void Operation();
- }
- //擴(kuò)展抽象化角色
- class RefinedAbstraction extends Abstraction {
- protected RefinedAbstraction(Implementor imple) {
- super(imple);
- }
- public void Operation() {
- System.out.println("擴(kuò)展抽象化(Refined Abstraction)角色被訪(fǎng)問(wèn)");
- imple.OperationImpl();
- }
- }
輸出結(jié)果如下:
- 擴(kuò)展抽象化(Refined Abstraction)角色被訪(fǎng)問(wèn)
- 具體實(shí)現(xiàn)化(Concrete Implementor)角色被訪(fǎng)問(wèn)
解決的問(wèn)題
在有多種可能會(huì)變化的情況下,用繼承會(huì)造成類(lèi)爆炸問(wèn)題,擴(kuò)展起來(lái)不靈活。
模式組成
可以將抽象化部分與實(shí)現(xiàn)化部分分開(kāi),取消二者的繼承關(guān)系,改用組合關(guān)系。
實(shí)例說(shuō)明
實(shí)例概況
某公司開(kāi)發(fā)了一個(gè)財(cái)務(wù)管理系統(tǒng),其中有個(gè)報(bào)表生成器的工具模塊,客戶(hù)可以指定任意一種報(bào)表類(lèi)型,如基本報(bào)表,往來(lái)報(bào)表,資金報(bào)表,資產(chǎn)報(bào)表等,并且可以指定不同 的報(bào)表樣式,如餅圖,柱狀圖等。系統(tǒng)設(shè)計(jì)人員針對(duì)這個(gè)報(bào)表生成器的結(jié)構(gòu)設(shè)計(jì)了如下圖所示的類(lèi)圖。
后來(lái)在客戶(hù)使用過(guò)程中,客戶(hù)又希望增加一個(gè)新的報(bào)表和新的線(xiàn)形圖,開(kāi)發(fā)人員這個(gè)時(shí)候發(fā)現(xiàn)維護(hù)起來(lái)非常麻煩,設(shè)計(jì)人員經(jīng)過(guò)仔細(xì)分析,發(fā)現(xiàn)存在嚴(yán)重的問(wèn)題,因?yàn)樾略黾右粋€(gè)報(bào)表或者圖,需要增加很多子類(lèi)。所以,系統(tǒng)分析師最終對(duì)這個(gè)模塊根據(jù)面向?qū)ο蟮脑O(shè)計(jì)原則對(duì)上面的方案進(jìn)行了重構(gòu),重構(gòu)后的圖如下所示。
在本重構(gòu)方案中,將報(bào)表和圖形設(shè)計(jì)成兩個(gè)繼承結(jié)構(gòu),兩者都可以獨(dú)立變化,編程的時(shí)候可以只針對(duì)抽象類(lèi)編碼,而在運(yùn)行的時(shí)候再將具體的圖形子類(lèi)對(duì)象注入到具體的 報(bào)表類(lèi)中。這樣的話(huà),系統(tǒng)就具有良好的可擴(kuò)展性和可維護(hù)性,并且滿(mǎn)足了面向?qū)ο笤O(shè)計(jì)原則的開(kāi)閉原則。
使用步驟
步驟1:定義實(shí)現(xiàn)化角色,報(bào)表接口
- interface IReport {
- void operationImpl();
- }
步驟2:定義具體實(shí)現(xiàn)化角色(基本報(bào)表、往來(lái)報(bào)表、資金報(bào)表)
- class BasicReport implements IReport {
- @Override
- public void operationImpl() {
- System.out.println("基本報(bào)表被訪(fǎng)問(wèn).");
- }
- }
- class IntercourseReport implements IReport {
- @Override
- public void operationImpl() {
- System.out.println("往來(lái)報(bào)表被訪(fǎng)問(wèn).");
- }
- }
- class CapitalReport implements IReport {
- @Override
- public void operationImpl() {
- System.out.println("資金報(bào)表被訪(fǎng)問(wèn).");
- }
- }
步驟3:定義抽象化角色,圖形
- abstract class AbstractionGraph {
- protected IReport iReport;
- public AbstractionGraph(IReport iReport) {
- this.iReport = iReport;
- }
- abstract void operation();
- }
步驟4:定義擴(kuò)展抽象化角色(柱狀圖、餅圖)
- class Barchart extends AbstractionGraph {
- public Barchart(IReport iReport) {
- super(iReport);
- }
- @Override
- void operation() {
- System.out.println("柱狀圖被訪(fǎng)問(wèn).");
- iReport.operationImpl();
- }
- }
- class Piechart extends AbstractionGraph {
- public Piechart(IReport iReport) {
- super(iReport);
- }
- @Override
- void operation() {
- System.out.println("餅圖被訪(fǎng)問(wèn).");
- iReport.operationImpl();
- }
- }
步驟5:測(cè)試
- public class BridgePattern {
- public static void main(String[] args) {
- //實(shí)現(xiàn)化和抽象化分離
- // 基本報(bào)表
- IReport basicReport = new BasicReport();
- // 往來(lái)報(bào)表
- IReport intercourseReport = new IntercourseReport();
- // 資金報(bào)表
- IReport capitalReport = new CapitalReport();
- // 基本報(bào)表使用柱狀圖
- AbstractionGraph barchart = new Barchart(basicReport);
- barchart.operation();
- // 基本報(bào)表使用餅圖
- AbstractionGraph piechart = new Piechart(basicReport);
- piechart.operation();
- }
- }
輸出結(jié)果
- 柱狀圖被訪(fǎng)問(wèn).
- 基本報(bào)表被訪(fǎng)問(wèn).
- 餅圖被訪(fǎng)問(wèn).
- 基本報(bào)表被訪(fǎng)問(wèn).
優(yōu)點(diǎn)
橋接模式遵循了里氏替換原則和依賴(lài)倒置原則,最終實(shí)現(xiàn)了開(kāi)閉原則,對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放。這里將橋接模式的優(yōu)缺點(diǎn)總結(jié)如下。
橋接(Bridge)模式的優(yōu)點(diǎn):
- 抽象與實(shí)現(xiàn)分離,擴(kuò)展能力強(qiáng)
- 符合開(kāi)閉原則
- 符合合成復(fù)用原則
- 其實(shí)現(xiàn)細(xì)節(jié)對(duì)客戶(hù)透明
缺點(diǎn)
由于聚合關(guān)系建立在抽象層,要求開(kāi)發(fā)者針對(duì)抽象化進(jìn)行設(shè)計(jì)與編程,能正確地識(shí)別出系統(tǒng)中兩個(gè)獨(dú)立變化的維度,這增加了系統(tǒng)的理解與設(shè)計(jì)難度。
應(yīng)用場(chǎng)景
當(dāng)一個(gè)類(lèi)內(nèi)部具備兩種或多種變化維度時(shí),使用橋接模式可以解耦這些變化的維度,使高層代碼架構(gòu)穩(wěn)定。
橋接模式通常適用于以下場(chǎng)景:
- 當(dāng)一個(gè)類(lèi)存在兩個(gè)獨(dú)立變化的維度,且這兩個(gè)維度都需要進(jìn)行擴(kuò)展時(shí);
- 當(dāng)一個(gè)系統(tǒng)不希望使用繼承或因?yàn)槎鄬哟卫^承導(dǎo)致系統(tǒng)類(lèi)的個(gè)數(shù)急劇增加時(shí);
- 當(dāng)一個(gè)系統(tǒng)需要在構(gòu)件的抽象化角色和具體化角色之間增加更多的靈活性時(shí)。
橋接模式的一個(gè)常見(jiàn)使用場(chǎng)景就是替換繼承。我們知道,繼承擁有很多優(yōu)點(diǎn),比如,抽象、封裝、多態(tài)等,父類(lèi)封裝共性,子類(lèi)實(shí)現(xiàn)特性。繼承可以很好的實(shí)現(xiàn)代碼復(fù)用(封裝)的功能,但這也是繼承的一大缺點(diǎn)。
因?yàn)楦割?lèi)擁有的方法,子類(lèi)也會(huì)繼承得到,無(wú)論子類(lèi)需不需要,這說(shuō)明繼承具備強(qiáng)侵入性(父類(lèi)代碼侵入子類(lèi)),同時(shí)會(huì)導(dǎo)致子類(lèi)臃腫。因此,在設(shè)計(jì)模式中,有一個(gè)原則為優(yōu)先使用組合/聚合,而不是繼承。
橋接模式模式的擴(kuò)展
在軟件開(kāi)發(fā)中,有時(shí)橋接(Bridge)模式可與適配器模式聯(lián)合使用。當(dāng)橋接(Bridge)模式的實(shí)現(xiàn)化角色的接口與現(xiàn)有類(lèi)的接口不一致時(shí),可以在二者中間定義一個(gè)適配器將二者連接起來(lái),其結(jié)構(gòu)圖如下:
源碼中的應(yīng)用
- JDBC驅(qū)動(dòng)程序
- ......
DriverManager類(lèi)
DriverManager作為一個(gè)抽象化角色,聚合了實(shí)現(xiàn)化角色Connection,只不過(guò)與標(biāo)準(zhǔn)的橋梁模式不一樣的是,DriverManager類(lèi)下面沒(méi)有子類(lèi)。
- // Worker method called by the public getConnection() methods.
- private static Connection getConnection(
- String url, java.util.Properties info, Class> caller) throws SQLException {
- /*
- * When callerCl is null, we should check the application's
- * (which is invoking this class indirectly)
- * classloader, so that the JDBC driver class outside rt.jar
- * can be loaded from here.
- */
- ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
- synchronized(DriverManager.class) {
- // synchronize loading of the correct classloader.
- if (callerCL == null) {
- callerCL = Thread.currentThread().getContextClassLoader();
- }
- }
- if(url == null) {
- throw new SQLException("The url cannot be null", "08001");
- }
- println("DriverManager.getConnection(\"" + url + "\")");
- // Walk through the loaded registeredDrivers attempting to make a connection.
- // Remember the first exception that gets raised so we can reraise it.
- SQLException reason = null;
- for(DriverInfo aDriver : registeredDrivers) {
- // If the caller does not have permission to load the driver then
- // skip it.
- if(isDriverAllowed(aDriver.driver, callerCL)) {
- try {
- println(" trying " + aDriver.driver.getClass().getName());
- Connection con = aDriver.driver.connect(url, info);
- if (con != null) {
- // Success!
- println("getConnection returning " + aDriver.driver.getClass().getName());
- return (con);
- }
- } catch (SQLException ex) {
- if (reason == null) {
- reason = ex;
- }
- }
- } else {
- println(" skipping: " + aDriver.getClass().getName());
- }
- }
- // if we got here nobody could connect.
- if (reason != null) {
- println("getConnection failed: " + reason);
- throw reason;
- }
- println("getConnection: no suitable driver found for "+ url);
- throw new SQLException("No suitable driver found for "+ url, "08001");
- }
PS:以上代碼提交在 Github :
https://github.com/Niuh-Study/niuh-designpatterns.git
文章名稱(chēng):設(shè)計(jì)模式系列—橋接模式
當(dāng)前URL:http://www.5511xx.com/article/cogechd.html


咨詢(xún)
建站咨詢(xún)
