49 | 桥接模式:如何实现支持不同类型和渠道的消息推送系统?

2020-02-24 王争
《设计模式之美》
课程介绍


讲述:冯永吉

时长:大小7.44M


上一节课我们学习了第一种结构型模式:代理模式。它在不改变原始类(或者叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。代理模式在平时的开发经常被用到,常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。
今天,我们再学习另外一种结构型模式:桥接模式。桥接模式的代码实现非常简单,但是理解起来稍微有点难度,并且应用场景也比较局限,所以,相当于代理模式来说,桥接模式在实际的项目中并没有那么常用,你只需要简单了解,见到能认识就可以,并不是我们学习的重点。
话不多说,让我们正式开始今天的学习吧!

桥接模式的原理解析

桥接模式,也叫作桥梁模式,英文是 Bridge Design Pattern。这个模式可以说是 23 种设计模式中最难理解的模式之一了。我查阅了比较多的书籍和资料之后发现,对于这个模式有两种不同的理解方式。
当然,这其中“最纯正”的理解方式,当属 GoF 的《设计模式》一书中对桥...

展开全文
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。

精选留言

  • 业余爱好者
    2020-03-04
    桥接看着就像是面向接口编程这一原则的原旨---将实现与抽象分离。让我迷惑的是,让两者独立变化的说法,接口不是应该稳定吗,为什么要变化?

    多个纬度独立变化那个解释倒是比较容易理解。文中举的警报的例子很贴切。紧急程度和警报的方式可以是两个不同的纬度。可以有不同的组合方式。这与slf4j这一日志门面的设计有异曲同工之妙。slf4j其中有三个核心概念,logger,appender和encoder。分别指这个日志记录器负责哪个类的日志,日志打印到哪里以及日志打印的格式。三个纬度上可以有不同的实现,使用者可以在每一纬度上定义多个实现,配置文件中将各个纬度的某一个实现组合在一起就ok了。

    行文至此,开头的那个问题也有了答案。一句话就是,桥接就是面向接口编程的集大成者。面向接口编程只是说在系统的某一个功能上将接口和实现解藕,而桥接是详细的分析系统功能,将各个独立的纬度都抽象出来,使用时按需组合。
    展开
    9
    143
  • zhengyu.nie
    2020-04-29
    举个很简单的例子,现在有两个纬度
    Car 车 (奔驰、宝马、奥迪等)
    Transmission 档位类型 (自动挡、手动挡、手自一体等)
    按照继承的设计模式,Car是一个Abstract基类,假设有M个车品牌,N个档位一共要写M*N个类去描述所有车和档位的结合。
    而当我们使用桥接模式的话,我首先new一个具体的Car(如奔驰),再new一个具体的Transmission(比如自动档)。然后奔驰.set(手动档)就可以了。
    那么这种模式只有M+N个类就可以描述所有类型,这就是M*N的继承类爆炸简化成了M+N组合。

    public abstract class AbstractCar {

      protected Transmission gear;
      
      public abstract void run();
      
      public void setTransmission(Transmission gear) {
        this.gear = gear;
      }
      
    }

    所以桥接模式解决的应该是继承爆炸问题。
    可以看作是两个abstract组合在一起,独立去拓展,在运行之前将两个具体实现组合到一起。
    遵循以下原则
    ·依赖倒置原则
    ·迪米特法则
    ·里氏替换原则
    ·接口隔离原则
    ·单一职责原则
    ·开闭原则

    展开

    作者回复: 是我说的第二种理解方式

    11
    120
  • 下雨天
    2020-02-24
    课后题:可以考虑使用建造者模式来重构!参见46讲中

    建造者使用场景:
    1.构造方法必填属性很多,需要检验
    2.类属性之间有依赖关系或者约束条件
    3.创建不可变对象(此题刚好符合这种场景)
    展开
    8
    66
  • 蹦哒
    2020-06-13
    老师请问是否可以这样理解:代理模式是一个类与另一个类的组合,桥接模式是一组类和另外一组类的组合

    作者回复: 有点那个意思~👍

    
    49
  • 松花皮蛋me
    2020-02-24
    这个模式和策略模式的区别是?
    11
    26
  • 忆水寒
    2020-02-24
    参数不多的情况可以在构造函数初始化,如果参数较多 就可以使用建造者模式初始化。
    
    19
  • 李朝辉
    2020-03-06
    一点思考:如果notification类针对一次告警,需要同时在微信、电话、邮件上发送通知,当前的Notification类定义就没办法满足条件了,可以将组合的MsgSender变成一个list或者set,将不同渠道的sender注册进去,这样,就可以在调用notify的时候,将list或set内的sender,都调用一遍send
    3
    16
  • 攻城拔寨
    2020-02-28
    我觉得桥接模式解释成: 一个类存在不同纬度的变化,可以通过组合的方式,让它们独自扩展。
    栗子:白色圆形,白色正方形,黑色圆形,黑色正方形。 抽象成 颜色 跟 形状 两个纬度去搞,就是桥接模式啦。
     至于 jdbc 的,我水平有限啊,还是理解不了~
    6
    13
  • 小晏子
    2020-02-24
    “emailAddresses、telephones、wechatIds 中的数据有可能在 Notification 类外部被修改”的原因是对外暴露了修改接口set*,如果不想被修改那么就不要暴露set接口,这样的话初始化这些email,telephone和wechat的工作就放到构造函数里,用构造函数去初始化这些变量,这样初始化之后正常情况下外面没法修改。
    
    12
  • 冰激凌的眼泪
    2020-02-25
    在桥接模式中,所谓抽象就是要干什么,所谓实现就是怎么去干,但是这俩是没有抽象与实现的意义的。
    2
    9
  • humor
    2020-02-26
    我觉得Notification没有必要再分成三个子类了,直接传入MsgSender子类就可以了吧
    3
    6
  • 峰
    2020-02-24
    set 方法里拷贝一份值,而不是直接赋值。
    
    5
  • Ryan24G
    2021-01-08
    每次举得例子都无比难理解,能不能先举个简单有共性的例子让大家知道是怎么回事,然后再加一些实用实战的源码之类的进行拓展?
    
    4
  • Heaven
    2020-03-10
    对于题目,由于这三个类之间不具有任何的依赖关系,所以没必要去使用我们的建造者模式,有些过度设计,直接使用构造函数就可以了
    对于桥接模式,我个人认为,就是一个类中有多个属性,我们可以将这些属性分开来设计,彼此之间不具有关联,这些个属性就可以认为是多个维度,可以说,就是所谓的抽象,而这个类,就是讲这些属性连接起来的桥,这就是桥接模式,也就是真正实现这个类时候,属性需要注入真正的实现类.例如:一个商品可以对应的多种属性,不同属性就是多个维度,这些属性在这个商品中可以是个抽象的概念,但是在扩展的时候,扩展出了实现类,而这个商品负责连接他们,做到了真正意义上的解耦.突然想到了小岛秀夫的死亡搁浅,所谓的桥接型游戏
    
    3
  • tt
    2020-02-28
    类庞大,在单一职责原则的基础上,是因为
    1、非功能需求,如缓存、日志、鉴权等需求引起的,这时使用代理模式,增强原始类或函数的非相关功能,调用代理类,实现类规模的降低。

    2、功能类需求,但还要求可扩展性。使用桥接模式,将可扩展的部分委托给实现类。比如JDBC使用桥接模式使得JDBC可扩展、可配置;Notification类使用桥接模式实现不同的消息发送渠道的可扩展。

    3、拓展原功能的某一侧面,如缓存。这里侧面就是某个方法,增强这个接口,首先它必须有实现,所以采用继承抽象类而不是实现某个接口的方式。使用装饰器模式,覆盖这个接口,强化某一个侧面。

    上述后两点,都是和原功能需求相关,被委托或组合的类都实现或继承自同一个接口或抽象类,这一点将其和一般的组合区分开来,一般的组合不要求被组合的类和原类继承自同一个父类。
    展开
    
    3
  • 陈尧东
    2020-02-24
    老师,有个疑问,重构后SevereNotification类依赖的都是接口MessageSender,没有依赖具体的实现,哪其它几个XxxNotification实现与其有何区别?
    3
    3
  • Jxin
    2020-02-24
    1.防止引用类型成员变量内的属性或元素被外部程序修改。可以在set时赋值 目标参数的深拷贝对象,以保证当前引用类型成员变量的作用范围尽在当前类(同时,对引用类型成员变量的所有修改操作,也应以对象方法的方式,限定在当前类的对象上)。

    2.防止成员变量本身被修改。为成员变量加final标识(增强语意),如此一来,其赋值操作将被限制在构造器构造的时候完成,不会出现被二次修改的场景。
    
    3
  • test
    2020-02-25
    返回不可变对象
    
    2
  • 黄林晴
    2020-02-24
    打卡
    
    2
  • 每天晒白牙
    2020-02-24
    建造者模式
    
    2