結構型—橋接(Bridge)模式

一、意圖:java

  將抽象部分(抽象接口)與它的實現部分(代碼實現)分離,使它們均可以獨立地變化。mysql

  理解:抽象部分是對外展示的接口(api),而實現部分是針對抽象接口提供的不一樣版本的功能實現,使二者獨立變化指二者能夠在各自的維度上自由變化,而不會產生太大的影響。如能夠在api中添加新的接口,而不影響具體實現部分;能夠在實現部分針對特定接口添加新的實現方式,而不影響抽象接口定義。sql

  橋接模式將類的繼承關係轉變爲類的聚合關係(見下圖)。對於抽象接口及其實現,Java中一般的實現方式是經過抽象類的繼承或接口的實現,可是橋接模式,將這種抽象接口與實現之間的關係變成了聚合關係。數據庫

二、橋接模式(也稱:橋樑模式)類圖api

  

角色:app

  • Abstraction:定義抽象接口,對外提供api;自身維護一個Implementor(橋)的引用,使用Implementor的操做來完成自身接口功能,如operation方法經過impl.doOperation()來實現;
  • RefinedAbstraction:擴展Abstraction的抽象接口;
  • Implementor:架在抽象接口與不一樣實現方式之間的橋,且自身的接口沒必要與Abstraction徹底相同;
  • ConcreteImplementorA:實現Implementor(橋)的接口,針對Implementor(橋)中的接口doOperation提供特點化的實現;
  • Client:用戶類。

協做:ide

  • Client調用Abstraction提供的接口,而Abstraction將Client的調用經過Implementor來實現。

三、JDBC與橋接:ui

JDBC是橋接模式的典型實現。url

先看下類圖:spa

  

  一般使用JDBC鏈接數據庫時,會使用以下代碼:

1     Class.forName("數據庫類驅動器");
2     Connection conn = DriverManager.getConnection("數據庫url", "用戶名", "密碼");
3     //.................

  針對不一樣的數據庫,JDBC均可以經過java.sql.DriverManager類的靜態方法getConnection(數據庫url, 用戶名, 密碼)來獲取數據庫的鏈接。JDBC經過DriverManager對外提供了操做數據庫的統一接口getConnection,經過該方法能夠獲取不一樣數據庫的鏈接,而且經過Connection類提供的接口來進行數據的查詢操做。

  JDBC爲不一樣的數據庫操做提供了相同的接口,可是JDBC自己並無針對每種數據庫提供一套具體實現代碼,而是經過接口java.sql.Driver的connect方法鏈接到了不一樣的數據庫實現。

1 public interface Driver
2 {
3 
4     public abstract Connection connect(String s, Properties properties) throws SQLException;
5     //其餘方法
6 
7 }

  在JDBC中,針對不一樣數據庫提供的統一的操做接口經過java.sql.Driver(橋)鏈接到了不一樣的數據庫實現。如鏈接mysql數據庫。

 1 package com.mysql.jdbc;
 2 
 3 public class NonRegisteringDriver implements java.sql.Driver //對java.sql.Driver接口提供了實現
 4 {
 5 
 6 public Connection connect(String url, Properties info)
 7         throws SQLException
 8     {
 9         //實現
10     }
11 
12     //其餘方法
13 }

  Java在鏈接MySQL時須要使用mysql-connector-java.jar,mysql-connector-java.jar包提供了對MySQL數據庫操做的具體實現,並經過接口Driver鏈接到了JDBC統一的api。

四、JDBC中橋接模式具體如何實現?

既然,針對不一樣的數據庫,經過DriverManger.getConnection()能夠得到相同的Connection接口,那先看DriverManager的源碼:

 1 public class DriverManager
 2 {
 3     private static final CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList(); //存放DriverInfo的鏈表
 4 
 5     public static synchronized void registerDriver(Driver driver)
 6         throws SQLException
 7     {
 8         if(driver != null)
 9             registeredDrivers.addIfAbsent(new DriverInfo(driver)); //向鏈表中添加DriverInfo實例,DriverInfo封裝了Driver
10         else
11             throw new NullPointerException();
12         println((new StringBuilder()).append("registerDriver: ").append(driver).toString());
13     }
14 
15     private static Connection getConnection(String s, Properties properties, Class class1)
16         throws SQLException
17     {
18         //.....
19         Iterator iterator = registeredDrivers.iterator(); //遍歷registeredDrivers表
20         do
21         {
22             if(!iterator.hasNext())
23                 break;
24             DriverInfo driverinfo = (DriverInfo)iterator.next();
25             if(isDriverAllowed(driverinfo.driver, classloader))
26                 try
27                 {
28                     Connection connection = driverinfo.driver.connect(s, properties); //調用Driver接口提供的connect方法來獲取Connection對象
29                     if(connection != null)
30                     {
31                         return connection;
32                     }
33                 }
34                 catch(SQLException sqlexception1)
35                 {
36                     if(sqlexception == null)
37                         sqlexception = sqlexception1;
38                 }
39         } while(true);
40     }
41 
42     //其餘方法
43 
44 }    
45 
46     

  從DriverManager.getConnection()源碼可見,方法中遍歷了包含DriverInfo實例的表registeredDrivers,經過表中實例driverinfo來獲取封裝的java.sql.Driver類型的實例,並調用java.sql.Driver接口的connect方法獲取到Connection。

  【注:DriverInfo是Driver的封裝類。由DriverInfo源碼可見。

 1 class DriverInfo
 2 {
 3 
 4     DriverInfo(Driver driver1)
 5     {
 6         driver = driver1;
 7     }
 8 
 9     public boolean equals(Object obj)
10     {
11         return (obj instanceof DriverInfo) && driver == ((DriverInfo)obj).driver;
12     }
13 
14     public int hashCode()
15     {
16         return driver.hashCode();
17     }
18 
19     public String toString()
20     {
21         return (new StringBuilder()).append("driver[className=").append(driver).append("]").toString();
22     }
23 
24     final Driver driver;
25 }
View Code

  】

  那麼,Driver實例是什麼時候注入到DriverManager類的registeredDrivers中的呢?以mysql爲例,在每次使用JDBC鏈接mysql時,都會有下面的調用:

1 Class.forName("com.mysql.jdbc.Driver");

  該行代碼經過反射加載了com.mysql.jdbc.Driver類(com.mysql.jdbc.Driver類在mysql-connector-java.jar中,而mysql-connector-java.jar是JDBC鏈接MySQL的jar包),查看com.mysql.jdbc.Driver類:

 1 package com.mysql.jdbc;
 2 
 3 public class Driver extends NonRegisteringDriver
 4     implements java.sql.Driver
 5 {
 6 
 7     public Driver()
 8         throws SQLException
 9     {
10     }
11 
12     static 
13     {
14         try
15         {
16             DriverManager.registerDriver(new Driver()); 17         }
18         catch(SQLException E)
19         {
20             throw new RuntimeException("Can't register driver!");
21         }
22     }
23 }

  在com.mysql.jdbc.Driver的源碼中能夠看到在加載com.mysql.jdbc.Driver類時,經過類中的靜態域中的紅色代碼,會調用DriverManager的registerDriver方法將當前MySQL的驅動類實例注入到DriverManager的registeredDrivers中。

  經過整個代碼調用,展現了橋接模式在JDBC中是如何運用的。

五、適用性:

  • 當不但願抽象接口和實現部分採用固定的綁定關係時;

六、特色:

  • 橋接模式良好地實現了開閉原則:一般應用橋接模式的地方,抽象接口和具體實現部分都是能夠變化的,且抽象接口與實現耦合度低;
  • 橋接模式將類繼承關係轉換成了對象組合關係,實現了類的複用,減小了類的個數。
相關文章
相關標籤/搜索