桥接模式
桥接模式将抽象部分与它的实现部分分离,允许它们可以独立的变化。桥接模式通过使用组合代替继承,有效的减少了类的数目,该模式在GOF95中桥接模式被分类为结构型模式。
- Abstraction:为“抽象部分”的接口,通常该对象中需要维护一个“实现部分”对象的引用。抽象对象里面的方法,需要调用实现部分的对象来完成功能。该对象中的方法通常都是和具体业务相关的方法
- RefinedAbstraction:扩展抽象部分的接口,这些对象中定义了与实际业务相关的方法,这些方法通常会调用Abstraction中的方法,也可能调用Implementor中的方法
- Implementor:为“实现部分”的接口,该接口中的方法不用和Abstraction中的一致。通常Implementor接口提供基本的操作,而Abstraction中定义基于这些基本操作的业务方法(较高层次操作)
- ConcreteImplementor:实现Implementor接口的具体类
所谓“桥接”,就是在两个不同的东西之间建立一个桥梁,在桥接模式中,桥就是Abstraction与Implementor两个维度的组合,它通过引用Abstraction.implementor实现。对于抽象部分的具体类,它需要通过该引用来使用一个实现部分的具体类,这种使用是单向的。
其实在面向接口编程中,广义的桥接模式是无处不在的。在Java中,只要声明了两个具有关联关系的接口,就潜在的使用了桥接模式。
桥接模式的优点:
- 分了了抽象和实现
- 更好的可扩展性
- 可以动态的切换实现
- 可以减少子类的数目
桥接模式的适用场景:
- 开发跨平台的图形和窗口系统
- 需要使用不同的方式改变接口和实现
考虑一下发送通知的业务场景:
- 消息可以分类为普通消息、重要消息、紧急消息等多种严重级别。它们的处理方式不同,重要消息会添加一个标记,紧急消息则在添加标记的基础上进行催促,直到接受者确认
- 消息可以通过多种方式发送出去:用户界面通知、短信、电子邮件、打电话
下面是一个最初的简化设计方案,先实现普通消息,支持电子邮件和用户界面方式发送:
下面我们来实现重要消息,重要消息会在普通消息的基础上添加标记,同样需要支持电子邮件、用户界面方式发送:
现在可以看出问题的端倪了,普通消息、重要消息 vs 电子邮件消息、用户界面消息,这是两个变化维度,通过继承将它们糅合在一起,会导致类爆炸,添加新功能困难。想想我们添加电话短信支持、添加紧急消息支持之后的类图:
可以看到,如果添加一种新的消息发送方式,那么每一种抽象的具体实现(RefinedAbstraction)(普通消息、重要消息、紧急消息)中,都要添加对新发送方式(短信、电话)的支持。
仔细分析可以看到,该业务场景有两个变化维度:消息的类型、消息的发送方式,可以有12种组合。如果引入桥接模式,将消息的抽象和实现两个维度进行解耦,会怎么样呢?
首先,桥接模式需要引入实现接口MessageImplementor,它把具体发送消息的方式从抽象层次AbstractMessage中独立出来,这样不管增加消息类型还是发送方式,都只需要在一个维度上扩展设计就可以了:
在这个设计中,重要消息可以对信息进行预处理,添加标记,然后调用实现部分去发送,紧急消息亦可在内部处理确认和重发的逻辑,这些业务逻辑都是和实现维度没有任何关系的。
驱动程序把上端应用系统和驱动器细节隔离开来,使得应用系统和驱动器可以相互独立的变化,这是桥接模式的体现。以JDBC驱动为例:
当使用不同数据库时,需要切换相应的驱动,DriverManager负责决定使用那种驱动。相应的,用户程序可以依据JDBC API进行独立的开发,不用担心数据库的变化。
- 与策略模式的关系:桥接模式和策略模式优点类似,Implementor相当于策略模式中的Strategy角色。但是策略模式中Context是固定的,桥接模式的Abstraction可以有自己的扩展层次,正是这个扩展层次造就了“两个变化维度”
Leave a Reply