设计模式之 桥接模式

桥接模式(Bridge Pattern),将抽象和实现解耦,让他们可以独立的变化。

只看上面的说明是没法明白桥接模式的,桥接模式也是设计模式中几个较难理解的模式之一。

再详细的说,上面的解释包含两个概念:抽象和实现。

抽象并不是抽象类或接口,是将易变的部分抽离之后的部分,也就是说原本这个抽象并不是抽象,而是带有具体实现的,的但这个具体实现又是存在多种演变的可能,所以将这一部分具体实现抽离,剩下的部分就称之为抽象,而被抽离出去的具体实现,我们就叫做实现。

比如我们Java中用到的JDBC连接数据库做CRUD操作。代码一般是这样的:

Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/tt_db?user=test&password=password";
Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
String query = "select * from test";
ResultSet rs=stmt.executeQuery(query);
......

这里的Class.forName(“com.mysql.jdbc.Driver”)作用就是加载jdbc驱动,就是我们上面说的被抽出来的”实现”的角色,不同的数据库实现了不同的jdbc driver,我们使用哪种数据库,就加载哪种数据库的驱动就可以了。

而DriverManager就是抽象部分,为什么呢,因为在上面加载mysql驱动的时候,mysql将自己注册到了DriverManager中,mysql的源码如下:

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

请看这一行:DriverManager.registerDriver(new Driver())

这里的new Driver()就是new的mysql的Driver,然后注册到了DriverManager中,DriverManager也就有了操作数据库的具体功能了。

所以在JDBC中DriverManager就相当于抽象,而各种数据库的驱动就相当于实现,这种实现是多种多样的,所以将其抽出来,各自实现各自演化,而DriverManager不必关心。

理解了上面的抽象和实现,再来看我们今天的例子。

我们今天就以灯泡和开关为例,来讲解桥接模式。

我们都知道,灯有多种多样,有普通的单色灯,也有可以变色的彩色灯;同样的,开关也有多种多样,有嵌在墙上的普通开关,也有遥控开关。

这里就引出了我们上面说的抽象和实现了。开关相当于抽象,灯相当于实现。我的开关可以控制各种各样的灯,而不应该将开关和灯绑死,比如灯坏了,换一个灯,你肯定不希望连墙上的没坏的开关也换掉。并且如果是绑死的效果,那代码就变成了开关和灯的代码写到一起了,这当然不是一个好的设计,所以这里我们就使用桥接模式来进行改造。

1、灯的抽象定义

package com.itzhimei.study.design.bridge;

/**
 * @Auther: www.itzhimei.com
 * @Description: 灯的抽象定义
 */
public interface ILight {

    /**
     * 灯照明
     */
    void lighting();

    /**
     * 灯变色
     */
    void changeColor();

    /**
     * 灯熄灭
     */
    void goesOut();
}

2、普通单色灯

package com.itzhimei.study.design.bridge;

/**
 * @Auther: www.itzhimei.com
 * @Description: 普通单色灯
 */
public class NormalLight implements ILight {
    @Override
    public void lighting() {
        System.out.println("NormalLight灯被打开了,开始照明...");
    }

    @Override
    public void changeColor() {
        System.out.println("NormalLight普通单色灯,没有变色功能...");
    }

    @Override
    public void goesOut() {
        System.out.println("NormalLight灯被关闭了...");
    }
}

3、可以变换颜色的彩色灯

package com.itzhimei.study.design.bridge;

/**
 * @Auther: www.itzhimei.com
 * @Description: 可以变换颜色的彩色灯
 */
public class ColorLight implements ILight {
    @Override
    public void lighting() {
        System.out.println("ColorLight灯被打开了,开始照明...");
    }

    @Override
    public void changeColor() {
        System.out.println("ColorLight灯变色了,红绿蓝变色中...");
    }

    @Override
    public void goesOut() {
        System.out.println("ColorLight灯关闭了...");
    }
}

4、普通嵌在墙上的开关,开关即能控制普通灯,也能控制彩色灯

package com.itzhimei.study.design.bridge;

/**
 * @Auther: www.itzhimei.com
 * @Description: 普通嵌在墙上的开关
 */
public class NormalSwitch implements ISwitch {

    ILight light;

    public NormalSwitch(ILight light) {
        this.light = light;
    }

    @Override
    public void turnOn() {
        light.lighting();
    }

    @Override
    public void changeColor() {
        light.changeColor();
    }

    @Override
    public void turnOff() {
        light.goesOut();
    }
}

5、测试

package com.itzhimei.study.design.bridge;

/**
 * @Auther: www.itzhimei.com
 * @Description: 测试
 */
public class Client {

    public static void main(String[] args) {
        ILight normalLight = new NormalLight();
        ILight colorLight = new ColorLight();

        ISwitch normalSwitch1 = new NormalSwitch(normalLight);
        normalSwitch1.turnOn();
        normalSwitch1.changeColor();
        normalSwitch1.turnOff();

        ISwitch normalSwitch2 = new NormalSwitch(colorLight);
        normalSwitch2.turnOn();
        normalSwitch2.changeColor();
        normalSwitch2.turnOff();
    }
}

输出:

NormalLight灯被打开了,开始照明...
NormalLight普通单色灯,没有变色功能...
NormalLight灯被关闭了...
ColorLight灯被打开了,开始照明...
ColorLight灯变色了,红绿蓝变色中...
ColorLight灯关闭了...

我们测试代码中,使用的普通开关,控制了两种灯,而且开关不限于控制这两种灯,还可以扩展出各种灯,都能让开关控制,这就是前面说到的“实现”的单独扩展能力。

当然开关也是可以再扩展的,我们可以新建遥控开关来控制灯的开关和颜色,这就达到了抽象和实现的双重独立演变。

类关系图:

总结:

理解桥接模式,一定要理解其中的“抽象”和“实现”的概念和各自承担的职责。

桥接模式将实现,注入到了抽象中,让两种角色都可以各自演变,这是将类之间的依赖关系从继承改为了组合,从而达到了这样的效果。