mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 12:27:15 +00:00
89 lines
5.6 KiB
Markdown
89 lines
5.6 KiB
Markdown
# 桥接模式
|
||
|
||
## 概念理解
|
||
|
||
桥接模式也叫作桥梁模式,英文是 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 来执行。
|
||
|
||
|
||
## 总结
|
||
桥接模式有两种理解方式。第一种理解方式是“将抽象和实现解耦,让它们能独立开发”。这种理解方式比较特别,应用场景也不多。另一种理解方式更加简单,类似“组合优于继承”设计原则,这种理解方式更加通用,应用场景比较多。 |