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连接数据库时,会使用如下代码:

     Class.forName("数据库类驱动器");
Connection conn = DriverManager.getConnection("数据库url", "用户名", "密码");
//.................

  针对不同的数据库,JDBC都可以通过java.sql.DriverManager类的静态方法getConnection(数据库url, 用户名, 密码)来获取数据库的连接。JDBC通过DriverManager对外提供了操作数据库的统一接口getConnection,通过该方法可以获取不同数据库的连接,并且通过Connection类提供的接口来进行数据的查询操作。

  JDBC为不同的数据库操作提供了相同的接口,但是JDBC本身并没有针对每种数据库提供一套具体实现代码,而是通过接口java.sql.Driver的connect方法连接到了不同的数据库实现。

 public interface Driver
{ public abstract Connection connect(String s, Properties properties) throws SQLException;
//其他方法 }

  在JDBC中,针对不同数据库提供的统一的操作接口通过java.sql.Driver(桥)连接到了不同的数据库实现。如连接mysql数据库。

 package com.mysql.jdbc;

 public class NonRegisteringDriver implements java.sql.Driver //对java.sql.Driver接口提供了实现
{ public Connection connect(String url, Properties info)
throws SQLException
{
//实现
} //其他方法
}

  Java在连接MySQL时需要使用mysql-connector-java.jar,mysql-connector-java.jar包提供了对MySQL数据库操作的具体实现,并通过接口Driver连接到了JDBC统一的api。

4、JDBC中桥接模式具体如何实现?

既然,针对不同的数据库,通过DriverManger.getConnection()可以获得相同的Connection接口,那先看DriverManager的源码:

 public class DriverManager
{
private static final CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList(); //存放DriverInfo的链表 public static synchronized void registerDriver(Driver driver)
throws SQLException
{
if(driver != null)
registeredDrivers.addIfAbsent(new DriverInfo(driver)); //向链表中添加DriverInfo实例,DriverInfo封装了Driver
else
throw new NullPointerException();
println((new StringBuilder()).append("registerDriver: ").append(driver).toString());
} private static Connection getConnection(String s, Properties properties, Class class1)
throws SQLException
{
//.....
Iterator iterator = registeredDrivers.iterator(); //遍历registeredDrivers表
do
{
if(!iterator.hasNext())
break;
DriverInfo driverinfo = (DriverInfo)iterator.next();
if(isDriverAllowed(driverinfo.driver, classloader))
try
{
Connection connection = driverinfo.driver.connect(s, properties); //调用Driver接口提供的connect方法来获取Connection对象
if(connection != null)
{
return connection;
}
}
catch(SQLException sqlexception1)
{
if(sqlexception == null)
sqlexception = sqlexception1;
}
} while(true);
} //其他方法 }

  从DriverManager.getConnection()源码可见,方法中遍历了包含DriverInfo实例的表registeredDrivers,通过表中实例driverinfo来获取封装的java.sql.Driver类型的实例,并调用java.sql.Driver接口的connect方法获取到Connection。

  【注:DriverInfo是Driver的封装类。由DriverInfo源码可见。

 class DriverInfo
{ DriverInfo(Driver driver1)
{
driver = driver1;
} public boolean equals(Object obj)
{
return (obj instanceof DriverInfo) && driver == ((DriverInfo)obj).driver;
} public int hashCode()
{
return driver.hashCode();
} public String toString()
{
return (new StringBuilder()).append("driver[className=").append(driver).append("]").toString();
} final Driver driver;
}

  】

  那么,Driver实例是何时注入到DriverManager类的registeredDrivers中的呢?以mysql为例,在每次使用JDBC连接mysql时,都会有下面的调用:

 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类:

 package com.mysql.jdbc;

 public class Driver extends NonRegisteringDriver
implements java.sql.Driver
{ public Driver()
throws SQLException
{
} static
{
try
{
DriverManager.registerDriver(new Driver());
}
catch(SQLException E)
{
throw new RuntimeException("Can't register driver!");
}
}
}

  在com.mysql.jdbc.Driver的源码中可以看到在加载com.mysql.jdbc.Driver类时,通过类中的静态域中的红色代码,会调用DriverManager的registerDriver方法将当前MySQL的驱动类实例注入到DriverManager的registeredDrivers中。

  通过整个代码调用,展示了桥接模式在JDBC中是如何运用的。

5、适用性:

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

6、特点:

  • 桥接模式良好地实现了开闭原则:通常应用桥接模式的地方,抽象接口和具体实现部分都是可以变化的,且抽象接口与实现耦合度低;
  • 桥接模式将类继承关系转换成了对象组合关系,实现了类的复用,减少了类的个数。

结构型—桥接(Bridge)模式的更多相关文章

  1. 【结构型】Bridge模式

    桥接模式是为了将对象的抽象与实现分离,使得它们可以独立变化.简简单单的一句话,却已经是站在了更高抽象层面上来看待.设计.解决问题.平常我们多是对具体问题进行分析.抽象,然后就开始设计,这对多数情况下基 ...

  2. 设计模式初探-桥接(Bridge)模式

    桥接(Bridge)模式,又称Handle/Body模式,属于对象结构型模式.用于将抽象部分与它的实现部分分离,使它们都可以独立地变化.比如常见的电脑窗口界面,不同的操作系统其窗口界面绘制的原理肯定不 ...

  3. Java 实现桥接(Bridge)模式

    类图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamp3d21scDQ1Ng==/font/5a6L5L2T/fontsize/400/fill/I0 ...

  4. 【结构型】Flyweight模式

    享元模式的主要目的.意图是为对象的大量使用提供一种共享机制.该模式的思想重在复用.共享复用.像文字.列表中的格子等这类多数都是需要考虑复用技术,否则必将大量耗费内存空间而使资源以及性能等大量耗费.该模 ...

  5. python设计模式---结构型之代理模式

    主要想着nginx:) from abc import ABCMeta, abstractmethod # 结构型设计模式---代理模式 class Actor: def __init__(self) ...

  6. 【设计模式】结构型03外观模式(Facade Pattern)

    [设计模式]结构型02装饰模式(Decorator Pattern) 意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 主要解决:降低访问 ...

  7. 设计模式C++描述----09.桥接(Bridge)模式

    一. 举例 N年前: 计算机最先出来时,软件和硬件是一绑在一起的,比如IBM出了一台电脑,上面有一个定制的系统,假如叫 IBM_Win,这个IBM_Win系统当然不能在HP电脑上运行,同样HP出的HP ...

  8. JAVA设计模式 5【结构型】代理模式的理解与使用

    今天要开始我们结构型 设计模式的学习,设计模式源于生活,还是希望能通过生活中的一些小栗子去理解学习它,而不是为了学习而学习这些东西. 结构型设计模式 结构型设计模式又分为 类 结构型 对象 结构型 前 ...

  9. 结构型---桥接模式(Bridge Pattern)

    定义 桥接模式即将抽象部分与实现部分脱耦,使它们可以独立变化.桥接模式的目的就是使两者分离,根据面向对象的封装变化的原则,我们可以把实现部分的变化封装到另外一个类中,这样的一个思路也就是桥接模式的实现 ...

随机推荐

  1. Mysql 的安装与配置

    MySQL的安装 第1步:下载 第2 步:以管理员身份进行安装 第3步:选择安装类型. 第4步:设置MySQL安装目录,及数据库文件目录 第5步:安装结束,开启配置向导 第6步:选择配置类型 第7步: ...

  2. CSDN 自动评论

    转载说明 本篇文章可能已经更新,最新文章请转:http://www.sollyu.com/csdn-auto-reviews/ 说明 当打开http://download.csdn.net/my/do ...

  3. Qt5.4生成安装包过程

    所需工具: 1.  HM NIS Edit 2.  windeployqt.exe 第一个工具需要自己去网上下载,第二个工具可以在qt安装目录下找到:D:\qtopengl\5.4\mingw491_ ...

  4. [Linux]学习笔记(4)-su及passwd的用法介绍

    (1)su su命令用于将当前的用户切换到一个指定的用户.语法为: su - user_name 如果用户利用telnet方式远程登录,是不能直接以root账户登录的,也就是说在使用telnet登录服 ...

  5. php入门之表单创建和基本处理

    为了方便后面学习数组,这里引入了过渡章节就是表单,至于为什么,等真的学习到数组的时候你就会发现它的妙处拉. ============================================== ...

  6. RTC搭建android下三层应用程序访问服务器MsSql-服务器端

    前几天通过Ro搭建webservice,然后在android下调用,虽然已近成功,但是返回的数据库里的中文有乱码一直未得到解决!rtc6.23版本,已经支持xe5,也支持fmx的android下开发, ...

  7. 转 在无法通过yum下载非标准包时,怎么办

    在CentOS下,我们可以通过yum来下载或更新rpm包,但是标准的源(repository)里只提供一部分的rpm包,虽然大部分情况下,这些包是够用的.但是有时候还是需要下载其他的一些非标准的包,如 ...

  8. 在win7上建立本地FTP站点详细步骤

    一.安装FTP组件点击:控制面板—>程序和功能—>打开或关闭Windows功能. 勾选“FTP服务器”及“FTP服务”“FTP扩展性”,点击“确定”,安装FTP组件. 勾选Web管理工具的 ...

  9. gulp前端自动化构建工具入门篇

    现在我们通过这3个问题来学习一下: 1.什么是gulp? 2.为什么要用gulp? 3.怎么用?   什么是gulp 答:是一个前端自动化的构建工具,直白点说,如果没有这个工具,我们利用人工依旧可以做 ...

  10. Xubuntu 安装mentohust

    对于路由器上网到用户来说,自动分配IP上网。 对于校园网用户,首先下载mentohust_0.3.4-1_i386.deb,双击安装程序 然后在命令窗口中输入sudo -s 密码:user来获得roo ...