聊聊Java设计模式-桥接模式

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 桥接(Bridge)模式是指将抽象部分与实现部分相分离,使它们都可以独立的发生变化。

桥接(Bridge)模式是指将抽象部分与实现部分相分离,使它们都可以独立的发生变化。

一、桥接模式介绍

我们知道,抽象部分一般与实现部分连接有两种方式:继承和实现。那么如何将其解耦分离,桥接模式提供一种方式,也就是将强关联转为弱关联,将继承转换为组合关系。如下图所示,取消两者的继承关系,改用组合关系:

image-20220330181522305

1.1 桥接模式的结构

我们可以看看桥接模式是怎么解耦,利用组合连接抽象和实现部分,如下所示:

image-20220330204226055

其结构中包含如下角色:

  • Abstraction:抽象化角色,定义抽象类,包含一个对实现化对象的引用(组合)
  • RefinedAbstraction:扩展抽象化角色,实现抽象化角色的子类,由此通过组合关系调用实现化角色中的业务方法
  • Implementor:实现化角色的接口,供扩展抽象化角色调用
  • ImplementorA、ImplementorB:实现化角色的具体实现

1.2 桥接模式的实现

我们可以根据上面的UML图实现对应的代码:

//客户端类
public class Client {
   
   
    public static void main(String[] args) {
   
   
        Implementor imple = new ImplementorA();
        Abstraction abs = new RefinedAbstraction(imple);
        abs.Operation();
    }
}
//实现化角色
interface Implementor {
   
   
    public void OperationImpl();
}
//具体的实现化角色
class ImplementorA implements Implementor {
   
   
    public void OperationImpl() {
   
   
        System.out.println("我是具体实现化角色A");
    }
}
class ImplementorB implements Implementor {
   
   
    public void OperationImpl() {
   
   
        System.out.println("我是具体实现化角色B");
    }
}
//抽象化角色
abstract class Abstraction {
   
   
    protected Implementor imple;

    protected Abstraction(Implementor imple) {
   
   
        this.imple = imple;
    }

    public abstract void Operation();
}
//扩展抽象化角色
class RefinedAbstraction extends Abstraction {
   
   
    protected RefinedAbstraction(Implementor imple) {
   
   
        super(imple);
    }

    public void Operation() {
   
   
        System.out.println("扩展抽象化角色被访问");
        imple.OperationImpl();
    }
}

实现结果:

扩展抽象化角色被访问
我是具体实现化角色A

二、桥接模式的应用场景

2.1 JDBC 驱动器

JDBC为所有的关系型数据库提供一个通用的标准,这就是一个桥接模式的典型应用。我们先回顾一下JDBC的使用,用JDBC连接MySQL数据库主要分为这样几步:

//1.加载MySQL驱动注入到DriverManager
Class.forName("com.mysql.cj.jdbc.Driver");
//2.提供JDBC连接的URL、用户名和密码
String url = "jdbc:mysql://localhost:3306/test_db?";
String username = "root";
String password = "root";
//3.创建数据库的连接
Connection connection = DriverManager.getConnection(url, username, password);
//4.创建statement实例
Statement statement = connection.createStatement();
//5.执行SQL语句
String query = "select * from test";  //查询语句,也可以换成CRUD的其他语句
ResultSet resultSet = statement.executeQuery(query);
//6.关闭连接对象
connection.close();

我们一步步来看,先看步骤1:

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

查看对应的 com.mysql.cj.jdbc.Driver路径下的源码:

package com.mysql.cj.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
   
   
    public Driver() throws SQLException {
   
   
    }

    static {
   
   
        try {
   
   
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
   
   
            throw new RuntimeException("Can't register driver!");
        }
    }
}

是通过静态方法调用registerDriver()方法来将MySQL驱动注入到DriverManagerregisterDriver()方法具体如下:

public static synchronized void registerDriver(java.sql.Driver driver)
    throws SQLException {
   
   
    //直接调用下面的同名静态方法
    registerDriver(driver, null);
}

public static synchronized void registerDriver(java.sql.Driver driver,DriverAction da)throws SQLException {
   
   
    /* registeredDrivers是一个list,用DriverInfo实例封装Driver */
    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:

public class DriverManager {
   
   
    // List of registered JDBC drivers
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    //...
}

DriverInfo类中封装了java.sql.Driver接口:

class DriverInfo {
   
   

    final Driver driver;
    DriverAction da;
    DriverInfo(Driver driver, DriverAction action) {
   
   
        this.driver = driver;
        da = action;
    }
    //...
}

再看步骤2、3,重点是步骤3

Connection connection = DriverManager.getConnection(url, username, password);

Connection接口是和特定数据库的连接会话,不同的数据库的连接会话都不相同:

public interface Connection  extends Wrapper, AutoCloseable {
   
   

    Statement createStatement() throws SQLException;
    //...
}

是通过DriverManager中的getConnection方法,从registeredDrivers进行选择对应数据库驱动下的连接实例:

public static Connection getConnection(String url,String user, String password) throws SQLException {
   
   
    java.util.Properties info = new java.util.Properties();

    if (user != null) {
   
   
        info.put("user", user);
    }
    if (password != null) {
   
   
        info.put("password", password);
    }

    return (getConnection(url, info, Reflection.getCallerClass()));
}
// 实际上调用的是下面的静态方法getConnection
//  Worker method called by the public getConnection() methods.
private static Connection getConnection(
    String url, java.util.Properties info, Class<?> caller) throws SQLException {
   
   
    /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
    ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    synchronized(DriverManager.class) {
   
   
        // synchronize loading of the correct classloader.
        if (callerCL == null) {
   
   
            callerCL = Thread.currentThread().getContextClassLoader();
        }
    }

    if(url == null) {
   
   
        throw new SQLException("The url cannot be null", "08001");
    }

    println("DriverManager.getConnection(\"" + url + "\")");

    // Walk through the loaded registeredDrivers attempting to make a connection.
    // Remember the first exception that gets raised so we can reraise it.
    SQLException reason = null;

    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());
        }
    }

    // if we got here nobody could connect.
    if (reason != null)    {
   
   
        println("getConnection failed: " + reason);
        throw reason;
    }

    println("getConnection: no suitable driver found for "+ url);
    throw new SQLException("No suitable driver found for "+ url, "08001");
}

Connection接口的具体实现部分,MySQL的连接是通过两层实现完成抽象部分的实现:

public class ConnectionImpl implements JdbcConnection, SessionEventListener, Serializable {
   
   
    private static final long serialVersionUID = 4009476458425101761L;
    private static final SQLPermission SET_NETWORK_TIMEOUT_PERM = new SQLPermission("setNetworkTimeout");
    //...
}
public interface JdbcConnection extends Connection, MysqlConnection, TransactionEventHandler {
   
   
    JdbcPropertySet getPropertySet();

    void changeUser(String var1, String var2) throws SQLException;
    //...
}

综上我们可以画出对应的类图:

image-20220330234125857

参考资料

http://c.biancheng.net/view/1364.html

https://jishuin.proginn.com/p/763bfbd68968

https://www.cnblogs.com/kuluo/p/13038076.html

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
15天前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
|
2天前
|
设计模式 Java
【JAVA基础篇教学】第十四篇:Java中设计模式
【JAVA基础篇教学】第十四篇:Java中设计模式
|
4天前
|
设计模式 算法 Java
设计模式在Java开发中的应用
设计模式在Java开发中的应用
15 0
|
10天前
|
设计模式 前端开发 Java
19:Web开发模式与MVC设计模式-Java Web
19:Web开发模式与MVC设计模式-Java Web
20 4
|
10天前
|
设计模式 存储 前端开发
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
18:JavaBean简介及其在表单处理与DAO设计模式中的应用-Java Web
25 4
|
10天前
|
设计模式 缓存 监控
JAVA设计模式之结构型模式
结构模型:适配器模型、桥接模型、过滤器模型、组合模型、装饰器模型、外观模型、享受元模型和代理模型。
21 3
|
14天前
|
设计模式 算法 Java
Java基础教程(19)-设计模式简述
【4月更文挑战第19天】设计模式是软件设计中反复使用的代码设计经验,旨在提升代码的可重用性、可扩展性和可维护性。23种模式分为创建型、结构型和行为型三类。创建型模式如工厂方法、抽象工厂、建造者、原型和单例,关注对象创建与使用的分离。结构型模式涉及对象组合,如适配器、装饰器、外观等,增强结构灵活性。行为型模式专注于对象间职责分配和算法合作,包括责任链、命令、观察者等。设计模式提供标准化解决方案,促进代码交流和复用。
|
15天前
|
设计模式 Java
Java 设计模式:混合、装饰器与组合的编程实践
【4月更文挑战第27天】在面向对象编程中,混合(Mixins)、装饰器(Decorators)和组合(Composition)是三种强大的设计模式,用于增强和扩展类的功能。
21 1
|
15天前
|
设计模式 消息中间件 Java
Java 设计模式:探索发布-订阅模式的原理与应用
【4月更文挑战第27天】发布-订阅模式是一种消息传递范式,被广泛用于构建松散耦合的系统。在 Java 中,这种模式允许多个对象监听和响应感兴趣的事件。
35 2
|
15天前
|
设计模式 算法 Java
Java 设计模式:深入模板方法模式的原理与应用
【4月更文挑战第27天】模板方法模式是一种行为设计模式,主要用于定义一个操作中的算法的框架,允许子类在不改变算法结构的情况下重定义算法的某些特定步骤。
22 1