设计模式十一--桥接模式

392 阅读3分钟

这是我参与更文挑战的第24天,活动详情查看: 更文挑战

设计模式

桥接模式

将实现和抽象放在两个不同的类层次中,使两个层次独立改变,即抽象化与实现化解耦,使得二者可以独立变化

假如我们现在的交通工具有汽车和火车,动力提供的方式有电和油。桥接模式如下

提供VehicleType接口

package com.wangscaler.bridge;

/**
 * @author wangscaler
 * @date 2021.06.24 16:48
 */
public interface VehicleType {
    void run();
}

供Car去实现

package com.wangscaler.bridge;

/**
 * @author wangscaler
 * @date 2021.06.24 16:48
 */
public class Car implements VehicleType {
    @Override
    public void run() {
        System.out.println("汽车在行驶");
    }
}

供Train去实现

package com.wangscaler.bridge;

/**
 * @author wangscaler
 * @date 2021.06.24 16:48
 */
public class Train implements VehicleType {
    @Override
    public void run() {
        System.out.println("火车在行驶");
    }
}

中间的桥梁即抽象类Vehicle

package com.wangscaler.bridge;

/**
 * @author wangscaler
 * @date 2021.06.24 16:48
 */
public abstract class Vehicle {
    private VehicleType vehicleType;

    public Vehicle(VehicleType vehicleType) {
        this.vehicleType = vehicleType;
    }

    protected void run() {
        this.vehicleType.run();
    }
}

电力的ElectricVehicle通过继承Vehicle的桥梁获取接口的实现类里的super.run();

package com.wangscaler.bridge;

/**
 * @author wangscaler
 * @date 2021.06.24 16:48
 */
public class ElectricVehicle extends Vehicle {
    public ElectricVehicle(VehicleType vehicleType) {
        super(vehicleType);
    }

    @Override
    protected void run() {
        System.out.println("提供电");
        super.run();

    }
}

同理油力的OilVehicle也是通过继承Vehicle的桥梁获取接口的实现类里的super.run();

package com.wangscaler.bridge;

/**
 * @author wangscaler
 * @date 2021.06.24 16:48
 */
public class OilVehicle extends Vehicle {
    public OilVehicle(VehicleType vehicleType) {
        super(vehicleType);
    }

    @Override
    protected void run() {
        System.out.println("提供油");
        super.run();

    }
}

main

package com.wangscaler.bridge;

/**
 * @author wangscaler
 * @date 2021.06.24 16:48
 */

public class Bridge {
    public static void main(String[] args) {
        ElectricVehicle electricVehicle = new ElectricVehicle(new Train());
        electricVehicle.run();
    }
}

当我们增加了油电混合的能源时,只需要增加OilElectricVehicle

package com.wangscaler.bridge;

/**
 * @author wangscaler
 * @date 2021.06.24 16:48
 */
public class OilElectricVehicle extends Vehicle {
    public OilElectricVehicle(VehicleType vehicleType) {
        super(vehicleType);
    }

    @Override
    protected void run() {
        System.out.println("油电混合的能源");
        super.run();
    }
}

即可实现。

源码中的桥接模式

JDBC中Driver接口

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

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!");
        }
    }
}

java.sql.Driver 就是桥接模式中的接口,上述代码是Mysql的Driver也就是Driver的实现类,当然Orcale的Driver也是实现的这个接口

而这个实现类通过DriverManager的registerDriver来注册驱动,此时将Driver加入到registeredDrivers中。

当我们加载了Mysql的驱动,通过DriverManager在调用getConnection的时候,会根据使用的驱动加载connection

public class DriverManager {
   private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {

        registerDriver(driver, null);
    }
    
    @CallerSensitive
    public static Connection getConnection(String url,
        java.util.Properties info) throws SQLException {

        return (getConnection(url, info, Reflection.getCallerClass()));
    }
	public static synchronized void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }

    @CallerSensitive
    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()));
    }


    @CallerSensitive
    public static Connection getConnection(String url)
        throws SQLException {

        java.util.Properties info = new java.util.Properties();
        return (getConnection(url, info, Reflection.getCallerClass()));
    }
     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");
    }


}

比如Mysql的Driver就是继承了NonRegisteringDriver并实现了connect这个方法

public class NonRegisteringDriver implements Driver {

    public Connection connect(String url, Properties info) throws SQLException {
        try {
            try {
                if (!ConnectionUrl.acceptsUrl(url)) {
                    return null;
                } else {
                    ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info);
                    switch(conStr.getType()) {
                    case SINGLE_CONNECTION:
                        return ConnectionImpl.getInstance(conStr.getMainHost());
                    case FAILOVER_CONNECTION:
                    case FAILOVER_DNS_SRV_CONNECTION:
                        return FailoverConnectionProxy.createProxyInstance(conStr);
                    case LOADBALANCE_CONNECTION:
                    case LOADBALANCE_DNS_SRV_CONNECTION:
                        return LoadBalancedConnectionProxy.createProxyInstance(conStr);
                    case REPLICATION_CONNECTION:
                    case REPLICATION_DNS_SRV_CONNECTION:
                        return ReplicationConnectionProxy.createProxyInstance(conStr);
                    default:
                        return null;
                    }
                }
            } catch (UnsupportedConnectionStringException var5) {
                return null;
            } catch (CJException var6) {
                throw (UnableToConnectException)ExceptionFactory.createException(UnableToConnectException.class, Messages.getString("NonRegisteringDriver.17", new Object[]{var6.toString()}), var6);
            }
        } catch (CJException var7) {
            throw SQLExceptionsMapping.translateException(var7);
        }
    }
    }

从而真正的实现了我们使用Mysql驱动时,调用getConnection是获取的mysql的连接。

总结

1、桥接模式最大的特点就是抽象和实现进行分离,大大增加了系统的灵活性,从而产生结构化系统。

2、适用于那些不希望继承或多层次继承来导致系统类的个数急剧增加的系统;一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

3、扩展任一维度,只需要增加不需要修改。符合开闭原则。

参考资料