【设计模式系列】结构型之桥接模式(常用)

2021年6月28日 4点热度 0条评论

前言

不知道大家对于设计模式是个什么样的看法,是认为没必要啊,还是觉得是那种深入到骨子里的优雅。随着工作年限的增加,对设计模式愈发熟悉的前提下,真心觉得,设计模式是真他 * 的优雅。设计模式之于代码,就像房间里的木地板,有无它,都行,但加了木地板的房间更加“优雅”了。总而言之言而总之,设计模式存在的意义,就是在代码到达一定复杂度时,能更好的(装X)扩展、解耦。

废话不多说,为了以后能拥有更强设计代码(装X)的能力,让我们进入本文的正题--桥接模式的学习吧!

正文

桥接模式,说实话,设计模式我来来回回看过几次了,就这个模式,一开始总感觉欠缺点儿理解,各种资料讲的又比较晦涩难懂,每次看都能悟出点儿新东西。

桥接模式官方正式一点讲一般有两个版本(官方讲嘛,大家都懂的,就是一般人看不懂呀

版本一:将抽象和实现解耦,让他们能各自变化。这是GOF《设计模式》中讲的,是不是不懂。。。。

版本二:一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展

两个版本都对,版本一纯学术概念,版本二引入了一点编程的常识,更靠近代码思维了。说多了没用,我们毕竟是要把代码拉出来亮亮相的,不为了(装X)提高代码质量,学了干嘛呢。

举个栗子

理论上,桥接模式最广为人知的用法是JDBC连接那边的设计,但是我不想写。。。所以我举个简单的例子,JDBC希望有人在留言里帮忙解释下,谢谢了。

假如,系统发生告警了,我们有三种告警级别,URGENCY(紧急)、NORMAL(普通)、TRIVIAL(无关紧要),紧急的需要电话通知,普通的发送短信,无关紧要的就发一封邮件吧。

如果不使用桥接模式,我们大体上会怎么写这段代码呢,来亮亮相吧~


代码就不写了,我大致解释下,我们会写个告警通知类,类中的通知方法根据告警级别不同,走不同的if else判断是该打电话还是发短信还是发邮件。所以按照我画的图,就存在两个可能变化的维度了。再对照版本二的解释,我们已经把可以独立变化的维度提取出来了,剩下的就是进行代码设计,分离出抽象和实现,让两者互相解耦。

脑袋还好吗?有没有晕了,晕了建议把上面的理清楚再看下面的吧,因为代码实现很简单,一看就懂,一写就不会。。。。可以去看会儿小说,不行就看个电影,再不行建议取关(开玩笑,千万别放弃我呀)。

下面我们来看看使用设计模式如何实现,先做下分析,我尽量一步步带大家从头到尾设计代码,这样比直接扔一段代码更容易理解。

第一步:理解要做的功能点后,先进行代码设计,抽离出一套流程骨架(抽象)
基于上面的功能,我们需要哪些抽象类(接口)

  1. 通知抽象类Notification-由上游代码直接调用通知类的notify方法进行通知,屏蔽通知内部的逻辑,只要知道发出了通知,具体是发短信还是打电话,不需要管,这是内部逻辑
  2. 发送消息接口SendMsg-使用接口屏蔽实现类的使用,方便代码可扩展性

代码如下:

//抽象
//通知抽象
public abstract class Notification {
    protected SendMsg sendMsg;

    protected Notification(SendMsg sendMsg){
        this.sendMsg = sendMsg;
    }

    /**
     * 通知的方法
     * @param msg
     */
    public abstract void notify(String msg);
}

//发送消息
public interface SendMsg {
    void send(String msg);
}

上面代码逻辑的骨架已经搭建好了,使用的是Notification和SendMsg组合的方式进行配合使用。组合相比于继承而言更轻便而且灵活。这也是桥接模式的精髓所在。

第二步:实现不同抽象的具体实现类

代码如下:

//具体实现 各级告警通知类
//普通级别通知
public class NormalNotification extends Notification {
    public NormalNotification(SendMsg sendMsg) {
        super(sendMsg);
    }

    @Override
    public void notify(String msg) {
        sendMsg.send(msg);
    }
}
//无关紧要级别
public class TrivialNotification extends Notification {
    public TrivialNotification(SendMsg sendMsg) {
        super(sendMsg);
    }

    @Override
    public void notify(String msg) {
        sendMsg.send(msg);
    }
}

//紧急级别
public class UrgencyNotification extends Notification {
    public UrgencyNotification(SendMsg sendMsg) {
        super(sendMsg);
    }

    @Override
    public void notify(String msg) {
        sendMsg.send(msg);
    }
}

// 发送消息实现类
public class TelephoneMsgSender implements MsgSender {  
    private List<String> telephones;  
    public TelephoneMsgSender(List<String> telephones) {    
        this.telephones = telephones;  
    }  
    
    @Override
    public void send(String message) {
    //...
    }
}
public class EmailMsgSender implements MsgSender {  
    // 与TelephoneMsgSender代码结构类似,所以省略...
}
public class SmallMsgSender implements MsgSender {  
    // 与TelephoneMsgSender代码结构类似,所以省略...
}

我们抽象和实现都已经搞定了,怎么进行使用呢,还是看看代码吧

//通过组合方式调用
public class TestCode {
    public static void main(String[] args) {
        /**
         * 使用桥接模式,普通问题通过发email的方式通知
         */
        List<String> emails = new ArrayList<String>();
        emails.add("123456789@163.com");
        Notification notification = new NormalNotification(new EmailSendMsgImpl(emails));
        notification.notify("系统出现普通问题,请检查");
    }
}

上面的代码,将Notification和MsgSender通过组合的方式调用,如果后续代码加了不同的告警级别和发送方式,原有代码完全不需要修改,只需要增加相应的告警级别通知实现类和发送方式实现类,在业务代码中组合使用,完全符合代码的高扩展性要求。我这里都是通过new一个对象的方式来构造类,实际在java项目中很多时候都可以通过注解的方式注入EmailSendMsgImpl或者NormalNotification,我这里只是写个实例,希望大家熟悉的前提下,做到举一反三,灵活变通。

上面写了一段很强的代码,但是桥接模式的弊端也是显而易见的

  • 1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
  • 2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

如此,你学会桥接模式了吗?一起加油吧,有什么好意见,欢迎下方评论留言啊,或者关注我的微信公众号,一样的也会不定时更新技术或者舆论文章的:java精进天路

本文由博客一文多发平台 OpenWrite 发布!