设计模式之桥接模式

开始涉足硬件

小明公司最近准备做硬件了,他们开始四处去寻找订单,但是效果不是太明显,不过,功夫不负有心人,一家名为长黑的电视机厂商被他们打动,电视机厂商准备将他们的遥控器外包给小明他们公司。小明很开心,又可以开始做新的项目了

电视机厂商遥控器对电视的控制都遵循统一的标准,所以长黑电视机厂商将这个标准和他们对标准的实现给了小明他们

遥控器的统一标准:

public interface Control {
    void on();
    void off();
    void setChannel(int channel);
}

长黑电视对电视的控制是现实:

public class CHControl implements Control {
    @Override
    public void on() {
        System.out.print("Open CH TV");
    }
    @Override
    public void off() {
        System.out.print("Off CH TV");
    }
    @Override
    public void setChannel(int channel) {
        System.out.print("Set CH TV To " + channel);
    }
}

只要小明的公司通过CHControl的方法就可以操作长黑的电视机了,现在小明的公司只为长黑电视机生产一个功能极简的遥控器,只有三个按钮,分别是开/关、上一个节目、下一个节目,小明的主管实现把遥控器的接口类编写好了:

public interface TvControl {
    void onOff();
    void preChannel();
    void nextChannel();
}

小明要做的,就是将这个具体的遥控器实现,小明想了想,他要做的事就是,实现TvControl并且通过CHControl来控制电视,所以思路非常清晰,然后小明很快实现了这个遥控器:

public class CHTvControl extends CHControl implements TvControl {

    private boolean on = false;
    private int channel = 1;

    @Override
    public void onOff() {
        if (on) off();
        else on();
    }

    @Override
    public void preChannel() {
        if (on) setChannel(--channel);
    }

    @Override
    public void nextChannel() {
        if (on) setChannel(++channel);
    }
}    

小明高兴的开始测试遥控器了:

public class Client {
    public static void main(String[] args){
        CHTvControl chTvControl = new CHTvControl();
        chTvControl.onOff();
        chTvControl.nextChannel();
        chTvControl.nextChannel();
        chTvControl.preChannel();
        chTvControl.onOff();
    }
}

测试结果

Open CH TV
Set CH TV To 2
Set CH TV To 3
Set CH TV To 2
Off CH TV

小明找到主管,询问主管是否需要再做修改,主管看了看说,小明,我们仔细想一下你的代码如果要扩展会有什么问题。现在有新的电视机厂商找到我们,遥控器的样式是不变的,也是和之前的遥控器功能一样,你就必须要再针对新的电视机厂商再实现新的遥控器类

比如,狗熊电视机厂再找到我们,遥控器仍然只有三个按钮,那么必须产生一个新的遥控器类:

public class GXTvContrlo extends GXControl implements TvControl {
    // 省略实现…………
}

还有一个问题,现在要针对长黑电视机生产新的遥控器了,我们也必须再实现新的遥控器,我们可以看到,我们现有的设计里面,变动的两个地方,一是电视机厂商,二是遥控器的种类,所以我们的类可能会有N*N个(N个电视机厂商,N种遥控器)。那么应该怎么办呢?我们又要认识一个新的朋友了,它可以帮助我们避免这种变化的问题,它就叫桥接模式,我们先来认识一下它,然后再用它帮我们解决问题

定义

通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变

桥接模式

角色

  • 抽象化角色
  • 修正抽象化角色
  • 实现化角色
  • 具体实现化角色

注:抽象化角色包含一个实现化角色的引用

解决问题

看完桥接模式的定义,我们现在来用桥接模式中各个角色对应我们上面的类,我们应该被抽象的应该是哪一个类呢?抽象化角色是用来被继承的,继承一般是扩展类的功能,在上面的例子中,我们需要扩展的类应该是遥控器,因为遥控器只会从简易化向复杂化演进,所以我们应该抽象画一个基本的遥控器父类,然后需要不同的遥控器时,我们再来扩展它。相应的,我们的实现化角色应该是我们对同步电视的控制类,对电视的控制有统一的标准,不同的厂家会实现这个标准,然后我们针对不同的厂家调用不同的实现,所以,实现化角色应该是我们的对电视的控制类。现在,一切都清晰了,开始做我们新的遥控器吧!

重新定义我们的抽象化角色,也就是我们的遥控器基类:

public abstract class TvControl {

    Control control;

    public TvControl(Control control) {
        this.control = control;
    }

    abstract void onOff();
    abstract void nextChannel();
    abstract void preChannel();
}    

我们实现遥控器时,最简单的功能也必须包括这三个功能,如果需要新加功能,只需要再扩展相应的功能即可,现在我们实现一个具体的遥控器:

public class SimpleTvControl extends TvControl {

    private boolean on = false;
    private int channel = 1;

    public SimpleTvControl(Control control) {
        super(control);
    }


    @Override
    void onOff() {
        if (on) control.off();
        else control.on();
        on = !on;
    }

    @Override
    void nextChannel() {
        if (on) control.setChannel(++channel);
    }

    @Override
    void preChannel() {
        if (on) control.setChannel(--channel);
    }
}

使用新的遥控器:

public class Client {
    public static void main(String[] args) {
        CHControl chControl = new CHControl();
        SimpleTvControl simpleTvControl = new SimpleTvControl(chControl);
        chTvControl.onOff();
        chTvControl.nextChannel();
        chTvControl.nextChannel();
        chTvControl.preChannel();
        chTvControl.onOff();
    }
}

结果是和上面一样的:

Open CH TV
Set CH TV To 2
Set CH TV To 3
Set CH TV To 2
Off CH TV

我们看到,用了桥接模式的新设计,不会出现像上面那样的问题了,同一款遥控器,我们只需要包含不同的电视控制实现就可以,比如我们的简易遥控器,只要在构造SimpleTvControl遥控器时,放入不同的电视控制类即可。同样的,如果我们的简易遥控器需要扩展,只需继承当前的SimpleTvControl,然后扩展新功能就行了,对电视控制类不会有影响

桥接模式和策略模式

仔细分析,我们的桥接模式貌似只是在策略模式的基础上加了一个继承,的确如此,那么我们看看两者的区别:

目的不同

桥接:让接口实现和抽象层可以分别演化,提高移植性
策略:将复杂的算法封装起来,从而便于替代不同的算法

使用方式不同

桥接:利用已有的类和方法
策略:扩展或者修改,并提供动态配置

重点不同

桥接:强调接口对象提供基本的操作
策略:强调接口对象提供的是一种算法

什么时候使用桥接模式

桥接模式的目的是为了分离抽象和实现,便于两者的独立变化,所以当有多维度分类时,而且每一种分类又有可能变化,就可以考虑使用我们的桥接模式了

坚持原创分享,您的支持将鼓励我不断前行!