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

1. 概述

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

2.桥接模式

2.1.两种定义

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

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

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

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

简单的理解,就是抽象会定义要做什么 ,实现是定义怎么去做 ,也就理解为插口与插件的关系。

比如:操作系统文件系统 的关系、JDBC中连接与数据库驱动之间的关系、Dubbo协议服务提供/调用者的封装之间的关系,都可以看做是这种抽象与实现分离的桥接关系。

除了上述的框架或开源代码之外,我们在实际的开发过程中也经常会有类似的需求。

比如:系统需要接入支付功能的时候,会先定义一套支付流程 ,在这个流程的节点中会调用不同的三方支付渠道 ,微信、支付宝等,这里的支付流程就是抽象,支付渠道就是实现。

再比如:需要实现一个消息发送平台,消息发送的流程 就是抽象,不同的消息发送渠道 (短信、邮件、推送、站内信等)就是实现,更进一步在短信发送中,短信发送的通用流程 就是抽象,调用不同的短信发送渠道商就是实现。


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

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

为什么说这种方式更加广泛呢?

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

举个例子:

还是上面说的消息发送的需求,在短信发送中,常规的短信在服务商那里有两种类型:通知类、营销类

对于营销类型的短信,需要在短信的正文末尾拼接上回T退订回TD退订或者类似的文本,而通知类型的是不需要的,这时候就可以将短信类型 作为一个维度,短信渠道 作为另一个维度,将两个维度通过组合的方式连接起来,就形成了一个简单的桥接模式实现。

在这个例子中,并没严格的抽象与实现的关系。

2.2.通用类图/通用代码

根据上面的描述,不同维度之间通过组合/聚合的方式进行连接,可以得到如下的类图:

通用代码实现:

  • 维度B:

    java 复制代码
    public interface DimensionB {
        void operation();
    }
    
    public class DimensionBImpl implements DimensionB {
        @Override
        public void operation() {
            System.out.println("维度B的实现");
        }
    }
  • 维度A:

    java 复制代码
    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);
        }
    }

2.3.在业务开发中的应用

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

  • 定义发送渠道的维度:

    java 复制代码
    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);
       }
    }
  • 定义短信类型维度:

    java 复制代码
    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);
        }
    }
  • 测试:

    java 复制代码
    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");
        }
    }

执行测试的结果如下:

使用阿里云短信通道发送消息:测试通知类型短信,给:18512345678

=====================================

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


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

java 复制代码
public class TencentSmsChannel implements SmsChannel {
    @Override
    public void send(String message, String phone) {
        System.out.println("使用腾讯云短信通道发送消息:" + message + ",给:" + phone);
    }
}

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

java 复制代码
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");
    }

}

使用阿里云短信通道发送消息:测试通知类型短信,给:18512345678

=====================================

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

=====================================

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

3.总结

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

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

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

相关推荐
拉里小猪的迷弟7 分钟前
设计模式-创建型-常用:单例模式、工厂模式、建造者模式
单例模式·设计模式·建造者模式·工厂模式
严文文-Chris2 小时前
【设计模式-中介者模式】
设计模式·中介者模式
刷帅耍帅2 小时前
设计模式-中介者模式
设计模式·中介者模式
刷帅耍帅2 小时前
设计模式-组合模式
设计模式·组合模式
刷帅耍帅4 小时前
设计模式-命令模式
设计模式·命令模式
码龄3年 审核中4 小时前
设计模式、系统设计 record part03
设计模式
刷帅耍帅4 小时前
设计模式-外观模式
设计模式·外观模式
刷帅耍帅5 小时前
设计模式-迭代器模式
设计模式·迭代器模式
liu_chunhai5 小时前
设计模式(3)builder
java·开发语言·设计模式
刷帅耍帅5 小时前
设计模式-策略模式
设计模式·策略模式