摘要:橋接模式屬于結(jié)構(gòu)型模式的一種,用于把抽象化與實(shí)現(xiàn)化解耦,使得二者可以獨(dú)立變化,它通過(guò)提供抽象化和實(shí)現(xiàn)化之間的橋接結(jié)構(gòu),來(lái)實(shí)現(xiàn)二者的解耦。相關(guān)模式裝飾模式與橋接模式在一定程度上都是為了減少子類(lèi)的數(shù)目,避免出現(xiàn)復(fù)雜的繼承關(guān)系。
概述橋接模式(Brideg Pattern)屬于結(jié)構(gòu)型模式的一種,用于把抽象化與實(shí)現(xiàn)化解耦,使得二者可以獨(dú)立變化,它通過(guò)提供抽象化和實(shí)現(xiàn)化之間的橋接結(jié)構(gòu),來(lái)實(shí)現(xiàn)二者的解耦。
橋接模式是一種很實(shí)用的結(jié)構(gòu)型設(shè)計(jì)模式,如果軟件系統(tǒng)中某個(gè)類(lèi)存在兩個(gè)獨(dú)立變化的維度,通過(guò)該模式可以將這兩個(gè)維度分離出來(lái),使兩者可以獨(dú)立擴(kuò)展,讓系統(tǒng)更加符合“單一職責(zé)原則”。與多層繼承方案不同,它將兩個(gè)獨(dú)立變化的維度設(shè)計(jì)為兩個(gè)獨(dú)立的繼承等級(jí)結(jié)構(gòu),并且在抽象層建立一個(gè)抽象關(guān)聯(lián),該關(guān)聯(lián)關(guān)系類(lèi)似一條連接兩個(gè)獨(dú)立繼承結(jié)構(gòu)的橋,故名橋接模式。
橋接模式用一種巧妙的方式處理多層繼承存在的問(wèn)題,用抽象關(guān)聯(lián)取代了傳統(tǒng)的多層繼承,將類(lèi)之間的靜態(tài)繼承關(guān)系轉(zhuǎn)換為動(dòng)態(tài)的對(duì)象組合關(guān)系,使得系統(tǒng)更加靈活,并易于擴(kuò)展。
合成復(fù)用原則合成復(fù)用原則又稱為組合/聚合復(fù)用原則(Composition/Aggregate Reuse Principle, CARP),指盡量使用對(duì)象組合,而不是繼承來(lái)達(dá)到復(fù)用的目的。
為什么要盡量使用合成和聚合,而不用繼承?
繼承復(fù)用破壞包裝,它把父類(lèi)的實(shí)現(xiàn)細(xì)節(jié)直接暴露給了子類(lèi),父類(lèi)發(fā)生改變,則子類(lèi)也要發(fā)生相應(yīng)的改變,這就直接導(dǎo)致了類(lèi)之間的緊耦合,不利于類(lèi)的擴(kuò)展、復(fù)用、維護(hù)。
合成與聚合的時(shí)候,對(duì)象交互往往是通過(guò)接口或抽象類(lèi)進(jìn)行的,可以很好的避免上面的不足,每個(gè)新的類(lèi)專注于實(shí)現(xiàn)自己的任務(wù),符合單一職責(zé)原則。
案例拿汽車(chē)在路上行駛的來(lái)說(shuō)。即有小汽車(chē)又有公共汽車(chē),它們都不但能在市區(qū)中的公路上行駛,也能在高速公路上行駛。這你會(huì)發(fā)現(xiàn),對(duì)于交通工具(汽車(chē))有不同的類(lèi)型,然而它們所行駛的環(huán)境(路)也在變化,在軟件系統(tǒng)中就要適應(yīng)兩個(gè)方面的變化?怎樣實(shí)現(xiàn)才能應(yīng)對(duì)這種變化呢?
緊耦合示例1.定義基類(lèi)Road,它擁有在上面行駛的方法
class Road { public void run() { System.out.println("在路上"); } }
2.路也有多種,這時(shí)候咋整?繼承一波唄
class SpeedWay extends Road { @Override public void run() { System.out.println("在高速公路上"); } } class Street extends Road { @Override public void run() { System.out.println("在市區(qū)街道上"); } }
3.問(wèn)題來(lái)了,不同的路跑著不同的車(chē),這個(gè)時(shí)候針對(duì)不同的車(chē)都要繼承SpeedWay/Street兩個(gè)類(lèi),假設(shè)我有(5種車(chē) 5條不同的路),媽呀那得多少類(lèi),先 (2 2)吧
class CarOnSpeedWay extends SpeedWay { @Override public void run() { System.out.println("汽車(chē)在高速公路上行駛"); } } class BusOnSpeedWay extends SpeedWay { @Override public void run() { System.out.println("公共汽車(chē)在高速公路上行駛"); } } class CarOnStreet extends Street { @Override public void run() { System.out.println("汽車(chē)在街道上行駛"); } } class BusOnStreet extends Street { @Override public void run() { System.out.println("公共汽車(chē)在街道上行駛"); } }
4.編寫(xiě)測(cè)試類(lèi),有木有發(fā)現(xiàn)代碼啰嗦不說(shuō),不是說(shuō)好的一般是面向接口編程么?
public class Client { public static void main(String[] args) { CarOnSpeedWay carOnSpeedWay = new CarOnSpeedWay(); carOnSpeedWay.run(); BusOnSpeedWay busOnSpeedWay = new BusOnSpeedWay(); busOnSpeedWay.run(); CarOnStreet carOnStreet = new CarOnStreet(); carOnStreet.run(); BusOnStreet busOnStreet = new BusOnStreet(); busOnStreet.run(); } }
弊端: 仔細(xì)分析就可以發(fā)現(xiàn),該方案雖然實(shí)現(xiàn)了功能但埋了不少坑,它在遵循開(kāi)放-封閉原則的同時(shí),違背了類(lèi)的單一職責(zé)原則,即一個(gè)類(lèi)只有一個(gè)引起它變化的原因,而這里引起變化的原因卻有兩個(gè),即路類(lèi)型的變化和汽車(chē)類(lèi)型的變化;其次是重復(fù)代碼會(huì)很多,不同的汽車(chē)在不同的路上行駛也會(huì)有一部分的代碼是相同的;再次是類(lèi)的結(jié)構(gòu)過(guò)于復(fù)雜,繼承關(guān)系太多,難于維護(hù),最后最致命的一點(diǎn)是擴(kuò)展性太差。如果變化沿著汽車(chē)的類(lèi)型和不同的道路兩個(gè)方向變化,我們會(huì)看到這個(gè)類(lèi)的結(jié)構(gòu)會(huì)迅速的變龐大。
松耦合示例首先我們要搞清楚,路上面可以行駛車(chē)輛(包含),但車(chē)并不是路的一部分,它們二者并不一定要緊密貼在一塊,它們之間是聚合關(guān)系
1.定義一個(gè)Car的接口,供給不同種類(lèi)的去實(shí)現(xiàn)
interface Car { void run(); }
2.抽象類(lèi)AbstractRoad,與Car關(guān)聯(lián)起來(lái),接收具體的實(shí)現(xiàn)
abstract class AbstractRoad { protected Car car; public void setCar(Car car) { this.car = car; } public abstract void run(); }
3.道路類(lèi)統(tǒng)一繼承AbstractRoad實(shí)現(xiàn)抽象方法,汽車(chē)類(lèi)統(tǒng)一實(shí)現(xiàn)Car接口
class SpeedWay extends AbstractRoad { @Override public void run() { car.run(); System.out.println("在高速公路上"); } } class Street extends AbstractRoad { @Override public void run() { car.run(); System.out.println("在市區(qū)街道上"); } } class SmallCar implements Car { @Override public void run() { System.out.println("小汽車(chē)"); } } class BigTruck implements Car { @Override public void run() { System.out.println("大卡車(chē)"); } }
4.編寫(xiě)測(cè)試類(lèi),通過(guò)傳入具體實(shí)現(xiàn)來(lái)獲得最終結(jié)果
public class Client { public static void main(String[] args) { Car bigTruck = new BigTruck(); Car smallCar = new SmallCar(); AbstractRoad way = new SpeedWay(); way.setCar(bigTruck); way.run(); way.setCar(smallCar); way.run(); AbstractRoad street = new Street(); street.setCar(bigTruck); street.run(); street.setCar(smallCar); street.run(); } }
可以看到,通過(guò)對(duì)象組合的方式,Bridge 模式把兩個(gè)角色之間的繼承關(guān)系改為了聚合的關(guān)系,從而使這兩者可以從容自若的各自獨(dú)立的變化,這也是Bridge模式的本意。
多維度變化結(jié)合上面的例子,增加一個(gè)維度"人",不同的人開(kāi)著不同的汽車(chē)在不同的路上行駛(三個(gè)維度);結(jié)合上面增加一個(gè)類(lèi)"人",并重新調(diào)用.
abstract class People { protected AbstractRoad abstractRoad; public void setAbstractRoad(AbstractRoad abstractRoad) { this.abstractRoad = abstractRoad; } public abstract void run(); } class Man extends People { @Override public void run() { System.out.println("男人開(kāi)著"); abstractRoad.run(); } } public class Client { public static void main(String[] args) { Car bigTruck = new BigTruck(); People people = new Man(); AbstractRoad street = new Street(); street.setCar(bigTruck); people.setAbstractRoad(street); people.run(); } }JDBC與橋接
通常使用JDBC連接數(shù)據(jù)庫(kù)時(shí),首選就是加載數(shù)據(jù)庫(kù)驅(qū)動(dòng),然后獲取數(shù)據(jù)庫(kù)連接
Class.forName("數(shù)據(jù)庫(kù)類(lèi)驅(qū)動(dòng)器"); Connection conn = DriverManager.getConnection("數(shù)據(jù)庫(kù)url", "用戶名", "密碼"); //.................
針對(duì)不同的數(shù)據(jù)庫(kù),JDBC都可以通過(guò)java.sql.DriverManager類(lèi)的靜態(tài)方法getConnection(數(shù)據(jù)庫(kù)url, 用戶名, 密碼)來(lái)獲取數(shù)據(jù)庫(kù)的連接。JDBC通過(guò)DriverManager對(duì)外提供了操作數(shù)據(jù)庫(kù)的統(tǒng)一接口getConnection,通過(guò)該方法可以獲取不同數(shù)據(jù)庫(kù)的連接,并且通過(guò)Connection類(lèi)提供的接口來(lái)進(jìn)行數(shù)據(jù)的查詢操作。
JDBC為不同的數(shù)據(jù)庫(kù)操作提供了相同的接口,但是JDBC本身并沒(méi)有針對(duì)每種數(shù)據(jù)庫(kù)提供一套具體實(shí)現(xiàn)代碼,而是通過(guò)接口java.sql.Driver的connect方法連接到了不同的數(shù)據(jù)庫(kù)實(shí)現(xiàn)。
public interface Driver { Connection connect(String url, java.util.Properties info) throws SQLException; //其他方法 }
在JDBC中,針對(duì)不同數(shù)據(jù)庫(kù)提供的統(tǒng)一的操作接口通過(guò)java.sql.Driver(橋)連接到了不同的數(shù)據(jù)庫(kù)實(shí)現(xiàn)。如連接mysql數(shù)據(jù)庫(kù)。
package com.mysql.jdbc; public class NonRegisteringDriver implements java.sql.Driver { public java.sql.Connection connect(String url, Properties info) throws SQLException { //省略具體實(shí)現(xiàn)代碼... } //其他方法 }具體實(shí)現(xiàn)
1.在com.mysql.jdbc.Driver的源碼中可以看到,類(lèi)加載時(shí)會(huì)將Driver注冊(cè)到DriverManager中。
package com.mysql.jdbc; public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can"t register driver!"); } } public Driver() throws SQLException { // Required for Class.forName().newInstance() } }
2.在DriverManager中,調(diào)用getConnection會(huì)迭代驅(qū)動(dòng)注冊(cè)表中的驅(qū)動(dòng),然后調(diào)用Driver接口提供的connect方法來(lái)獲取Connection對(duì)象
public class DriverManager { // JDBC驅(qū)動(dòng)程序注冊(cè)表 private final static CopyOnWriteArrayListregisteredDrivers = new CopyOnWriteArrayList<>(); public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { /* Register the driver if it has not already been added to our list */ if(driver != null) { registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { // This is for compatibility with the original DriverManager throw new NullPointerException(); } println("registerDriver: " + driver); } private static Connection getConnection(String url, java.util.Properties info, Class> caller) throws SQLException { 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 + "")"); SQLException reason = null; //遍歷registeredDrivers表 for(DriverInfo aDriver : registeredDrivers) { // 如果沒(méi)有加載驅(qū)動(dòng)的權(quán)限則跳過(guò) if(isDriverAllowed(aDriver.driver, callerCL)) { try { //調(diào)用Driver接口提供的connect方法來(lái)獲取Connection對(duì)象 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"); } } }
上述代碼中為橋接模式在JDBC中運(yùn)用方式
總結(jié)實(shí)現(xiàn)要點(diǎn)
Bridge模式使用對(duì)象間的組合關(guān)系解耦了抽象和實(shí)現(xiàn)之間固有的綁定關(guān)系,使得抽象和實(shí)現(xiàn)可以沿著各自的維度來(lái)變化。
所謂抽象和實(shí)現(xiàn)沿著各自維度的變化,即子類(lèi)化它們,得到各個(gè)子類(lèi)之后,便可以任意它們,從而獲得不同路上的不同汽車(chē)。
Bridge模式有時(shí)候類(lèi)似于多繼承方案,但是多繼承方案往往違背了類(lèi)的單一職責(zé)原則(即一個(gè)類(lèi)只有一個(gè)變化的原因),復(fù)用性比較差。Bridge模式是比多繼承方案更好的解決方法。
Bridge模式的應(yīng)用一般在兩個(gè)非常強(qiáng)的變化維度,有時(shí)候,即使兩個(gè)維度存在變化,但是變化的地方影響并不大,換而言之兩個(gè)變化不會(huì)導(dǎo)致縱橫交錯(cuò)的結(jié)果,并不一定要使用Bridge模式。
適用場(chǎng)景
如果一個(gè)系統(tǒng)需要在構(gòu)件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個(gè)層次之間建立靜態(tài)的聯(lián)系。
設(shè)計(jì)要求實(shí)現(xiàn)化角色的任何改變不應(yīng)當(dāng)影響客戶端,或者說(shuō)實(shí)現(xiàn)化角色的改變對(duì)客戶端是完全透明的。
一個(gè)構(gòu)件有多于一個(gè)的抽象化角色和實(shí)現(xiàn)化角色,系統(tǒng)需要它們之間進(jìn)行動(dòng)態(tài)耦合。
雖然在系統(tǒng)中使用繼承是沒(méi)有問(wèn)題的,但是由于抽象化角色和具體化角色需要獨(dú)立變化,設(shè)計(jì)要求需要獨(dú)立管理這兩者。
相關(guān)模式
裝飾模式與橋接模式在一定程度上都是為了減少子類(lèi)的數(shù)目,避免出現(xiàn)復(fù)雜的繼承關(guān)系。但是它們解決的方法卻各有不同,裝飾模式把子類(lèi)中比基類(lèi)中多出來(lái)的部分放到多帶帶的類(lèi)里面,以適應(yīng)新功能增加的需要,當(dāng)我們把描述新功能的類(lèi)封裝到基類(lèi)的對(duì)象里面時(shí),就得到了所需要的子類(lèi)對(duì)象,這些描述新功能的類(lèi)通過(guò)組合可以實(shí)現(xiàn)很多的功能組合.
- 說(shuō)點(diǎn)什么參考文獻(xiàn):http://blog.csdn.net/zlts000/article/details/26749723
參考文獻(xiàn):https://www.cnblogs.com/houleixx/archive/2008/02/23/1078877.html
參考文件:http://www.cnblogs.com/-crazysnail/p/3977815.html
全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter6/battcn-brideg
個(gè)人QQ:1837307557
battcn開(kāi)源群(適合新手):391619659
微信公眾號(hào):battcn(歡迎調(diào)戲)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/67998.html
摘要:本文只是尋找設(shè)計(jì)模式在中的應(yīng)用。來(lái)補(bǔ)全這一塊工廠模式中的應(yīng)用包線程池解釋和代碼線程池中有線程創(chuàng)建工廠。狀態(tài)模式中的應(yīng)用解釋和代碼根據(jù)一個(gè)指針的狀態(tài)而改變自己的行為適配器模式中的應(yīng)用解釋和代碼將一個(gè)類(lèi)的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。 前言 最近重學(xué)設(shè)計(jì)模式,而且還有很多源碼要看。所以就想一舉兩得。從源碼中尋找設(shè)計(jì)模式。順便還可以看看源碼。。。本文只是尋找設(shè)計(jì)模式在java中的應(yīng)用。優(yōu)...
摘要:該文章屬于編程中的那些經(jīng)典套路設(shè)計(jì)模式匯總系列,并且以下內(nèi)容基于語(yǔ)言今天來(lái)談?wù)剺蚪幽J剑瑯蚪幽J降墓δ茉谟趯蓚€(gè)原本不相關(guān)的類(lèi)結(jié)合在一起,然后利用兩個(gè)類(lèi)中的方法和屬性,輸出一份新的結(jié)果。 該文章屬于《編程中的那些經(jīng)典套路——設(shè)計(jì)模式匯總》系列,并且以下內(nèi)容基于語(yǔ)言PHP 今天來(lái)談?wù)剺蚪幽J剑瑯蚪幽J降墓δ茉谟趯蓚€(gè)原本不相關(guān)的類(lèi)結(jié)合在一起,然后利用兩個(gè)類(lèi)中的方法和屬性,輸出一份新的結(jié)果...
摘要:進(jìn)階多線程開(kāi)發(fā)關(guān)鍵技術(shù)后端掘金原創(chuàng)文章,轉(zhuǎn)載請(qǐng)務(wù)必將下面這段話置于文章開(kāi)頭處保留超鏈接。關(guān)于中間件入門(mén)教程后端掘金前言中間件 Java 開(kāi)發(fā)人員最常犯的 10 個(gè)錯(cuò)誤 - 后端 - 掘金一 、把數(shù)組轉(zhuǎn)成ArrayList 為了將數(shù)組轉(zhuǎn)換為ArrayList,開(kāi)發(fā)者經(jīng)常... Java 9 中的 9 個(gè)新特性 - 后端 - 掘金Java 8 發(fā)布三年多之后,即將快到2017年7月下一個(gè)版...
摘要:簡(jiǎn)介橋接模式將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化。同時(shí)橋接模式也有自己的缺點(diǎn)大量的類(lèi)將導(dǎo)致開(kāi)發(fā)成本的增加,同時(shí)在性能方面可能也會(huì)有所減少。 1. 簡(jiǎn)介 橋接模式(Bridge)將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化。其實(shí)就是函數(shù)的封裝,比如要對(duì)某個(gè)DOM元素添加color和backgroundColor,可以封裝個(gè)changeColor函數(shù),這樣可以在多個(gè)...
閱讀 2075·2023-04-25 17:48
閱讀 3586·2021-09-22 15:37
閱讀 2939·2021-09-22 15:36
閱讀 5997·2021-09-22 15:06
閱讀 1642·2019-08-30 15:53
閱讀 1428·2019-08-30 15:52
閱讀 713·2019-08-30 13:48
閱讀 1124·2019-08-30 12:44