结构型模式——桥接模式(Bridge Pattern)

桥接模式(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)角色:这个角色给出实现化角色接口的具体实现。
优点:
  1. 抽象和实现的分离。
  2. 扩展灵活:在某个变化维度中任意扩展一个维度,都不需要修改原有系统。
  3. 动态切换具体实现
  4. 缓解继承导致的子类臃肿
缺点:
  1. 系统复杂 : 增加了系统的理解与设计难度 。
  2. 识别变化维度 : 需要正确地识别出系统中独立变化的维度 。
  3. 由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
使用场景:
  1. 当一个对象有多个变化维度的时候,通过抽象这些变化维度,将依赖具体的实现,修改为抽象。
  2. 适用于那些不希望使用继承或因为多层次继承,导致系统类的个数急剧增加的系统。
  3. 当我们期望一个对象的多个变化因素可以动态的变化,而且不影响客户的程序的使用时。

**注意事项:**对于两个独立变化的维度,使用桥接模式再适合不过了。

JDK使用分析

JDBC中的驱动便是使用了该模式。

jdbc部分UML图

桥接模式与jdbc驱动

角色对应
  • 抽象化(Abstraction)角色:Connection
  • 修正抽象化(RefinedAbstraction)角色:com.mysql.cj.jdbc.ConnectionImpl、oracle.jdbc.driver.PhysicalConnection等连接
  • 实现化(Implementor)角色:Driver
  • 具体实现化(ConcreteImplementor)角色:MysqlDriver、OracleDriver等具体的数据库驱动
代码分析

多种驱动,拿MySQL来进行举例。

  1. 总所周知…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又做了什么事情?

  2. 带着这个问题从接口java.sql.Driver下手,这个接口扮演的是实现化(Implementor)角色。

    package java.sql;
    ...
    public interface Driver {
        ...
        //获取数据库连接的主要方法
        Connection connect(String url, java.util.Properties info) throws SQLException;
        ...
    }
    

    我们有许多数据库种类,所以jdbc中就拥有着许多数据库驱动。例如:MySQL驱动、Oracle数据库驱动等。

    所以jdbc中该维度(驱动)有改变、拓展的需求

  3. 其次com.mysql.cj.jdbc.NonRegisteringDrivercom.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中的注册方法。

  4. 那么去到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;
        }    
    }
    
  5. 回到加载驱动后。开始获取连接

    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);
                    }
                    ...
                }
            ...
        }
    }
    
  6. 从DriverManager中获得了对应的连接,并继续通过该连接获取Statement类。

    而这个Connection因为是在抽象的情况下扮演了抽象化(Abstraction)角色(其中因为Driver的不同获取不同的Connection)。

    package java.sql;
    ...
    public interface Connection  extends Wrapper, AutoCloseable {
        //Statement的创建与返回
    	Statement createStatement() throws SQLException;
    	...
    }
    
  7. ConnectionImpl扮演的就是修正抽象化(RefinedAbstraction)角色。

    DriverConnectionImpl的一部分,但是他们之间的变化却互不影响。

    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;
        }
    ...
    }
    
  8. 小结:

    • 将这几个类抽象,抽象到DriverConnection的关,两个维度出现。
      • 驱动维度
      • 连接维度
    • 驱动与连接两个维度,是为了获取Statement对象。
      1. 则在抽象概念上实现了桥接模式。
  9. 小结:

    • 将这几个类抽象,抽象到DriverConnection的关,两个维度出现。
      • 驱动维度
      • 连接维度
    • 驱动与连接两个维度,是为了获取Statement对象。
      1. 则在抽象概念上实现了桥接模式。

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值