聊聊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
目录
相关文章
|
8天前
|
设计模式 开发者
【设计模式】第七篇:和我一起简单认识桥接模式
实现的意思并不是指抽象的派生类,而是指通过组合来代替继承关系,从而降低抽象和具体实现产品两个可变换维度之间的耦合,就像我们的相机品牌和相机产品类型之间的分离
21 4
|
25天前
|
设计模式 Java
【设计模式】JAVA Design Patterns——Bridge(桥接模式)
【设计模式】JAVA Design Patterns——Bridge(桥接模式)
【设计模式】JAVA Design Patterns——Bridge(桥接模式)
|
21天前
|
设计模式 Java 数据库连接
【重温设计模式】代理模式及其Java示例
【重温设计模式】代理模式及其Java示例
|
25天前
|
设计模式 Java API
【设计模式】JAVA Design Patterns——Combinator(功能模式)
【设计模式】JAVA Design Patterns——Combinator(功能模式)
|
25天前
|
设计模式 监控 Java
【设计模式】JAVA Design Patterns——Circuit Breaker(断路器模式)
【设计模式】JAVA Design Patterns——Circuit Breaker(断路器模式)
|
25天前
|
设计模式 Java 程序员
【设计模式】JAVA Design Patterns——Bytecode(字节码模式)
【设计模式】JAVA Design Patterns——Bytecode(字节码模式)
|
25天前
|
设计模式 算法 Java
【设计模式】JAVA Design Patterns——Builder(构造器模式)
【设计模式】JAVA Design Patterns——Builder(构造器模式)
|
3天前
|
设计模式 存储 安全
Java中的23种设计模式
Java中的23种设计模式
5 1
|
18天前
|
设计模式 存储 前端开发
Java的mvc设计模式在web开发中应用
Java的mvc设计模式在web开发中应用
|
20天前
|
设计模式 安全 Java
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
该文介绍了一种C++的编程技巧——奇异递归模板模式(CRTP),旨在让派生组件能继承基本组件的特定功能。通过示例展示了如何创建一个`Fighter`接口和`MmaFighter`类,其中`MmaFighter`及其子类如`MmaBantamweightFighter`和`MmaHeavyweightFighter`强制类型安全,确保相同重量级的拳手之间才能进行比赛。这种设计避免了不同重量级拳手间的错误匹配,编译时会报错。CRTP适用于处理类型冲突、参数化类方法和限制方法只对相同类型实例生效的情况。
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)