设计模式实战-桥接模式,想做月老吗?

时间:2022-07-22
本文章向大家介绍设计模式实战-桥接模式,想做月老吗?,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

桥接模式 (Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体 (Handle and Body) 模式或接口 (Interface) 模式。

Bridge 的意思就是 “桥梁”,好比现实生活中的桥梁,它的存在就是将河流两侧的东西给连接起来,应用到软件里面 Bridge 就是将类的功能层次结构与实现层次结构连接起来。

2、类的功能层次结构 Vs 实现层次结构

这里解释下什么叫做功能层次结构和实现层次结构。比方说,我们有一个类叫做 Abstraction,当我们想在 Abstraction 上增加新的功能时,一般我们会新建一个子类继承 Abstraction,比如叫 RefinedAbstraction 类,新类一般还会增加自定义的一些功能方法,这种就构成了一个简单的类层次结构:

这种层次机构具有如下特点:

  • 父类具备基本功能
  • 子类在父类基础上添加新的功能

这种就称为” 类的功能层次结构 “,当然,当我们继续添加新的功能时,一般我们选择某一功能层级的类然后继承它,在其基础上进行新功能的追加。(类的层级太深反而不好)

什么又叫做类的实现层次机构呢?我们都知道抽象类的作用,抽象类一般声明一套接口(API),然后子类继承并实现这套接口,简单来说就是父类定义接口,子类实现接口。这种接口层面的” 实现 “关系就会产生一套层次结构,比如如下:

这种基于” 实现 “层级有如下特点:

  • 父类声明抽象方法定义相关接口(API);
  • 子类通过具体方法来实现接口(API)。

这种因” 实现 “产生的结构层级就叫做” 类的实现层级 “。

讲了这么多,Bridge 模式就是为了将类的功能层次结构与实现层次结构连接起来,充当中间桥梁的作用。

3、Bridge 模式的 UML 类图

上面说了那么多,可能有点云里雾里,下面是 Bridge 模式的类图,通过图来理解可能会好一些:

4、组成角色

桥接模式中包含了几种角色,分别是:

  • 抽象化(Abstraction):该角色位于属于 “类的功能层次结构” 的最上层,用于定义抽象接口,一般是抽象类而不是抽象接口。其内部往往包含一个实现类接口实例(Implementor),使用委托方式进行内部调用;
  • 改善后的抽象化,或者叫补充抽象类(RefinedAbstraction):该角色用于补充 Abstraction 功能而存在,通常情况下不再是抽象类而是具体的实现类,在内部可以直接调用 Implementor 中的业务方法;
  • 实现者(Implementor):该角色位于 “类的实现层次结构” 的最上层,定义了用于实现 Abstraction 角色的接口(API),这里的接口并非要和 Abstraction 中定义的完全一致,Implementor 只对这些接口进行声明,具体实现还是要交给子类。通过委托,在 Abstraction 中,不仅可以调用自己方法,还可以调用到 Implementor 中定义的方法;
  • 具体实现者(ConcreteImplementor):该角色用于实现 Implementor 角色中定义的接口,不同的实现类提供不同的业务处理方法,程序运行时,ConcreteImplementor 将替换 Abstraction 中的 Implementor,提供给抽象类具体的业务操作方法。

5、使用实例

下面我们通过一个例子来理解下 Bridge 模式,我们都知道手机品牌有很多,每种手机上面又包含各种各样的软甲,我们如何表示” 在手机上运行软件 “这一活动?手机的品牌和软件是两个维度上的东西,可以类比为上面的” 功能层级 “和” 实现层级 “,桥接模式的作用就是将这些不同的实现独立开来,从而应对不断地变化,下面是具体的类图设计:

这里我们定义抽象类 MobilePhoneAbstraction,表示手机抽象类,该类自带 run 方法来 “运行各种软件”,示例代码如下:

public abstract class MobilePhoneAbstraction {

    protected SoftImplementor impl;

    public abstract void run();

    public MobilePhoneAbstraction(SoftImplementor impl) {
        this.impl = impl;
    }
}

MobilePhoneAbstraction 为我们定义的类的功能层次结构的顶层,内部包含对 SoftImplementor 的引用,SoftImplementor 即是我们定义的手机软件的抽象类,示例代码如下:

public abstract class SoftImplementor {

    public abstract void rawrun();

}

这里我们声明 rawrun 方法供子类实现,同时又委托给 MobilePhoneAbstraction 供其调用,下面是手机品牌的实现类:

public class HuaWeiMobilePhone extends MobilePhoneAbstraction{

    public HuaWeiMobilePhone(SoftImplementor impl) {
        super(impl);
    }

    @Override
    public void run() {
        // 使用委托:调用HuaWeiMobilePhone的run时,实际调用的是SoftImplementor的rawrun
        this.impl.rawrun();
    }

    public void gpuTurborRun(){
        System.out.println("GPU Turbo Running start...");
        this.run();
        System.out.println("GPU Turbo Running end...");
    }
}

这里子类添加自己独特的功能方法”GPU Turbo“ 方法类运行软件,进行 run 调用,可以看到实际调用的是 SoftImplementor 中的 rawrun 方法,该方法为我们自定义实现的基于抽象的 SoftImplementor,接下来看下 SoftImplementor 示例代码:

public abstract class SoftImplementor {

    public abstract void rawrun();

}

这里声明 rawrun 接口(API),实现的子类有两个,分别是游戏软件 ——GameSoft,和聊天软件 ——ChatSoft,相关代码分别如下:

public class ChatSoft extends SoftImplementor{

    @Override
    public void rawrun() {
        System.out.println("ChatSoft rawrun...");
    }
}
...

public class GameSoft extends SoftImplementor {

    @Override
    public void rawrun() {
        System.out.println("GameSoft rawrun...");
    }
}

最后是我们的测试类:

MobilePhoneAbstraction m1 = new HuaWeiMobilePhone(new ChatSoft());

HuaWeiMobilePhone m2 = new HuaWeiMobilePhone(new GameSoft());

m1.run();
m2.run();

m2.gpuTurborRun();

运行结果如下:

ChatSoft rawrun...

GameSoft rawrun...

GPU Turbo Running start...
GameSoft rawrun...
GPU Turbo Running end...

由于 m1、m2 都属于 MobilePhoneAbstraction 的类的实例,因此我们可以调用它们的 run 方法,而 m1、m2 又都属于 HuaWeiMobilePhone 的实例,所以我们还能调用其 gpuTurborRun 方法,比如后期我们 run 实现有所变动,那么只需要修改 main 方法和 SoftImplementor 的实现类即可,其它代码就可以保持不动了。

通过 MobilePhoneAbstraction 中的 impl 桥接,就实现了抽象与行为实现的分离,这种就是桥接模式的存在意义。

6、优缺点

桥接模式的优缺点总结如下:

  • 抽象与实现相分离:抽象与实现相分离,从而让抽象与实现分别独立开来,分别定义接口,有助于系统分层及产生更好的结构化系统;
  • 更好的拓展性:系统拓展时,因为抽象与实现已经分别独立,所以可以进行分别拓展不会相互影响,从而大大提高系统拓展性。

7、总结

桥接模式是解决一个系统有多个变化维度的一种设计模式,分离了抽象接口与实现部分,提高了系统的可拓展性,符合开闭原则,相反地,也增加了系统的理解与设计上的复杂性。