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

博客 动态
0 159
优雅殿下
优雅殿下 2022-03-30 23:57:21
悬赏:0 积分 收藏

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

桥接(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驱动注入到DriverManagerClass.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

posted @ 2022-03-30 23:43 归斯君 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员