• 【设计模式】桥接模式在开发中的应用


    1. 概述

    桥接模式是一个非常简单的设计模式,可能大家在开发的过程中已经使用到了这种模式而不自知。总的来说,桥接模式最大的作用就是解耦,所谓的解耦,就是通过转换代码的设计,减少类与类,模块与模块之间的依赖紧密程度。

    2.桥接模式

    2.1.两种定义

    桥接模式的主流定义有两种,第一种是GoF的定义:

    将抽象和实现解耦,使得两者可以独立地变化。

    通过这句话,我们能够得到的信息是需要降低抽象实现和依赖紧密程度,以达到两者之间可以独立扩展,互不影响的效果。

    在大多数情况下,抽象一般指的是接口和抽象类,实现就是实现类。在桥接模式中的抽象和实现,无所谓接口、抽象类还是实现类的差异。桥接模式抽象更多的是提供一定的约束,比如一个固定的执行流程,在这个流程中会定义出一些可以自由替换的口子,而实现就是去适配这些口子,让一个固定的执行流程可以得到不同的执行结果。在这个定义下,桥接模式中的抽闲实现,可能各自都是一套类库,或者是一个独立的模块。

    简单的理解,就是抽象会定义要做什么,实现是定义怎么去做,也就理解为插口与插件的关系。
    比如:操作系统文件系统的关系、JDBC中连接与数据库驱动之间的关系、Dubbo协议服务提供/调用者的封装之间的关系,都可以看做是这种抽象与实现分离的桥接关系。

    除了上述的框架或开源代码之外,我们在实际的开发过程中也经常会有类似的需求。
    比如:系统需要接入支付功能的时候,会先定义一套支付流程,在这个流程的节点中会调用不同的三方支付渠道,微信、支付宝等,这里的支付流程就是抽象,支付渠道就是实现。
    再比如:需要实现一个消息发送平台,消息发送的流程就是抽象,不同的消息发送渠道(短信、邮件、推送、站内信等)就是实现,更进一步在短信发送中,短信发送的通用流程就是抽象,调用不同的短信发送渠道商就是实现。


    第二种定义方式更为简单使用更加广泛:

    一个类存在多个独立变化的维度,通过组合的方式让多个维度可以独立进行扩展。

    为什么说这种方式更加广泛呢?
    思考一下就会发现,在第一种定义中抽象和实现完全可以理解为两个不同的维度,这两个维度都是可以独立变化的,也就是说,第二种定义包含了第一种定义,并在这个基础上扩展了,即使不满足抽象与实现的关系,只要有多个不同的维护可以独立变化,并且通过组合/聚合的方式实现不同维度独立扩招,就可以认为是桥接模式。

    举个例子:
    还是上面说的消息发送的需求,在短信发送中,常规的短信在服务商那里有两种类型:通知类、营销类
    对于营销类型的短信,需要在短信的正文末尾拼接上回T退订回TD退订或者类似的文本,而通知类型的是不需要的,这时候就可以将短信类型作为一个维度,短信渠道作为另一个维度,将两个维度通过组合的方式连接起来,就形成了一个简单的桥接模式实现。
    在这个例子中,并没严格的抽象与实现的关系。

    2.2.通用类图/通用代码

    根据上面的描述,不同维度之间通过组合/聚合的方式进行连接,可以得到如下的类图:
    在这里插入图片描述
    通用代码实现:

    • 维度B:
      public interface DimensionB {
          void operation();
      }
      
      public class DimensionBImpl implements DimensionB {
          @Override
          public void operation() {
              System.out.println("维度B的实现");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 维度A:
      public abstract class DimensionA {
      
          private DimensionB dimensionB;
      
          public DimensionA(DimensionB dimensionB) {
              this.dimensionB = dimensionB;
          }
      
          public void operation() {
              dimensionB.operation();
          }
      
      }
      
      public class DimensionAImpl extends DimensionA {
      
          public DimensionAImpl(DimensionB dimensionB) {
              super(dimensionB);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

    2.3.在业务开发中的应用

    参照通用的代码和类图,可以在实际的开发中使用桥接模式,以上面讲的不同短信类型为例,简单的实现一下短信发送的业务。

    • 定义发送渠道的维度:
      public interface SmsChannel {
          void send(String message, String phone);
      }
      
      public class AliSmsChannel implements SmsChannel {
         @Override
         public void send(String message, String phone) {
             System.out.println("使用阿里云短信通道发送消息:" + message + ",给:" + phone);
         }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • 定义短信类型维度:
      public abstract class SmsType {
      
          private SmsChannel smsChannel;
      
          public SmsType(SmsChannel smsChannel) {
              this.smsChannel = smsChannel;
          }
      
          public void send(String message, String phone) {
              smsChannel.send(message, phone);
          }
      }
      
      public class NotificationSmsType extends SmsType {
          public NotificationSmsType(SmsChannel smsChannel) {
              super(smsChannel);
          }
      }
      
      public class MarketingSmsType extends SmsType {
      
          public MarketingSmsType(SmsChannel smsChannel) {
              super(smsChannel);
          }
      
          @Override
          public void send(String message, String phone) {
              message = message + " 回T退订";
              super.send(message, phone);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
    • 测试:
      public class SmsTest {
      
          public static void main(String[] args) {
              SmsType notificationSmsType = new NotificationSmsType(new AliSmsChannel());
              notificationSmsType.send("测试通知类型短信", "18512345678");
      
              System.out.println("=====================================");
      
              SmsType marketingSmsType = new MarketingSmsType(new AliSmsChannel());
              marketingSmsType.send("测试营销类型短信", "18512345678");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

    执行测试的结果如下:

    使用阿里云短信通道发送消息:测试通知类型短信,给:18512345678
    =====================================
    使用阿里云短信通道发送消息:测试营销类型短信 回T退订,给:18512345678


    此时要拓展一个腾讯云的发送渠道,只需要创建一个新的SmsChannle,在SmsType的维度上是不用做修改的,主打的就是不同维度独立拓展。

    public class TencentSmsChannel implements SmsChannel {
        @Override
        public void send(String message, String phone) {
            System.out.println("使用腾讯云短信通道发送消息:" + message + ",给:" + phone);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在做一次测试,对比一下两种渠道:

    public class SmsTest {
    
        public static void main(String[] args) {
            SmsType notificationSmsType = new NotificationSmsType(new AliSmsChannel());
            notificationSmsType.send("测试通知类型短信", "18512345678");
    
            System.out.println("=====================================");
    
            SmsType marketingSmsType = new MarketingSmsType(new AliSmsChannel());
            marketingSmsType.send("测试营销类型短信", "18512345678");
    
            System.out.println("=====================================");
    
            MarketingSmsType tencentMarketingSmsType = new MarketingSmsType(new TencentSmsChannel());
            tencentMarketingSmsType.send("测试营销类型短信","18512345678");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    使用阿里云短信通道发送消息:测试通知类型短信,给:18512345678
    =====================================
    使用阿里云短信通道发送消息:测试营销类型短信 回T退订,给:18512345678
    =====================================
    使用腾讯云短信通道发送消息:测试营销类型短信 回T退订,给:18512345678

    3.总结

    本篇主要讲了桥接模式的概念及实现,桥接模式本身是一个很简单的设计模式,只需要掌握桥接模式的两个最重要的特点即可:

    • 通过组合/聚合进行连接,而不是继承
    • 多个维度可以独立拓展,互不影响

    这两个特点其实也是对组合优于继承的一种体现,掌握这两个特点,可以在日后的开发过程中识别出桥接模式,并使用桥接模式写出高拓展性的代码。

  • 相关阅读:
    docker与宿主机共享内存通信
    不是吧,还有人连Java最强大的技术之一:反射还没搞懂?赶紧码住
    更换掉mac自带PHP,并配置Apache
    leetcode93. 复原 IP 地址
    UI学习-学习内容
    [入门一]C# webApi创建、与发布、部署、api调用
    为什么需要扩展标签属性?(搭建场景体会)
    记录一次阿里云服务器ECS上启动的portainer无法访问的问题
    windows上 adb devices有设备 wsl上没有
    【CSS】基础使用,层级关系,选择器
  • 原文地址:https://blog.csdn.net/qq_38249409/article/details/132803756