Files
knowledge-kit/Chapter6 - Design Pattern/6.14.md
2023-11-02 16:06:38 +08:00

5.6 KiB
Raw Blame History

桥接模式

概念理解

桥接模式也叫作桥梁模式,英文是 Bridge Design Pattern。这个模式可以说是 23 种设计模式中最难理解的模式之一了。我查阅了比较多的书籍和资料之后发现,对于这个模式有两种不同的理解方式。

这其中“最纯正”的理解方式,当属 GoF 的《设计模式》一书中对桥接模式的定义。毕竟,这 23 种经典的设计模式,最初就是由这本书总结出来的。在 GoF 的《设计模式》一书中桥接模式是这么定义的“Decouple an abstraction from its implementation so that the two can vary independently。”翻译成中文就是将抽象和实现解耦,让它们可以独立变化

很多书籍、资料中,还有另外一种理解方式:“一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。”通过组合关系来替代继承关系,避免继承层次的指数级爆炸。这种理解方式非常类似于,我们之前讲过的“组合优于继承”设计原则

GoF 给出的定义非常的简短,单凭这一句话,估计没几个人能看懂是什么意思。所以,我们通过 JDBC 驱动的例子来解释一下。JDBC 驱动是桥接模式的经典应用。我们先来看一下,如何利用 JDBC 驱动来查询数据库。具体的代码如下所示

Class.forName("com.mysql.jdbc.Driver");//加载及注册JDBC驱动程序
String url = "jdbc:mysql://localhost:3306/sample_db?user=root&password=your_password
Connection con = DriverManager.getConnection(url);
Statement stmt = con.createStatement()
String query = "select * from test";
ResultSet rs=stmt.executeQuery(query);
while(rs.next()) {
    rs.getString(1);
    rs.getInt(2);
}

如果我们想要把 MySQL 数据库换成 Oracle 数据库,只需要把第一行代码中的 com.mysql.jdbc.Driver 换成 oracle.jdbc.driver.OracleDriver 就可以了。当然,也有更灵活的实现方式,我们可以把需要加载的 Driver 类写到配置文件中,当程序启动的时候,自动从配置文件中加载,这样在切换数据库的时候,我们都不需要修改代码,只需要修改配置文件就可以了。

分析源码 com.mysql.jdbc.Driver

package com.mysql.jdbc;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    /**
    * Construct a new driver and register it with DriverManager
    * @throws SQLException if a database error occurs.
    */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

结合 com.mysql.jdbc.Driver 的代码实现,我们可以发现,当执行 Class.forName(“com.mysql.jdbc.Driver”) 这条语句的时候,实际上是做了两件事情。第一件事情是要求 JVM 查找并加载指定的 Driver 类,第二件事情是执行该类的静态代码,也就是将 MySQL Driver 注册到 DriverManager 类中。 现在我们再来看一下DriverManager 类是干什么用的。具体的代码如下所示。当我们把具体的 Driver 实现类比如com.mysql.jdbc.Driver注册到 DriverManager 之后,后续所有对 JDBC 接口的调用,都会委派到对具体的 Driver 实现类来执行。而 Driver 实现类都实现了相同的接口java.sql.Driver ),这也是可以灵活切换 Driver 的原因

public class DriverManager {
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new
    //...
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
    //...
    public static synchronized void registerDriver(java.sql.Driver driver) throws
        if (driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver));
        } else {
            throw new NullPointerException();
        }
    }
    public static Connection getConnection(String url, String user, String password) {
        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()));
    }
    //...
}

桥接模式的定义是“将抽象和实现解耦,让它们可以独立变化”。那弄懂定义中“抽象”和“实现”两个概念,就是理解桥接模式的关键。那在 JDBC 这个例子中,什么 是“抽象”?什么是“实现”呢?

实际上JDBC 本身就相当于“抽象”。注意这里所说的“抽象”指的并非“抽象类”或“接口”而是跟具体的数据库无关的、被抽象出来的一套“类库”。具体的Driver比如com.mysql.jdbc.Driver就相当于“实现”。注意这里所说的“实现”也并非指“接口的实现类”而是跟具体数据库相关的一套“类库”。JDBC 和 Driver 独立开发通过对象之间的组合关系组装在一起。JDBC 的所有逻辑操作,最终都委托给 Driver 来执行。

总结

桥接模式有两种理解方式。第一种理解方式是“将抽象和实现解耦,让它们能独立开发”。这种理解方式比较特别,应用场景也不多。另一种理解方式更加简单,类似“组合优于继承”设计原则,这种理解方式更加通用,应用场景比较多。