文章目录
桥接模式(Bridge Pattern)
使用
java中的使用模板
public class BridgePattern {
public static void main(String[] args) {
Implementor implementorA = new ConcreteImplementorA();
Implementor implementorB = new ConcreteImplementorB();
RefinedAbstraction abstractionA = new RefinedAbstraction(implementorA);
RefinedAbstraction abstractionB = new RefinedAbstraction(implementorB);
abstractionA.operation();
System.out.println("===================================");
abstractionB.operation();
}
}
/**
* 维度一接口
*/
interface Implementor{
void implementorOperation();
}
/**
* 维度一变化A
*/
class ConcreteImplementorA implements Implementor{
@Override
public void implementorOperation() {
System.out.println("维度一:实现类a");
}
}
/**
* 维度一变化B
*/
class ConcreteImplementorB implements Implementor{
@Override
public void implementorOperation() {
System.out.println("维度一:实现类b");
}
}
/**
* 主维度抽象类
* 其中保存了维度一的实例
*/
abstract class Abstraction {
protected Implementor implementor;
//通过构造器获得其他维度变化的实例
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
abstract void operation();
}
/**
* 主维度实现类
* 其中使用了维度一的实例
*/
class RefinedAbstraction extends Abstraction{
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
void operation() {
System.out.println("主维度开始");
//TUDO 使用父类中的维度一实例
implementor.implementorOperation();
System.out.println("主维度结束");
}
}
JDK中的实例分析
- 数据库驱动
简介
桥接模式(Bridge Pattern)又称为柄体(Handle and Body)模式或接口(Interfce)模式,属于结构型模式。
用于把抽象化与实现化解耦。它通过提供抽象化和实现化之间的桥接结构,来实现解耦,使得它们可以独立变化。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
意图:
将抽象部分与实现部分分离,使它们都可以独立的变化。
主要解决:
在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
何时使用:
系统中可能在多个维度变化,每一个维度都可能变化。
如何解决:
每个角度进行独立变化,独立扩展
关键代码:
抽象类依赖实现类。
UML:
角色:
- 抽象化(Abstraction)角色:抽象化主要内容,并保存实现对象的引用。
- 修正抽象化(RefinedAbstraction)角色:扩展抽象化角色,具体抽象化的定义。
- 实现化(Implementor)角色:这个角色为其他维度变化的接口。其实现的实例被作为参数存在于抽象化角色。
- 具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。
优点:
- 抽象和实现的分离。
- 扩展灵活:在某个变化维度中任意扩展一个维度,都不需要修改原有系统。
- 动态切换具体实现
- 缓解继承导致的子类臃肿
缺点:
- 系统复杂 : 增加了系统的理解与设计难度 。
- 识别变化维度 : 需要正确地识别出系统中独立变化的维度 。
- 由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
使用场景:
- 当一个对象有多个变化维度的时候,通过抽象这些变化维度,将依赖具体的实现,修改为抽象。
- 适用于那些不希望使用继承或因为多层次继承,导致系统类的个数急剧增加的系统。
- 当我们期望一个对象的多个变化因素可以动态的变化,而且不影响客户的程序的使用时。
**注意事项:**对于两个独立变化的维度,使用桥接模式再适合不过了。
JDK使用分析
JDBC中的驱动便是使用了该模式。
jdbc部分UML图
角色对应
- 抽象化(Abstraction)角色:Connection
- 修正抽象化(RefinedAbstraction)角色:com.mysql.cj.jdbc.ConnectionImpl、oracle.jdbc.driver.PhysicalConnection等连接
- 实现化(Implementor)角色:Driver
- 具体实现化(ConcreteImplementor)角色:MysqlDriver、OracleDriver等具体的数据库驱动
代码分析
多种驱动,拿MySQL来进行举例。
-
总所周知…
jdbc
连接MySQL数据库需要经历的步骤//1.加载MySQL驱动注入到DriverManager Class.forName("com.mysql.cj.jdbc.Driver"); //2.创建数据库的连接 Connection conn = DriverManager.getConnection(url, username, password); //3.创建statement实例 Statement statement = conn.createStatement(); //4.执行SQL语句 ResultSet resultSet = statement.executeQuery(sql); //5.关闭连接对象 connection.close();
过去经常疑惑的就是第一步——加载驱动。那么就用它来开始这次分析。
这段代码简单来说就是将类通过类名进行初始化。但是初始化的时候
com.mysql.cj.jdbc.Driver
又做了什么事情? -
带着这个问题从接口
java.sql.Driver
下手,这个接口扮演的是实现化(Implementor)角色。package java.sql; ... public interface Driver { ... //获取数据库连接的主要方法 Connection connect(String url, java.util.Properties info) throws SQLException; ... }
我们有许多数据库种类,所以
jdbc
中就拥有着许多数据库驱动。例如:MySQL驱动、Oracle数据库驱动等。所以在
jdbc
中该维度(驱动)有改变、拓展的需求。 -
其次
com.mysql.cj.jdbc.NonRegisteringDriver
与com.mysql.cj.jdbc.Driver
这两个类作为具体实现化(ConcreteImplementor)角色... public class NonRegisteringDriver implements java.sql.Driver { ... //该类中实现接口中的获取连接的方法 public java.sql.Connection connect(String url, Properties info) throws SQLException { ... } ... } ——————————————————————————————————分割线———————————————————————————————————————————— package com.mysql.cj.jdbc; ... public class Driver extends NonRegisteringDriver implements java.sql.Driver { //创建实例时加载静态代码块 static { try { //调用构造器获得实例并将该实例注册到DriverManager java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } public Driver() throws SQLException { } }
在该类的初始化中就调用了
DriverManager
中的注册方法。 -
那么去到
java.sql.DriverManager
中。... public class DriverManager { //该变量实际上一个list //保存了驱动信息列表。正是因此,驱动与DriverManager之间的变动互不影响。 //也就是这座桥,将两个不同的维度相连接 private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); ... //该方法即为com.mysql.cj.jdbc.Driver中所用的注册实例的静态方法 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 { if(driver != null) { //当运行到此处时,带有驱动实例的类DriverInfo,将会在registeredDrivers中不存在的情况下存入 registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { throw new NullPointerException(); } println("registerDriver: " + driver); } ... } /* * Wrapper class for registered Drivers in order to not expose Driver.equals() * to avoid the capture of the Driver it being compared to as it might not * normally have access. * 大意为:通过该类来防止因为权限问题无法比较两个驱动是否相同的问题。 */ class DriverInfo { //驱动实例 final Driver driver; ... //驱动比较 @Override public boolean equals(Object other) { return (other instanceof DriverInfo) && this.driver == ((DriverInfo) other).driver; } }
-
回到加载驱动后。开始获取连接
Class.forName("com.mysql.cj.jdbc.Driver"); Connection conn = DriverManager.getConnection(url,user,password);
加载了驱动类后,遍历已经注册的驱动,尝试建立连接。
... public class DriverManager { //获取数据库连接方法,此方法做完校验后调用的下一个方法 @CallerSensitive public static Connection getConnection(String url, String user, String password) throws SQLException { ... return (getConnection(url, info, Reflection.getCallerClass())); } //获取数据库连接(主要) private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException { ... //遍历已经被注册的驱动 for(DriverInfo aDriver : registeredDrivers) { ...//此处省略了校验等代码 //调用成功匹配到的驱动,并进行连接,返回的是驱动对应的连接对象☆☆☆ Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } ... } ... } }
-
从DriverManager中获得了对应的连接,并继续通过该连接获取
Statement
类。而这个
Connection
因为是在抽象的情况下扮演了抽象化(Abstraction)角色(其中因为Driver的不同获取不同的Connection)。package java.sql; ... public interface Connection extends Wrapper, AutoCloseable { //Statement的创建与返回 Statement createStatement() throws SQLException; ... }
-
ConnectionImpl
扮演的就是修正抽象化(RefinedAbstraction)角色。Driver
为ConnectionImpl
的一部分,但是他们之间的变化却互不影响。public class ConnectionImpl implements JdbcConnection, SessionEventListener, Serializable { ... @Override public java.sql.Statement createStatement() throws SQLException { return createStatement(DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); } @Override public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { StatementImpl stmt = new StatementImpl(getMultiHostSafeProxy(), this.database); stmt.setResultSetType(resultSetType); stmt.setResultSetConcurrency(resultSetConcurrency); return stmt; } ... }
-
小结:
- 将这几个类抽象,抽象到
Driver
与Connection
的关,两个维度出现。- 驱动维度
- 连接维度
- 驱动与连接两个维度,是为了获取
Statement
对象。- 则在抽象概念上实现了桥接模式。
- 将这几个类抽象,抽象到
-
小结:
- 将这几个类抽象,抽象到
Driver
与Connection
的关,两个维度出现。- 驱动维度
- 连接维度
- 驱动与连接两个维度,是为了获取
Statement
对象。- 则在抽象概念上实现了桥接模式。
- 将这几个类抽象,抽象到
参考: