我所理解的设计模式系列·第6篇·我没听说过的桥接模式

2022/02/01 设计模式 共 2717 字,约 8 分钟

事实上,在系统性地学习23种设计模式之前,我从未听说过桥接模式。

现实也是如此,技术有太多太多,能够解决各种各样的问题,但是实际上平常开发需要用到的,或许不到十之五六,一些“旁门左道”甚至你连听都没有听说过,当然这里的“旁门左道”不是贬义词,因为我加了引号——技术人的严谨和求生欲。

然而就是这些你连听都没有听说过的技术,可能在日常工作中已经被你无意识地应用了,桥接模式或许就是此中翘楚。所以说,能够将这些概念总结归纳并提炼出来的人,可真是牛啊。

design-patttern-6-桥接模式-封面

1. 定义

桥接模式是一种结构型设计模式,与代理模式的“为其他对象提供一种代理以控制对这个对象的访问”不同的是,桥接模式的定义是:“将抽象和实现解耦,让它们可以独立变化。”

桥接模式另外还有一种理解:“通过组合代替继承,让不同纬度的抽象可以独立变化。”

可能这两种理解从字面上都比较抽象,但事实上我们在日常开发中已经运用的比较多了。

例如“通过组合代替继承”,我们在企业级分层思想驱动的开发过程中,一般有几种分层:

  • Dao,数据持久层,一些通用的 CRUD,一般以持久化模型为单位
  • Service,服务层,组合一个或多个 Dao 实例,实现跨表的通用业务
  • Controller,视图层,组合一个或多个 Service 实例,实现跨多业务的功能

这里在 Service 和 Controller 层就运用到了桥接模式的思想。就拿 Controller 来说吧,我们通常会在一个 Controller 中注入多个 Service 来完成不同业务的编排与融合的功能,这就是一种组合思想。而 Service 我们一般声明为接口,因为它可能有多种实现,如同步与异步的多种实现、不同硬件平台的不同实现、基于不同入参的多种实现等。对于 Controller 来说,它只关注 Service 提供的能力就够了,与具体实现逻辑无关。

这是不是跟面向接口编程的思想非常像?所以说技术都是相通的。

2. 类图

在桥接模式中,有这样几种角色:

  • 实现角色:定义角色必需的行为和属性,一般是接口或抽象类
  • 具体实现角色:实现角色的具体实现类
  • 抽象角色:定义角色的行为,同时组合一个实现角色,该角色一般是抽象类或接口
  • 修正抽象角色:覆盖抽象角色对实现角色的引用及调用,该角色可以不存在

design-patttern-6-桥接模式-1

3. 示例

就拿上文中说到的同步与异步实现的场景来举一个例子,目前的场景是:用户注册后,营销中心将调用积分中心提供的接口对用户发放注册积分奖励。此时,抽象角色是营销中心,实现角色是积分中心,具体实现角色是同步积分中心实现类,异步积分中心实现类。

说一下这里为什么会有同步和异步的实现类:积分中心为了保证发放积分接口的 TPS 能够达到客户的要求,将其改为异步发放,只记录发放动作,通过 MQ 消息异步执行充值动作,同时基于消息重试保证最终一致性。

抽象角色——营销中心,提供注册有礼方法,其实现代码如下:

public class MarketingCenter {

    private PointCenter pointCenter;

    public MarketingCenter(PointCenter pointCenter) {
        this.pointCenter = pointCenter;
    }

    /**
     * 注册有礼
     *
     * @param userId     用户ID
     * @param pointValue 发放积分值
     */
    public void RegistrationGift(String userId, Long pointValue) {
        System.out.printf("[营销中心]用户[%s]注册成功,发放奖励积分值[%s]\n", userId, pointValue);
        pointCenter.grantPoint(userId, pointValue);
    }
}

实现角色——积分中心抽象接口,提供发放积分方法,其实现代码如下:

public interface PointCenter {

    /**
     * 发放积分
     *
     * @param userId     用户ID
     * @param pointValue 发放积分值
     */
    void grantPoint(String userId, Long pointValue);
}

具体实现角色——同步/异步实现类代码如下:

public class SyncPointCenter implements PointCenter {

    @Override
    public void grantPoint(String userId, Long pointValue) {
        System.out.printf("[同步积分中心实现类]用户[%s]发放积分值[%s]\n", userId, pointValue);
    }
}

public class AsyncPointCenter implements PointCenter {

    @Override
    public void grantPoint(String userId, Long pointValue) {
        System.out.printf("[异步积分中心实现类]用户[%s]发放积分值[%s]\n", userId, pointValue);
    }
}

最后编写测试代码:

public class Test {

    public static void main(String[] args) {
        SyncPointCenter syncPointCenter = new SyncPointCenter();
        MarketingCenter marketingCenter = new MarketingCenter(syncPointCenter);
				// 注册有礼
        marketingCenter.RegistrationGift("1", 100L);
    }
}

// output:
// [营销中心]用户[1]注册成功,发放奖励积分值[100]
// [同步积分中心实现类]用户[1]发放积分值[100]

4. 使用场景

桥接模式的使用场景是:

  • 不想使用继承的情况:桥接模式是以组合代替继承,当然适用于不想使用继承的情况
  • 抽象角色不稳定:这也是不使用继承的原因
  • 方便扩展:如果有新的需求,只需要再组合一个新的实现角色即可
  • 面向接口编程,不感知具体实现:抽象角色只需要组合实现角色接口/抽象类,不需要感知具体的实现逻辑,这与 Java 虚拟机的实现思想很类似—— JVM 保证了不同操作系统运行一段代码的结果是一致的,即“一次编译,到处运行”

5. 小结

本文讲述了桥接模式,它的定义是——将抽象和实现解耦,让它们可以独立变化。我们通常会以组合的形式来实现桥接模式,这很好地避免了多层继承带来的一系列问题。桥接模式在日常开发中其实十分常用,当然对于其可拓展性的应用,大多数人没有进行很好地实践。

6. 参考资料

  • 《设计模式之禅》(第2版)

最后,本文收录于个人语雀知识库: 我所理解的后端技术,欢迎来访。

文档信息

Search

    Table of Contents