# 七大软件规划准则
# 规划形式-工厂形式
# 规划形式-单例形式
# 规划形式-原型形式
# 规划形式-策略形式
# 规划形式-责任链形式
# 规划形式-建造者形式
# 规划形式-深度分析代理形式
# 规划形式-门面形式
# 规划形式-装修器形式
# 规划形式-享元形式
# 规划形式-组合形式
# 规划形式-适配器形式

桥接形式( Bridge Pattern )也称为桥梁形式、接口(Interfce)形式或柄体( Handle and Body)模 式,是将笼统部分与它的详细完成部分别离,使它们都能够独登时改变,属于结构型形式。

原文:Decouple an abstraction from its implementation so that the two can vary independently.
翻译:解锅笼统和完成,使得两者能够独立的改变。
也能够了解成:一个类存在两个(或多个)独立改变的维度,咱们经过组合的办法,让这两个(或多个)维度能够独立进行扩展。

桥接形式首要目的是经过组合的办法树立两个类之间的联络,而不是承继。但又类似于多重承继方 案,可是多重承继计划往往违背了类得单一责任准则,其复用性比较差,桥接形式是比多重承继更好的 替代计划。桥接形式的核心在于解耦笼统和完成。
注:此处的笼统并不是指笼统类象接口这种商层概念,完成也不是承继或接口完成。笼统与完成其实指的是两种独立 改变的维度。

桥接形式的UML类图

设计模式-桥接模式
详细代码:

public abstract class Abstraction {
    private final Implementor implementor;
    public Abstraction(Implementor implementor){
        this.implementor = implementor;
    }
    public void function(){
        implementor.implementation();
    }
}
public class RefinedAbstraction extends Abstraction{
    public RefinedAbstraction(Implementor implementor) {
        super(implementor);
    }
    public void refinedFunction(){
        //子类扩展办法
        super.implementor.implementation();
    }
}
public interface Implementor {
    void implementation();
}
public class ConcreteImplementorA implements Implementor{
    @Override
    public void implementation() {
        System.out.println("ConcreteImplementorA");
    }
}
public class ConcreteImplementorB implements Implementor{
    @Override
    public void implementation() {
        System.out.println("ConcreteImplementorB");
    }
}
public class Test {
    public static void main(String[] args) {
        Implementor implementor = new ConcreteImplementorA();
        RefinedAbstraction refinedAbstraction = new RefinedAbstraction(implementor);
        refinedAbstraction.refinedFunction();
    }
}

示例代码

举个比方,家用电器,有各种家用电器比方洗衣机、冰箱、空调,一切的家用电器又对应这自己的品牌,这就别离出来了两个维度一个是家用电器,还一个是品牌两个都能扩展,这儿就很合适运用桥接形式 详细代码如下: 首先把家用电器笼统出来

public abstract class Product {
    protected ICompany company;
    public Product(ICompany company) {
        this.company = company;
    }
    public abstract void printProductInfo();
}

详细完成

public class AirConditioner extends Product{
    public AirConditioner(ICompany company) {
        super(company);
    }
    @Override
    public void printProductInfo() {
        System.out.print(company.getName());
        System.out.print("公司---");
        System.out.println("空调");
    }
}
public class Washer extends Product{
    public Washer(ICompany company) {
        super(company);
    }
    @Override
    public void printProductInfo() {
        System.out.print(company.getName());
        System.out.print("公司---");
        System.out.println("洗衣机");
    }
}

然后便是公司的接口

public interface ICompany {
    String getName();
}

详细完成:

public class Haier implements ICompany{
    @Override
    public String getName() {
        return "海尔";
    }
}
public class Meidi implements ICompany{
    @Override
    public String getName() {
        return "美的";
    }
}

终究运用办法:

public class Test {
    public static void main(String[] args) {
        Product product = new Washer(new Haier());
        Product product1 = new Washer(new Meidi());
        product.printProductInfo();
        product1.printProductInfo();
    }
}

源码中的使用

咱们非常了解的 JDBC API,其间有一个Driver类便是桥接目标。咱们都知道,咱们在运用的时分 经过Class.forName()办法能够动态加载各个数据库厂商完成的 Driver 类。详细客户端使用代码如下, 以 MySQL的完成为例

Class.forName("com.mysql.jdbc.Driver");
Connection conn= DriverManager.getConnection("jdbc:mysql://1ocalhost:3306/test","root","root");
Statement stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("select *from table");

先看Driver这个类

public interface Driver {
    Connection connect(String url, java.util.Properties info)
        throws SQLException;
    boolean acceptsURL(String url) throws SQLException;
    DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
                         throws SQLException;
    int getMinorVersion();
    boolean jdbcCompliant();
    public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}

发现SDK提供的Driver 类是一个接口

设计模式-桥接模式
mysql完成了这个接口 能够看到当加载Driver这个类的时分会执行

DriverManager.registerDriver(new Driver());
public static void registerDriver(java.sql.Driver driver)
    throws SQLException {
    registerDriver(driver, null);
}
public static 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);
}

JDK底层是把mysql 的Driver的实例包装成了 DriverInfo目标.

接下来再看 getConnection办法

@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()));
}
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;
        if (callerCL == null || callerCL == ClassLoader.getPlatformClassLoader()) {
            callerCL = Thread.currentThread().getContextClassLoader();
        }
        if (url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }
        println("DriverManager.getConnection("" + url + "")");
        ensureDriversInitialized();
        // 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.driver.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完成的 driver类回来的。

所以实在操作数据库的代码都是各个厂商根据自己数据库生成的,jdk仅仅一致了接口,所以切换数据库只需要切换驱动就行了。

这其间 DriverManager就相当于 Abstraction角色 Driver便是 Implementor角色,每个厂商对Driver的完成便是ConcreteImplementor角色

桥接形式优缺点

桥接(Bridge)形式的界说是:将笼统与完成别离,使它们能够独立改变。它是用组合联系替代承继联系来完成,然后降低了笼统和完成这两个可变维度的耦合度。
经过上面的讲解,咱们能很好的感觉到桥接形式遵循了里氏替换准则和依赖倒置准则,终究完成了开闭准则,对修改封闭,对扩展开放。这儿将桥接形式的优缺点总结如下:

长处:

  • 笼统与完成别离,扩展能力强
  • 契合开闭准则
  • 契合合成复用准则
  • 其完成细节对客户通明

缺点:

  • 增加了体系的了解与规划难度
  • 需要正确地识别体系中两个独立改变的维度