Java設計模式之橋接模式
本文通過老王和小王買車,引出設計模式中的結構型設計之橋接模式,接著說明設計型模式的概念和代碼實現,為了加深理解,會說明適配器設計模式在JDBC中的應用,最后談談橋接模式和適配器模式的總結。
讀者可以拉取完整代碼到本地進行學習,實現代碼均測試通過后上傳到碼云,本地源碼下載。
一、引出問題
老王和小王去奔馳4S店買車,奔馳4S店的各種品牌型號琳瑯滿目,老王想試駕奔馳E、小王想試駕奔馳G,并且提出兩種奔馳型號的各種顏色都想體驗一把,這讓店小二犯了難,兩兩組合就是很多種,4S店壓根放不下。
無奈店小二求救經理,經理出了一個注意:將奔馳E和G開的品牌抽象出來,將顏色也抽象出來,通過品牌和顏色的組合代替繼承關系,減少了顏色和品牌的耦合,且減少了車的個數,只需要兩臺就夠了。
果然經理不愧是經理。
經理所說的其實就是橋接模式。這種模式涉及到一個作為橋接的接口,使得實體類的功能獨立于接口實現類。這兩種類型的類可被結構化改變而互不影響。
二、概念與使用
我們看一些概念:橋接(Bridge)是用于把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬于結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。
在該模式中應該涉及到四個角色:
①實現類接口(Implementor):定義實現角色的接口,供擴展抽象化角色使用,例如抽象出奔馳品牌benz 可以擴展出 benzE benzG
②具體實現角色(ConcreteImplementor):實現類的具體實現,例如各種奔馳品牌
③抽象化(Abstraction)角色:定義一個抽象類,其中引用了實現化角色(想要組合),例如汽車產品
④擴展抽象化(RefinedAbstraction)角色:抽象化角色子類,實現父類方法,且通過組合關系調用實現化角色中的業(yè)務方法,例如具體奔馳產品,紅色奔馳、白色奔馳
根據該模式的定義,我們將奔馳品牌抽象出來,然后各品牌有各自的實現,每個顏色的車把車品牌組合進來,在客戶端中每個相機類型和相機品牌都能兩兩組合。
我們看具體的代碼實現:
實現類接口:
/** * 奔馳品牌類 * @author tcy * @Date 05-08-2022 */ public interface BenzBrand { void showInfo(); }
具體實現角色1:
/** * @author tcy * @Date 05-08-2022 */ public class BenzE implements BenzBrand{ @Override public void showInfo() { System.out.print("【奔馳E】顏色是:"); } }
具體實現角色2:
/** * @author tcy * @Date 05-08-2022 */ public class BenzG implements BenzBrand{ @Override public void showInfo() { System.out.print("【奔馳G】顏色是:"); } }
抽象化角色:
/** * 抽象奔馳類 * @author tcy * @Date 05-08-2022 */ public abstract class Benz { // 將品牌組合進來 protected BenzBrand benzBrand; public Benz(BenzBrand benzBrand) { this.benzBrand = benzBrand; } public void showInfo(){ benzBrand.showInfo(); } }
擴展抽象化1:
/** * @author tcy * @Date 05-08-2022 */ public class BlackBenz extends Benz { public BlackBenz(BenzBrand benzBrand) { super(benzBrand); } @Override public void showInfo() { super.showInfo(); System.out.println("黑色..."); } }
擴展抽象化2:
/** * @author tcy * @Date 05-08-2022 */ public class RedBenz extends Benz { public RedBenz(BenzBrand benzBrand) { super(benzBrand); } @Override public void showInfo() { super.showInfo(); System.out.println("紅色..."); } }
客戶端調用:
/** * @author tcy * @Date 05-08-2022 */ public class Client { public static void main(String[] args) { // 黑色奔馳E Benz benz1 = new BlackBenz(new BenzE()); benz1.showInfo(); // 黑色奔馳G Benz benz2 = new BlackBenz(new BenzG()); benz2.showInfo(); // 紅色奔馳E Benz benz3 = new RedBenz(new BenzE()); benz3.showInfo(); // 紅色奔馳G Benz benz4 = new RedBenz(new BenzG()); benz4.showInfo(); } }
【奔馳E】顏色是:黑色...
【奔馳G】顏色是:黑色...
【奔馳E】顏色是:紅色...
【奔馳G】顏色是:紅色...
這樣即使老王提出來新的顏色、新的車型,只需要增加相應的具體實現角色或者擴展抽象化角色即可。
顧名思義,橋接模式就像是一個橋,可以用來連接兩個不同地方,這兩個地方自由發(fā)展,中間的貿易是通過一座橋來連接。
這種方法的缺點也很顯著,汽車能很快的確立型號和顏色兩個維度,在實際業(yè)務開發(fā)中,識別出系統兩個獨立變化的維度就不簡單了。
不難看出,列舉的例子有些過于強求,在現實世界中是永遠不可能發(fā)生的,為了加深理解我找了大量在JDK亦或是Spirng等各種框架對橋接模式的應用,只找到了橋接模式在Jdbc中的應用。
三、應用
我們都知道通過JDBC可以完成Java對關系型數據庫的SQL操作,我們在連接數據數據庫時,想必都接觸過Driver,在連接MySQL和Oracle的Driver都是不同的,這些都是實現接口類。
我們看一下MySQL中實現的Driver類。
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
在該類中實際上有兩個作用,一是調用了DriverManager中的registerDriver方法來注冊驅動,二是當驅動注冊完成后,我們就會開始調用DriverManager中的getConnection方法了。
我們看DriverManager的完整代碼:
public class DriverManager { public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } return (getConnection(url, info, Reflection.getCallerClass())); } 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"); } } }
在Java中通過Connection提供給各個數據庫一樣的操作接口,這里的Connection可以看作抽象類。
可以說我們用來操作不同數據庫的方法都是相同的,不過MySQL有自己的ConnectionImpl類,同樣Oracle也有對應的實現類。
這里Driver和Connection之間是通過DriverManager類進行橋接的,這種橋接模式和我們上面可以清晰的看出來各個角色是不同的。
四、總結
橋接模式是很好理解的,相信認真看了實例的同學應該都可以看懂,但那并不代表你已經掌握了該設計模式。在我們使用JDBC的時候,想必有很多同學并不能看出來這是橋接模式。
紙上得來終覺淺,有一部分例子是為了說明橋接模式而“構想”出來的,各個角色都是清晰直觀??戳诉@樣的代碼,可以學會橋接模式,但是到了實際中很可能還是不會用。
最好的方法就是給出真實項目里的例子。但是這個難度確實很大,一到了真實項目里,就會遇到很多細節(jié)問題,從而影響對模式的理解,而且真實項目都帶有一定的業(yè)務環(huán)境。
看懂并且學會了設計模式是一回事,在實際開發(fā)中擇優(yōu)選擇設計模式那是另外一回事,這不僅需要對各個設計模式理解到位,更多的是對業(yè)務的理解和代碼理念的把控。
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。如果你想了解更多相關內容請查看下面相關鏈接
相關文章
springboot+redis 實現分布式限流令牌桶的示例代碼
這篇文章主要介紹了springboot+redis 實現分布式限流令牌桶 ,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04Spring Boot項目如何同時支持HTTP和HTTPS協議的實現
這篇文章主要介紹了Spring Boot項目如何同時支持HTTP和HTTPS協議的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10如何解決Gradle、Maven項目build后沒有mybatis的mapper.xml文件的問題
這篇文章主要介紹了如何解決Gradle、Maven項目build后沒有mybatis的mapper.xml文件的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01