• 结构型—桥接(Bridge)模式


    1、意图:

      将抽象部分(抽象接口)与它的实现部分(代码实现)分离,使它们都可以独立地变化。

      理解:抽象部分是对外展现的接口(api),而实现部分是针对抽象接口提供的不同版本的功能实现,使两者独立变化指两者可以在各自的维度上自由变化,而不会产生太大的影响。如可以在api中添加新的接口,而不影响具体实现部分;可以在实现部分针对特定接口添加新的实现方式,而不影响抽象接口定义。

      桥接模式将类的继承关系转变为类的聚合关系(见下图)。对于抽象接口及其实现,Java中通常的实现方式是通过抽象类的继承或接口的实现,但是桥接模式,将这种抽象接口与实现之间的关系变成了聚合关系。

    2、桥接模式(也称:桥梁模式)类图

      

    角色:

    • Abstraction:定义抽象接口,对外提供api;自身维护一个Implementor(桥)的引用,使用Implementor的操作来完成自身接口功能,如operation方法通过impl.doOperation()来实现;
    • RefinedAbstraction:扩展Abstraction的抽象接口;
    • Implementor:架在抽象接口与不同实现方式之间的桥,且自身的接口不必与Abstraction完全相同;
    • ConcreteImplementorA:实现Implementor(桥)的接口,针对Implementor(桥)中的接口doOperation提供特色化的实现;
    • Client:用户类。

    协作:

    • Client调用Abstraction提供的接口,而Abstraction将Client的调用通过Implementor来实现。

    3、JDBC与桥接:

    JDBC是桥接模式的典型实现。

    先看下类图:

      

      通常使用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。

    4、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中是如何运用的。

    5、适用性:

    • 当不希望抽象接口和实现部分采用固定的绑定关系时;

    6、特点:

    • 桥接模式良好地实现了开闭原则:通常应用桥接模式的地方,抽象接口和具体实现部分都是可以变化的,且抽象接口与实现耦合度低;
    • 桥接模式将类继承关系转换成了对象组合关系,实现了类的复用,减少了类的个数。
  • 相关阅读:
    使用SSH密钥方式登录ubuntu Linux,指令(ssh-keygen 和 ssh-copy-id)
    显示、更改ubuntu linux主机名(计算机名)
    如何在VMware ubuntu linux虚拟机中安装VMware tools
    VMware bridge 桥接方式连接internet
    在STEP7 TIA PORTAL中,设置模块的地址和设备名(Device name)
    查看linux网卡硬件名称
    Ubuntu linux安装putty
    Linux修改文件permission可执行属性
    在windows中,使用SSH登录VMware ubuntu linux虚拟机
    单选,复选操作div,显示隐藏
  • 原文地址:https://www.cnblogs.com/-crazysnail/p/3977815.html
Copyright © 2020-2023  润新知