详解Java设计模式之桥接模式

目录
  • 一、什么是桥接模式:
  • 二、UML结构图
  • 三、代码实现
  • 四、JDBC源码解析-桥接模式
    • 1、源码分析
    • 2、源码类
    • 3、对 JDBC 的观点

一、什么是桥接模式:

桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯,桥接模式的作用就是为被分离的抽象部分和实现部分搭桥。在现实生活中一个物品在搭配不同的配件时会产生不同的动作和结果,例如一辆赛车搭配的是硬胎或者是软胎就能够在干燥的马路上行驶,而如果要在下雨的路面行驶,就需要搭配雨胎了,这种根据行驶的路面不同,需要搭配不同的轮胎的变化的情况,我们从软件设计的角度来分析,就是一个系统由于自身的逻辑,会有两个或多个维度的变化,而为了应对这种变化,我们就可以使用桥接模式来进行系统的解耦。 桥接模式将一个系统的抽象部分和实现部分分离,使它们都可以独立地进行变化,对应到上面就是赛车的种类可以相对变化,轮胎的种类可以相对变化,形成一种交叉的关系,最后的结果就是一种赛车对应一种轮胎就能够成功产生一种结果和行为。

桥接模式将系统的抽象部分与实现部分分离解耦,使他们可以独立的变化。为了达到让抽象部分和实现部分独立变化的目的,桥接模式使用组合关系来代替继承关系,抽象部分拥有实现部分的接口对象,从而能够通过这个接口对象来调用具体实现部分的功能。也就是说,桥接模式中的桥接是一个单方向的关系,只能够抽象部分去使用实现部分的对象,而不能反过来。

桥接模式符合“开闭原则”,提高了系统的可拓展性,在两个变化维度中任意扩展一个维度,都不需要修改原来的系统;并且实现细节对客户不透明,可以隐藏实现细节。但是由于聚合关系建立在抽象层,要求开发者针对抽象进行编程,这增加系统的理解和设计难度。

所以,桥接模式一般适用于以下几种应用场景:

(1) 系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,则可以通过桥接模式使他们在抽象层建立一个关联关系;

(2) 系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时

(3) 一个类存在两个独立变化的维度,而这两个维度都需要进行扩展。

二、UML结构图

抽象化角色 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 接口,精简类的层次,但是我建议还是需要抽象出实现部分的接口。

四、JDBC源码解析-桥接模式

Java 中,我们使用 JDBC 连接数据库时,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是使用了桥接模式,JDBC 提供统一接口,每种类型的数据库提供各自的实现,然后由桥接类创建一个连接数据库的驱动,使用某一个数据库的时候只需要切换一下就行。接下来我们就对 JDBC 的源码做下剖析:

通过原生JDBC API连接MySQL数据库,则有如下示例代码:

Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://<host>:<port>/<database>");

短短两行代码难以看出桥接模式的结构,下面先对源码进行一定的分析,理解各个类和接口之间的关系:

1、源码分析

(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 接口实现关系

2、源码类

对 Driver 和 Connection 进行抽象

桥接模式通过聚合关系代替继承关系,实现抽象化和实现化部分的解耦。以上述 JDBC 在 MySQL 中的简略类图为例,抽象化部分有 DriverManager,实现化部分有 Driver 接口和 Connection 接口。对于不同的数据库,Driver接口和Connection接口都有自己独特的实现类。

但是,和 Driver 接口不同的是,Connection 接口与 DriverManager 类的关系只是联系较弱的依赖关系,并不符合桥接模式的定义和特点。

桥接模式中的实现化角色 (Implementor) 对应上图的 Driver 接口,具体实现化 (Concrete Implementor) 角色对应 MysqlDriver、OracleDriver 和 MariadbDriver,扩展抽象化 (Refined Abstraction) 角色对应 DriverManager,不具有抽象化 (Abstraction) 角色作为扩展抽象化角色的父类

3、对 JDBC 的观点

(1)观点:JDBC采用的是策略模式而不是桥接模式

jdbc是桥接模式还是策略模式?

因为这确实和策略模式十分相似,如果把桥接模式的抽象部分简化来看,不去设计Abstraction,也就是用 Refined Abstraction 代替 Abstraction,那么就类似于策略模式的 Context 来使用接口的对象。

但是,桥接模式和策略模式的目的是不一样的,策略模式属于对象行为模式(描述对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责),它的目的是封装一系列的算法,使得算法可以相互替代,并在程序运行的不同时刻选择合适的算法。而桥接模式属于对象结构模式(描述如何将对象按某种布局组成更大的结构),它的目的是将抽象与实现分离,使它们可以独立变化

因此,从设计的目的来看,JDBC采用的并不是策略模式,在一段程序中数据库驱动并不存在频繁地相互替换

以上就是详解Java设计模式之桥接模式的详细内容,更多关于Java桥接模式的资料请关注我们其它相关文章!

时间: 2022-06-20

java设计模式--桥接模式详解

目录 引例 桥接模式 实战示例 代码: 总结 引例 需求:对不同手机类型的不同品牌(比如按键手机:诺基亚.翻盖手机:纽曼.智能手机:华为.小米)实现操作编程(比如: 开机.关机.打电话). 先来说说一般解法:将不同手机类型继承父类手机,最后各个品牌再继承对应手机类型: 弊端:乍一看没问题,但其实不易扩展(类爆炸),如果增加新的手机类型(比如新兴的折叠式),就需要增加各个手机品牌的类去继承(比如已继承智能手机的华为小米).同样如果我们增加一个手机品牌,也要在各个手机样式类下增加.违反了单一职责原则

Java设计模式之桥接模式实例详解

本文实例讲述了Java设计模式之桥接模式.分享给大家供大家参考,具体如下: 概念: 桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化. 桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量. 什么情况下会用桥接模式? 简单的说就是我们在抽象对象的特征时,对象的特征属性又很抽象,不得不把属性再次抽象. 否则的话,具体子类的数量将会成几何增长,而且不易扩展.没办法维护现有代码. 举例,我们在抽象手机这二个对象时,它的几个属性,如

Java设计模式之桥接模式的实现

桥接模式 桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立地变化.它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式. 上图一个类被三个类继承,使我们的程序扩展性,可维护性低,违反了单一职责原则. 具体代码实现如下: 1.创建品牌接口 package com.jialidun.gof.birdge; //品牌 public interface Brand { void info(); } 2.创建计算机的抽象类 package com

Java设计模式之桥接模式的示例详解

目录 定义 案例 需求 方案一 方案二 对比分析 总结 定义 桥梁模式是对象的结构模式.又称为柄体(Handle and Body)模式或接口(Interface)模式.桥梁模式的用意是“将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化”. 案例 需求 通过企业微信和qq的方式给员工发送消息 方案一 定义发送消息的接口 /** * 发送消息的接口 * @author:liyajie * @createTime:2022/2/21 10:33

Java设计模式之java桥接模式详解

目录 一.什么是桥接模式: 二.UML结构图: 三.代码实现: 四.JDBC源码解析-桥接模式: 1.源码分析: 2.源码类图: 3.对 JDBC 的观点: 参考博客: 总结 一.什么是桥接模式: 桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯,桥接模式的作用就是为被分离的抽象部分和实现部分搭桥.在现实生活中一个物品在搭配不同的配件时会产生不同的动作和结果,例如一辆赛车搭配的是硬胎或者是软胎就能够在干燥的马路上行驶,而如果要在下雨的路面行驶,就需要搭配雨胎了,这种根据行驶的路面不

深入理解Java设计模式之桥接模式

目录 二.桥接模式的结构 三.桥接模式的使用场景 四.桥接模式的优缺点 五.装饰,桥接和适配器模式的异同 适配器模式: 桥接模式: 装饰器模式: 六.桥接模式的实现 七.总结 一.什么是桥接模式 桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化.它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式. 二.桥接模式的结构 在桥接模式结构图中包含如下几个角色: Abstraction(抽象类):用于定义

java设计模式之桥接模式(Bridge)

概述 桥接模式一种结构型模式,它主要应对的是:由于实际的需要,某个类具有两个或以上的维度变化,如果只是用继承将无法实现这种需要,或者使得设计变得相当臃肿. 桥接模式的做法是把变化的部分抽象出来,使变化部分与主类分离开来,从而将多个维度的变化彻底分离.最后,提供一个管理类来组合不同维度上的变化,通过这种组合来满足业务的需要. UML结构图 代码示例 package interview; interface Implementor{ void operationImpl(); } abstract

理解java设计模式之建造者模式

建造者模式(Builder Pattern)主要用于"分步骤构建一个复杂的对象",在这其中"分步骤"是一个稳定的算法,而复杂对象的各个部分则经常变化.因此, 建造者模式主要用来解决"对象部分"的需求变化. 这样可以对对象构造的过程进行更加精细的控制. package com.shejimoshi.create.Builder; /** * 功能:意图是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 * 适用性: * 当创

快速理解Java设计模式中的组合模式

组合模式是一种常见的设计模式(但我感觉有点复杂)也叫合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系. 个人理解:组合模式就是将部分组装成整体. 定义如下: 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. 通用类图如下: 组合模式的包含角色: ● Component 抽象构件角色 定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性. ● Leaf 叶子构件 叶子对象,其下再也没有其他的分支,也就是遍历的最

Java设计模式之建造者模式实例详解

本文实例讲述了Java设计模式之建造者模式.分享给大家供大家参考,具体如下: 建造者模式(builder)可以将部件和其组装过程分开.一步一步创建一个复杂的对象. 用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节. 什么情况下会用到建造者模式? 个人理解,当我们创建的对象在创建时需要逻辑的时候. 比如,一个简单的pojo对象,我们想要创建,直接就可以new出来,没有什么逻辑. 当有一个复杂的对象,你想要创建它时,它的各个属性之间是有逻辑关系的. 一个属性赋值取值依赖于

Java设计模式之代理模式与装饰模式实例详解

本文实例讲述了Java设计模式之代理模式与装饰模式.分享给大家供大家参考,具体如下: 之所以把这两种模式放在一起说,是因为我发现这了两种模式几乎一模一样! 从网上也搜了一些资料,发现两者还是有一些区别的.我们在学习的同时也把这种困惑搞清楚. 定义: 代理模式,为其他对象提供一种代理以控制对这个对象的访问. 装饰模式,动态地给一个对象添加一些额外的职责. 代理模式,很好理解,就是把一个对象再次封装,以后就对封装的对象访问就可以了. 因为代理对象已经取代了被代理对象. 装饰模式,给一个对象增加功能,

Java设计模式之代理模式原理及实现代码分享

简介 Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术.生活中的方方面面都可以虚拟到代码中.代理模式所讲的就是现实生活中的这么一个概念:中介. 代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用. 代理模式包含如下角色: ISubject:抽象主题角色,是一个接口.该接口是对象和它的代理共用的接口. RealSubject:真实主题角色,是实现抽象主题接口的类. Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实

Java设计模式之策略模式原理与用法实例详解

本文实例讲述了Java设计模式之策略模式原理与用法.分享给大家供大家参考,具体如下: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化.其中JDK里面的TreeSet类和TreeMap类就用到了策略模式.这两个类是带排序的集合类,其中排序的规则就相当于策略模式里定义的一系列算法,而集合类就相当于是策略模式里的环境类,供用户使用,用只知道TreeSet和TreeMap是带排序的,至于怎么排序的,是由排序的算法决定的. 策略模式

浅谈JAVA设计模式之代理模式

代理模式 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能.这种类型的设计模式属于结构型模式. 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口. 介绍 意图: 为其他对象提供一种代理以控制对这个对象的访问. 主要解决: 在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上.在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时

Java设计模式之备忘录模式_动力节点Java学院

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和