结构型—桥接(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 }

  】

  那么,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、特点:

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

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

【结构型】Bridge模式

桥接模式是为了将对象的抽象与实现分离,使得它们可以独立变化.简简单单的一句话,却已经是站在了更高抽象层面上来看待.设计.解决问题.平常我们多是对具体问题进行分析.抽象,然后就开始设计,这对多数情况下基本完全够用,毕竟实际项目中的功能模块都是找一“最优解的"实现来解决掉问题,把功能设计出来即可.这种情况下的结构关系图参考如下: 这种抽象设计的缺点是:如果解决问题的方式不止一种,则必需为以上所有Concrete类都是实现对应的具体版本,这样不但类继承体系十分复杂,后期只要随便增加一种实现方式,都将要

Java 实现桥接(Bridge)模式

类图: /** * 手机壳.套 * @author stone * */ public abstract class BaseCellphoneShell { public abstract void mapping();//对应匹配哪种手机 } /** * 手机,桥接了 手机壳BaseCellphoneShell * @author stone */ public abstract class BaseCellphone { private BaseCellphoneShell shell;

C++设计模式实现--桥接(Bridge)模式

一. 举例 N年前: 计算机最先出来时,软件和硬件是一绑在一起的,比如IBM出了一台电脑,上面有一个定制的系统,假如叫 IBM_Win,这个IBM_Win系统当然不能在HP电脑上运行,同样HP出的HP_Win系统也不能运行在IBM上. 这时如果有一家新公司Apple出了一台新电脑,那么这个公司也要开发自己的系统 Apple_Win,Apple_Linux. 其实,上面几家公司开发的软件和硬件在很多地方都有相似之处,如果每家公司都做自己的系统,这意味着资源和人员的极大浪费. 这时,无论出现新的系统

设计模式(结构型)之组合模式(Composite Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(结构型)之桥接模式(Bridge Pattern)> http://blog.csdn.net/yanbober/article/details/45366781 概述 组合模式又叫做部分-整体模式,使我们在树型结构的问题中模糊简单元素和复杂元素的概念,客户程序可以像

设计模式(结构型)之外观模式(Facade Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(结构型)之装饰者模式(Decorator Pattern)>http://blog.csdn.net/yanbober/article/details/45395747 概述 一个客户类需要和多个业务类交互,而这些业务类经常会作为整体出现,由于涉及到的类比较多,导致使

设计模式(结构型)之代理模式(Proxy Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(结构型)之享元模式(Flyweight Pattern)>http://blog.csdn.net/yanbober/article/details/45477551 概述 代理模式是常用的结构型设计模式之一,当无法直接访问某个对象或访问某个对象存在困难时可以通过一个

设计模式之结构型桥接模式

在系统沿着多个维度变化的同时,又不增加其复杂度并已达到解耦. function changeColor(dom, color, bg) { // 设置元素的字体颜色 dom.style.color = color; // 设置元素的背景颜色 dom.style.background = bg; } var spans = document.getElementsByTagName('span'); spans[0].onmouseover = function() { changeColor(t

&quot;围观&quot;设计模式(15)--结构型之桥梁模式(Bridge Pattern)

桥接模式是软件设计模式中最复杂的模式之一,它把事物对象和其具体行为.具体特征分离开来,使它们可以各自独立的变化.事物对象仅是一个抽象的概念.如"圆形"."三角形"归于抽象的"形状"之下,而"画圆"."画三角"归于实现行为的"画图"类之下,然后由"形状"调用"画图".----WIKIPEDIA 个人理解 桥梁模式的核心在于解耦,通过抽象化将具体的事物

设计模式--桥接(Bridge)模式

1.概述: 桥接模式:把抽象和行为分离开来,中间需要某个介质来连接抽象化和行为化.此模式的概述听起来非常像适配器模式,不要搞混了,虽然都是借用中间介质,但意义不同. 2.例子: 我们哪车子来打比方,比如:车有很多中类型(Bus,Jeep,taxi等),但每个车的引擎又不一样(有2000cc,2200cc)的:现在我们来造车,平常思维是根据车的配置来安装不同的引擎,而我们桥接模式的思维却是根据引擎来造车. 平常思维一: /** *造一个安装了引擎的车类接口 */ package com.msit.