• 设计模式学习笔记(九)桥接模式及其应用


    桥接(Bridge)模式是指将抽象部分与实现部分相分离,使它们都可以独立的发生变化。

    一、桥接模式介绍

    我们知道,抽象部分一般与实现部分连接有两种方式:继承和实现。那么如何将其解耦分离,桥接模式提供一种方式,也就是将强关联转为弱关联,将继承转换为组合关系。如下图所示,取消两者的继承关系,改用组合关系:

    image-20220330181522305

    1.1 桥接模式的结构

    我们可以看看桥接模式是怎么解耦,利用组合连接抽象和实现部分,如下所示:

    image-20220330204226055

    其结构中包含如下角色:

    • Abstraction:抽象化角色,定义抽象类,包含一个对实现化对象的引用(组合)
    • RefinedAbstraction:扩展抽象化角色,实现抽象化角色的子类,由此通过组合关系调用实现化角色中的业务方法
    • Implementor:实现化角色的接口,供扩展抽象化角色调用
    • ImplementorA、ImplementorB:实现化角色的具体实现

    1.2 桥接模式的实现

    我们可以根据上面的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驱动注入到DriverManagerregisterDriver()方法具体如下:

    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<DriverInfo> 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 Connection  extends 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;
        //...
    }
    

    综上我们可以画出对应的类图:

    image-20220330234125857

    参考资料

    http://c.biancheng.net/view/1364.html

    https://jishuin.proginn.com/p/763bfbd68968

    https://www.cnblogs.com/kuluo/p/13038076.html

  • 相关阅读:
    poj_3984_迷宫问题_(bfs+记录路径)
    uva_10369_mst
    HDU_1875_mst
    人工智能、机器学习和深度学习做好准备的数据中心
    网络安全需求爆发,千亿产业人才匮乏
    物联网IOT市场才起步,还要10年才成熟
    AI人士不得不了解的十大机器算法
    中国“人工智能”实力最强的5所大学
    物联网的历史:过去与未来
    人工智能进入第四代,人工直觉成为下一步发展
  • 原文地址:https://www.cnblogs.com/EthanWong/p/16079803.html
Copyright © 2020-2023  润新知