关注「Java视界」公众号,获取更多技术干货

桥接模式 bridge —— 关注多维场景!

类比于二维平面的坐标,或多维坐标。"桥"将多个维度的接口(或实现)连接起来,构成新的产品。

一、什么是桥接模式?

为什么关注的是多维的?

因为当某个类具有两个或两个以上的维度变化,若仅用继承将无法实现这种需要,或会使得设计变得相当臃肿。

这个多维度变化怎么理解?正好前几天新房到手要装修了,准备买个热水器,就以热水器为例,热水器有很多的品牌(海尔、格力等),每个品牌下按加热原理热水器也有很多类型(燃气、电、太阳能等),也就是说要挑选一个热水器要经过两个维度:一个是选啥品牌、一个是选啥加热类型。

我们来简单实现下,代码如下:

public class SelectHeater {

    public void doSelect(int brand, int type) {
        // 海尔
        if (brand == 1) {
            Console.log("挑选海尔旗下热水器");
            if (type == 1) {
                Console.log("燃气热水器");
            } else if (type == 2) {
                Console.log("电热水器");
            } else if (type == 3) {
                Console.log("太阳能热水器");
            }
        // 格力
        } else if (brand == 2) {
            Console.log("挑选格力旗下热水器");
            if (type == 1) {
                Console.log("燃气热水器");
            } else if (type == 2) {
                Console.log("电热水器");
            } else if (type == 3) {
                Console.log("太阳能热水器");
            }
        }
    }
}

上面的结构,就是基于继承的解决方案,一看就是一堆if、else,具体有以下缺点:

  • 1) 扩展性问题(类爆炸),如果再增加一种加热类型,比如空气能,就需要在各个品牌热水器中新增这个空气能加热方式的热水器,同样如果增加一个热水器品牌,也要在各个加热方式的热水器;
  • 2) 违反了单一职责原则,当增加加热方式时,要同时增加所有品牌的热水器,这样增加了代码维护成本;

那要解决上面的尴尬,就出现了桥接模式,它的解决模型如下:

桥接模式(Bridge模式)基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,将多个维度的变化彻底分离,最后,提供一个管理类来组合不同维度上的变化,通过这种组合来满足业务的需要。从而可以保持各部分的独立性以及应对他们的功能扩展。

说白了,核心实现也就是在A类中含有B类接口,通过构造函数传递B类的实现,这个B类就是设计的

  • Abstraction:定义抽象接口,维护了Implementor即它的实现类ConcreteImplementorA/B,二者是聚合关系,Abstraction充当桥接类;
  • RefinedAbstraction:是Abstraction抽象类的子类;
  • Implementor:是具体实现的接口,Implementor和RefinedAbstraction接口并不一定完全一致,实际上这两个接口可以完全不一样Implementor提供具体操作方法,而Abstraction提供更高层次的调用;
  • ConcreteImplementorA/B:实现Implementor接口,给出具体实现
  • Client:桥接模式的调用者。

二、代码实例

来实现下上面的热水器选择的场景:

@AllArgsConstructor
public abstract class WaterHeater {
    protected HeaterType heaterType;
    
    public abstract void heat();
}
public class HairWaterHeater extends WaterHeater {

    public HairWaterHeater(HeaterType heaterType) {
        super(heaterType);
    }

    @Override
    public void heat() {
        Console.log("海尔热水器准备加热");
        Boolean heat = heaterType.getHeat();
        if (heat) {
            Console.log("海尔热水器开始加热");
        }
        Console.log("加热完毕");
    }
}
public class GeliWaterHeater extends WaterHeater {

    public GeliWaterHeater(HeaterType heaterType) {
        super(heaterType);
    }

    @Override
    public void heat() {
        Console.log("格力热水器准备加热");
        Boolean heat = heaterType.getHeat();
        if (heat) {
            Console.log("格力热水器开始加热");
        }
        Console.log("加热完毕");
    }
}
public interface HeaterType {
    boolean getHeat();
}
public class Gas implements HeaterType {
    @Override
    public boolean getHeat() {
        Console.log("燃气加热!");
        return true;
    }
}
public class Electricity implements HeaterType {
    @Override
    public boolean getHeat() {
        Console.log("电加热!");
        return true;
    }
}
public class Sun implements HeaterType {
    @Override
    public boolean getHeat() {
        Console.log("太阳能加热!");
        return true;
    }
}
public class Client {
    public static void main(String[] args) {
        // 如果选择的是海尔燃气热水器
        WaterHeater hairWaterHeater = new HairWaterHeater(new Gas());
        hairWaterHeater.heat();

        Console.log("=============================================");

        // 如果选择的是格力太阳能热水器
        WaterHeater geliWaterHeater = new GeliWaterHeater(new Sun());
        geliWaterHeater.heat();
    }
}
海尔热水器准备加热
燃气加热!
海尔热水器开始加热
加热完毕
=============================================
格力热水器准备加热
太阳能加热!
格力热水器开始加热
加热完毕

 与上面的ifelse实现方式相比,外部的使用接口的用户不需要关心具体的实现,只按需选择使用即可。

三、适用场景

优点:

(1)分离抽象和实现部分

桥接模式分离了抽象部分和实现部分,从而极大地提高了系统的灵活性。让抽象部分和实现部分独立开来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。

(2)更好的扩展性

由于桥接模式把抽象部分和实现部分分离开了,分别定义接口,这就使得抽象部分和实现部分可以分别独立地扩展,而不会相互影响,大大地提高了系统的可扩展性。

(3)可动态地切换实现

由于桥接模式把抽象部分和实现部分分离开了,所以在实现桥接的时候,就可以实现动态的选择和使用具体的实现。也就是说一个实现不再是固定的绑定在一个抽象接口上了,可以实现运行期间动态地切换。比如海尔燃气、海尔电、格力燃气等自由组合。

(4)可减少子类的个数

根据前面的讲述,对于有两个变化纬度的情况,如果采用继承的实现方式,大约需要两个纬度上的可变化数量的乘积个子类;而采用桥接模式来实现,大约需要两个纬度上的可变化数量的和个子类。可以明显地减少子类的个数。

缺点:

桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

适用场景:

1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。

2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

所以,对于两个独立变化的维度,使用桥接模式再适合不过了。

posted @ 2022-06-25 14:02  沙滩de流沙  阅读(59)  评论(0编辑  收藏  举报

关注「Java视界」公众号,获取更多技术干货