您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java設計模式之橋接模式怎么實現”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Java設計模式之橋接模式怎么實現”文章能幫助大家解決問題。
橋接,顧名思義,就是用來連接兩個部分,使得兩個部分可以互相通訊,橋接模式的作用就是為被分離的抽象部分和實現部分搭橋。在現實生活中一個物品在搭配不同的配件時會產生不同的動作和結果,例如一輛賽車搭配的是硬胎或者是軟胎就能夠在干燥的馬路上行駛,而如果要在下雨的路面行駛,就需要搭配雨胎了,這種根據行駛的路面不同,需要搭配不同的輪胎的變化的情況,我們從軟件設計的角度來分析,就是一個系統由于自身的邏輯,會有兩個或多個維度的變化,而為了應對這種變化,我們就可以使用橋接模式來進行系統的解耦。 橋接模式將一個系統的抽象部分和實現部分分離,使它們都可以獨立地進行變化,對應到上面就是賽車的種類可以相對變化,輪胎的種類可以相對變化,形成一種交叉的關系,最后的結果就是一種賽車對應一種輪胎就能夠成功產生一種結果和行為。
橋接模式將系統的抽象部分與實現部分分離解耦,使他們可以獨立的變化。為了達到讓抽象部分和實現部分獨立變化的目的,橋接模式使用組合關系來代替繼承關系,抽象部分擁有實現部分的接口對象,從而能夠通過這個接口對象來調用具體實現部分的功能。也就是說,橋接模式中的橋接是一個單方向的關系,只能夠抽象部分去使用實現部分的對象,而不能反過來。
橋接模式符合“開閉原則”,提高了系統的可拓展性,在兩個變化維度中任意擴展一個維度,都不需要修改原來的系統;并且實現細節對客戶不透明,可以隱藏實現細節。但是由于聚合關系建立在抽象層,要求開發者針對抽象進行編程,這增加系統的理解和設計難度。
所以,橋接模式一般適用于以下幾種應用場景:
(1) 系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,避免在兩個層次之間建立靜態的繼承聯系,則可以通過橋接模式使他們在抽象層建立一個關聯關系;
(2) 系統不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加時
(3) 一個類存在兩個獨立變化的維度,而這兩個維度都需要進行擴展。
抽象化角色 Abstraction:定義抽象的接口,包含一個對實現化角色的引用,抽象角色的方法需要調用實現化角色;擴展抽象化角色 RefinedAbstraction:抽象化角色的子類,一般對抽象部分的方法進行完善和擴展,實現父類中的業務方法,并通過組合/聚合關系調用實現化角色中的業務方法實現化角色 Implementor:定義具體行為、具體特征的應用接口,供擴展抽象化角色使用,一般情況下是由實現化角色提供基本的操作,而抽象化角色定義基于實現部分基本操作的業務方法;具體實現化角色 ConcreteImplementor:完善實現化角色中定義的具體邏輯。
Implementor 接口類:
public interface Implementor { void operationImpl(); }
ConcreteImplementor 接口實現類:
public class ConcreteImplementorA implements Implementor{ @Override public void operationImpl() { //具體實現 } } public class ConcreteImplementorB implements Implementor{ @Override public void operationImpl() { //具體實現 } }
Abstraction 抽象類:
public abstract class Abstraction { private Implementor implementor; public Abstraction(Implementor implementor) { this.implementor = implementor; } public void operation() { implementor.operationImpl(); } }
RefinedAbstraction 抽象類的具體實現:
public class RefinedAbstraction extends Abstraction{ public RefinedAbstraction(Implementor implementor) { super(implementor); } public void refinedOperation() { //對 Abstraction 中的 operation 方法進行擴展 } }
看了這段通用代碼之后,橋接模式的結構應該就很清楚了,需要注意的是 RefinedAbstraction 根據實際情況是可以有多個的。 當然上面的 UML 類圖和通用代碼只是最常用的實現方式而已,在實際使用中可能會有其他的情況,比如 Implementor 只有一個類的情況,雖然這時候可以不去創建 Implementor 接口,精簡類的層次,但是我建議還是需要抽象出實現部分的接口。
Java 中,我們使用 JDBC 連接數據庫時,在各個數據庫之間進行切換,基本不需要動太多的代碼,原因就是使用了橋接模式,JDBC 提供統一接口,每種類型的數據庫提供各自的實現,然后由橋接類創建一個連接數據庫的驅動,使用某一個數據庫的時候只需要切換一下就行。接下來我們就對 JDBC 的源碼做下剖析:
通過原生JDBC API連接MySQL數據庫,則有如下示例代碼:
Class.forName("com.mysql.cj.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://<host>:<port>/<database>");
短短兩行代碼難以看出橋接模式的結構,下面先對源碼進行一定的分析,理解各個類和接口之間的關系:
(1)Class.forName() 方法:
該方法將返回與給定字符串名的類或接口相關聯的 java.lang.Class 類對象,用于在程序運行時動態加載該類或該接口到當前線程中,如果 Class.forName() 加載的是一個類,也會執行類中包含的static { } 靜態代碼段
(2)com.mysql.cj.jdbc.Driver 類:
MySQL 將具體的 java.sql.Driver 接口的實現放到了 NonRegisteringDriver
中,com.mysql.cj.jdbc.Driver
類僅包含一段靜態代碼
其中最關鍵的是靜態代碼段中的 DriverManager.registerDriver(new Driver())
,它會在客戶端調用Class.forName()
方法加載 com.mysql.cj.jdbc.Driver
類的同時被執行,Driver 類自身的一個實例被注冊到 DriverManager
(即保存到 DriverManager 的靜態字段 registeredDrivers 內),注冊過程的源碼如下:
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); }
registeredDrivers
靜態字段的類型是實現了 List 接口的 CopyOnWriteArrayList
類,它能夠保存進一步封裝 java.sql.Driver
接口的 DriverInfo 類實例,DriverInfo 類的聲明代碼如下:
class DriverInfo { final Driver driver; DriverAction da; DriverInfo(Driver driver, DriverAction action) { this.driver = driver; da = action; } // …… }
DriverInfo 還包裝了 DriverAction,DriverAction 會在Driver被取消注冊時被調用,在 MySQL 的 Driver 在向 DriverManager 進行注冊時,DriverAction 被設置為 null
(3)DriverManager 類:
由上面的分析可得,Class.forName() 方法調用后,com.mysql.cj.jdbc.Driver 類被加載,并執行static { } 靜態代碼段,將 com.mysql.cj.jdbc.Driver 類實例注冊到 DriverManager 中。然后,客戶端會調用 DriverManager.getConnection() 方法獲取一個 Connection 數據庫連接實例,該方法的部分源碼如下:
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException { // …… 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()); } } // …… }
DriverManager.getConnection() 方法會遍歷 registeredDrivers 靜態字段,獲取字段內保存的每一個 Driver 來嘗試響應客戶端的數據庫連接請求,若所有 Driver 都連接數據庫失敗,則提示連接失敗信息
(4)Connection接口:
Connection 代表和特定數據庫的連接會話,能夠執行SQL語句并在連接的上下文中返回執行結果。因此,DriverManager.getConnection() 方法返回的 Connection 數據庫連接實例根據不同的數據庫有不同的實現,MySQL 的 Connection 接口實現關系
對 Driver 和 Connection 進行抽象
橋接模式通過聚合關系代替繼承關系,實現抽象化和實現化部分的解耦。以上述 JDBC 在 MySQL 中的簡略類圖為例,抽象化部分有 DriverManager,實現化部分有 Driver 接口和 Connection 接口。對于不同的數據庫,Driver接口和Connection接口都有自己獨特的實現類。
但是,和 Driver 接口不同的是,Connection 接口與 DriverManager 類的關系只是聯系較弱的依賴關系,并不符合橋接模式的定義和特點。
橋接模式中的實現化角色 (Implementor) 對應上圖的 Driver 接口,具體實現化 (Concrete Implementor) 角色對應 MysqlDriver、OracleDriver 和 MariadbDriver,擴展抽象化 (Refined Abstraction) 角色對應 DriverManager,不具有抽象化 (Abstraction) 角色作為擴展抽象化角色的父類
(1)觀點:JDBC采用的是策略模式而不是橋接模式
jdbc是橋接模式還是策略模式?
因為這確實和策略模式十分相似,如果把橋接模式的抽象部分簡化來看,不去設計Abstraction,也就是用 Refined Abstraction 代替 Abstraction,那么就類似于策略模式的 Context 來使用接口的對象。
但是,橋接模式和策略模式的目的是不一樣的,策略模式屬于對象行為模式(描述對象之間怎樣相互協作共同完成單個對象都無法單獨完成的任務,以及怎樣分配職責),它的目的是封裝一系列的算法,使得算法可以相互替代,并在程序運行的不同時刻選擇合適的算法。而橋接模式屬于對象結構模式(描述如何將對象按某種布局組成更大的結構),它的目的是將抽象與實現分離,使它們可以獨立變化
因此,從設計的目的來看,JDBC采用的并不是策略模式,在一段程序中數據庫驅動并不存在頻繁地相互替換
關于“Java設計模式之橋接模式怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。