桥接模式详解和分析JDBC中的应用
🎯 设计模式专栏,持续更新中, 欢迎订阅:JAVA实现设计模式
🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言
桥接模式
文章目录
- 桥接模式
- 桥接模式的四个核心组成:
- 案例:电视与遥控器的桥接模式
- ⚙️代码实现
- Step 1: 实现 `TV` 接口 (Implementor)
- Step 2: 创建 `SonyTV` 和 `SamsungTV` 类 (Concrete Implementor)
- Step 3: 创建 `RemoteControl` 类 (Abstraction)
- Step 4: 创建 `SonyRemoteControl` 和 `SamsungRemoteControl` (Refined Abstraction)
- 🌟 Main函数展示桥接模式的使用
- 源码应用
- JDBC中的桥接模式源码探索:
- JDBC桥接模式总结:
- 📜 总结:
桥接模式(Bridge Pattern)是一种结构型设计模式,目的是将抽象部分与它的实现部分分离开来,使它们可以独立变化。这种模式通过组合的方式(而不是继承)将不同的维度分离,使代码更加灵活、易于扩展。
简单说法: 想象你有一个电视遥控器,遥控器负责打开/关闭电视、调节音量等操作。而不同品牌的电视(如索尼、三星)可能实现这些操作的方式不一样。桥接模式允许你在不修改遥控器代码的情况下,切换或扩展不同品牌的电视。遥控器和电视品牌之间使用接口桥接,使得它们可以独立变化。
桥接模式的四个核心组成:
- Abstraction(抽象部分):高层接口,定义抽象的操作,如遥控器的功能(打开、关闭、调音量等)。
- Refined Abstraction(扩展抽象部分):扩展自Abstraction,定义更具体的遥控器类型。
- Implementor(实现部分):具体实现接口,不同的实现类可以完成具体操作(不同品牌电视的功能实现)。
- Concrete Implementor(具体实现部分):Implementor的具体实现,如SonyTV和SamsungTV。
案例:电视与遥控器的桥接模式
你有一个通用的遥控器,可以操作不同品牌的电视,遥控器定义了on()
、off()
、setChannel()
等功能,而每个品牌的电视实现这些功能的细节可能不同。通过桥接模式,我们将遥控器和电视品牌分离,便于扩展。
UML类图结构
Abstraction Implementor
RemoteControl ----> TV
/ \ / \
Concrete SonyRemote SonyTV SamsungTV
Abstraction ConcreteImplementor
⚙️代码实现
Step 1: 实现 TV
接口 (Implementor)
// TV接口定义基本操作
interface TV {
void on();
void off();
void setChannel(int channel);
}
Step 2: 创建 SonyTV
和 SamsungTV
类 (Concrete Implementor)
// SonyTV 具体实现类
class SonyTV implements TV {
@Override
public void on() {
System.out.println("Sony TV is now ON");
}
@Override
public void off() {
System.out.println("Sony TV is now OFF");
}
@Override
public void setChannel(int channel) {
System.out.println("Sony TV: Channel set to " + channel);
}
}
// SamsungTV 具体实现类
class SamsungTV implements TV {
@Override
public void on() {
System.out.println("Samsung TV is now ON");
}
@Override
public void off() {
System.out.println("Samsung TV is now OFF");
}
@Override
public void setChannel(int channel) {
System.out.println("Samsung TV: Channel set to " + channel);
}
}
Step 3: 创建 RemoteControl
类 (Abstraction)
// 遥控器抽象类,组合TV接口
abstract class RemoteControl {
protected TV tv;
// 通过构造方法传递TV对象,形成桥接
public RemoteControl(TV tv) {
this.tv = tv;
}
public abstract void on();
public abstract void off();
public abstract void setChannel(int channel);
}
Step 4: 创建 SonyRemoteControl
和 SamsungRemoteControl
(Refined Abstraction)
// 扩展的具体遥控器类,使用不同的TV实现
class SonyRemoteControl extends RemoteControl {
public SonyRemoteControl(TV tv) {
super(tv);
}
@Override
public void on() {
System.out.println("Using Sony Remote Control:");
tv.on();
}
@Override
public void off() {
System.out.println("Using Sony Remote Control:");
tv.off();
}
@Override
public void setChannel(int channel) {
System.out.println("Using Sony Remote Control:");
tv.setChannel(channel);
}
}
class SamsungRemoteControl extends RemoteControl {
public SamsungRemoteControl(TV tv) {
super(tv);
}
@Override
public void on() {
System.out.println("Using Samsung Remote Control:");
tv.on();
}
@Override
public void off() {
System.out.println("Using Samsung Remote Control:");
tv.off();
}
@Override
public void setChannel(int channel) {
System.out.println("Using Samsung Remote Control:");
tv.setChannel(channel);
}
}
🌟 Main函数展示桥接模式的使用
public class BridgePatternDemo {
public static void main(String[] args) {
// 创建不同品牌的电视
TV sonyTV = new SonyTV();
TV samsungTV = new SamsungTV();
// 使用Sony Remote控制Sony TV
RemoteControl sonyRemote = new SonyRemoteControl(sonyTV);
sonyRemote.on();
sonyRemote.setChannel(5);
sonyRemote.off();
System.out.println("-------------");
// 使用Samsung Remote控制Samsung TV
RemoteControl samsungRemote = new SamsungRemoteControl(samsungTV);
samsungRemote.on();
samsungRemote.setChannel(10);
samsungRemote.off();
}
}
✨ 运行结果:
Using Sony Remote Control:
Sony TV is now ON
Using Sony Remote Control:
Sony TV: Channel set to 5
Using Sony Remote Control:
Sony TV is now OFF
-------------
Using Samsung Remote Control:
Samsung TV is now ON
Using Samsung Remote Control:
Samsung TV: Channel set to 10
Using Samsung Remote Control:
Samsung TV is now OFF
源码应用
JDBC中的桥接模式源码探索:
背景:
在Java的数据库连接(JDBC)中,JDBC API 提供了统一的数据库操作接口,而具体的数据库操作实现则由各数据库厂商的驱动(Driver)实现。这里,JDBC API 是抽象部分,而每个数据库驱动则是具体实现部分。
桥接模式分析:
- Abstraction(抽象部分):
java.sql.DriverManager
提供了一个统一的接口,用于注册不同数据库的驱动。 - Implementor(实现部分): 各个数据库驱动(例如MySQL、PostgreSQL、Oracle等),分别通过实现
java.sql.Driver
接口来具体执行数据库连接操作。
桥接效果:
程序员只需基于统一的JDBC
接口操作数据库,而无需关心底层数据库具体如何实现连接。通过桥接模式,可以轻松扩展或替换不同的数据库驱动,而不影响客户端代码。
代码使用示例:
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
DriverManager
在内部会根据传入的 JDBC URL
查找合适的 Driver
来建立数据库连接,这样就将抽象的 Connection
操作与具体的数据库实现分离。
🌐 桥接模式的类图
+-------------------------------------+ +--------------------------------+
| java.sql.Driver | <---- Bridge | Abstract representation |
|-------------------------------------| | of how to connect to database |
| + connect() | | using different implementations|
| + acceptURL() | +--------------------------------+
| + other common methods |
+----------------+--------------------+
^
| Implementor
|
+-------------------------------+ +------------------------------------+
| com.mysql.cj.jdbc.Driver | | org.postgresql.Driver |
+-------------------------------+ +------------------------------------+
| + Implementation of connect() | | + Implementation of connect() |
+-------------------------------+ +------------------------------------+
^ ^
| |
+-------------------------------+ |
| java.sql.DriverManager | |
+-------------------------------+ |
| + registerDriver(Driver d) | |
| + getConnection(String url) | |
| + other common methods | |
+-------------------------------+ |
|
+------------+-------------+
| java.sql.Connection |
+--------------------------+
| + createStatement() |
| + close() |
+--------------------------+
⚙️ 关键源码解读:
1. Driver
接口(抽象部分,桥接接口)
package java.sql;
import java.util.Properties;
import java.sql.SQLException;
public interface Driver {
// 尝试建立数据库连接
Connection connect(String url, Properties info) throws SQLException;
// 判断Driver是否可以处理传入的URL
boolean acceptsURL(String url) throws SQLException;
// 获取驱动的相关属性信息
DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException;
// 获取驱动的主版本号
int getMajorVersion();
// 获取驱动的次版本号
int getMinorVersion();
// 检查驱动是否为JDBC规范的合规实现
boolean jdbcCompliant();
}
在这个接口中,Driver
定义了通用的行为,所有数据库驱动都需要实现这些方法,如 connect()
和 acceptsURL()
。
2. DriverManager
类(抽象部分与实现部分的桥梁)
package java.sql;
import java.util.Enumeration;
import java.util.Vector;
import java.sql.Driver;
import java.sql.SQLException;
public class DriverManager {
// 注册驱动
public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
}
// 通过指定URL连接数据库
public static Connection getConnection(String url, String user, String password)
throws SQLException {
java.util.Properties info = new java.util.Properties();
info.put("user", user);
info.put("password", password);
return getConnection(url, info);
}
// 具体的连接数据库逻辑
public static Connection getConnection(String url, java.util.Properties info)
throws SQLException {
// 遍历已注册的驱动
for (DriverInfo aDriver : registeredDrivers) {
try {
if (aDriver.driver.acceptsURL(url)) {
return aDriver.driver.connect(url, info); // 调用驱动的connect方法
}
} catch (SQLException e) {
// 忽略异常,尝试下一个驱动
}
}
throw new SQLException("No suitable driver found for " + url);
}
// 保持已注册驱动的信息
private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
static {
// 内部静态代码块加载驱动
loadInitialDrivers();
}
// 省略其他方法
}
解析:
DriverManager.registerDriver()
用于注册数据库驱动,开发者无需直接使用具体驱动类来注册。DriverManager.getConnection()
是我们日常使用的API,内部通过调用已注册的Driver
实现connect()
方法来建立连接。
桥接模式应用: DriverManager
作为抽象的管理者,通过 Driver
接口的实现来具体连接到不同的数据库,真正实现了抽象和实现的分离。不同的数据库驱动(如MySQL、PostgreSQL等)是 Driver
接口的不同实现,开发者使用统一的 DriverManager.getConnection()
方法进行数据库操作,无需了解底层的实现。
3. com.mysql.cj.jdbc.Driver
类(具体实现部分)
package com.mysql.cj.jdbc;
import java.sql.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class Driver implements java.sql.Driver {
static {
try {
// 在类加载时自动注册MySQL驱动
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException e) {
throw new RuntimeException("Failed to register MySQL driver.", e);
}
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
// 检查是否支持该URL
if (!acceptsURL(url)) {
return null;
}
// 创建与MySQL数据库的连接
return new ConnectionImpl(host, port, dbName, user, password);
}
@Override
public boolean acceptsURL(String url) {
// 检查URL是否符合MySQL的格式
return url.startsWith("jdbc:mysql:");
}
// 省略其他实现方法...
}
解析:
-
com.mysql.cj.jdbc.Driver
是Driver
接口的具体实现,通过桥接到DriverManager
,它实现了具体的连接MySQL数据库的逻辑。 -
acceptsURL()
方法判断传入的URL是否是MySQL的格式,connect()
方法则通过该URL与MySQL数据库建立连接。 -
JDBC桥接模式总结:
- Abstraction(抽象部分):
DriverManager
是抽象部分,提供统一的API供开发者使用,它只负责管理和协调不同的Driver
实现,而不关心具体实现细节。 - Implementor(实现部分): 各个数据库驱动实现了
Driver
接口,例如com.mysql.cj.jdbc.Driver
和org.postgresql.Driver
,它们分别负责与具体的数据库进行交互。
桥接模式通过分离抽象和实现,使得JDBC API可以轻松支持多种不同的数据库,而开发者只需通过统一的
DriverManager.getConnection()
进行操作。不同数据库的连接实现细节被隐藏在各自的Driver
实现类中,确保了系统的扩展性和维护性。 - Abstraction(抽象部分):
📜 总结:
桥接模式让我们能够将抽象部分与具体实现分离,在保持系统灵活的同时,允许独立扩展抽象和实现层次。在上面的例子中,RemoteControl
和 TV
分离,使得你可以轻松扩展更多不同类型的遥控器或电视品牌,而无需修改已有代码。
桥接模式在JDK和各种框架中得到广泛应用,特别是在处理跨平台差异、解耦抽象和实现的场景中。通过桥接模式,能够保持系统的高扩展性和灵活性,便于维护和改进。无论是数据库操作、GUI绘制、日志框架还是视图解析,桥接模式都扮演着核心角色。