设计模式学习笔记(九)桥接模式及其应用
桥接(Bridge)模式是指将抽象部分与实现部分相分离,使它们都可以独立的发生变化。
一、桥接模式介绍
【设计模式学习笔记(九)桥接模式及其应用】我们知道,抽象部分一般与实现部分连接有两种方式:继承和实现。那么如何将其解耦分离,桥接模式提供一种方式,也就是将强关联转为弱关联,将继承转换为组合关系。如下图所示,取消两者的继承关系,改用组合关系:
文章图片
1.1 桥接模式的结构
我们可以看看桥接模式是怎么解耦,利用组合连接抽象和实现部分,如下所示:
其结构中包含如下角色:
Abstraction
:抽象化角色,定义抽象类,包含一个对实现化对象的引用(组合)RefinedAbstraction
:扩展抽象化角色,实现抽象化角色的子类,由此通过组合关系调用实现化角色中的业务方法Implementor
:实现化角色的接口,供扩展抽象化角色调用ImplementorA、ImplementorB
:实现化角色的具体实现
我们可以根据上面的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驱动注入到DriverManager
,registerDriver()
方法具体如下: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 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 Connectionextends 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;
//...
}
综上我们可以画出对应的类图:
文章图片
参考资料 http://c.biancheng.net/view/1364.html
https://jishuin.proginn.com/p/763bfbd68968
https://www.cnblogs.com/kuluo/p/13038076.html
推荐阅读
- Qt学习|Qt学习之使用QTableWiget实现简易五子棋游戏(人机对战)
- 【Redis 系列】redis 学习九,Redis 的发布和订阅是咋玩的
- 【Redis 系列】redis 学习八,redis 持久化 RDB 和 AOF
- C语言指针学习
- OpenWrt|OpenWrt学习总结(2)编译OpenWrt过程中整理的软件包下载网址
- 一点就分享系列|一点就分享系列(实践篇4-上篇)深度学习部署之Tensorrt转换思路(“授人与鱼不如授人与渔”)
- stm32|stm32学习之路,方法很重要
- 嵌入式|网上五花八门的单片机教程,到底应该怎么整理学习过程
- 笔记|纯手写SpringFramework-第二代(原创)
- 算法笔记练习题|(5.6B)n的阶乘