桥接模式也是23种设计模式中比较常用的模式之一,在创建型模式、结构性模式和行为型模式分类中,桥接模式归类为创建型模式。
在正式介绍桥接模式之前,先简单的看一个例子,通过例子我们再介绍引入桥接模式。
假设要设计一个跨平台的图片浏览系统,该系统可以正常显示PNG、JPG、GIF等不同格式图片,而且在不同的操作系统上面也可以正常运行。系统会首先将文件解析为不同的像素矩阵(Matrix),然后将像素矩阵显示在屏幕上,不同的操作系统可以调用不同的方法绘制像素矩阵。
首先使用类的继承机制设计如下类图:
Image是一个公共抽象父类,每一种图片格式,如ImageJPG、ImagePNG等都作为Image的直接子类,不同的图片格式具有不同的解析方法,可以得到不同的像素矩阵。由于每一种图像又需要在不同的操作系统中显示,不同的操作系统在屏幕上显示像素矩阵有所差异,因此需要为不同的图像类再提供一组在不同操作系统显示的子类,如ImagePNG又提供了三个子类WindowsPNGImpl、LinuxPNGImpl和MacPNGImpl,分别在Windows、Linux和Mac上面显示。
采用这种方案设计主要存在如下两个问题:
- 由于采用了多层继承,使系统设计中类的个数急剧增加。如果再增加一种图片格式,都需要增加对应操作系统的实现类,实际上在实现上具体层类的实现个数=图片格式数*操作系统分类数。
- 系统扩展比较麻烦,由于每一个具体实现类既包含了图片格式信息又包含了操作系统信息,无论新增加图片格式支持还是操作系统支持都会增加大量具体类。违反了面向对象的设计原则:单一职责、合成复用原则,每个类的职责要单一,多用组合少用继承。
通过上面两个问题的分析,我们会发现主要在两个维度上面的变化,一个是图片格式维度,另外一个是操作系统维度。
如何将各种不同类型的图像文件解析为像素矩阵与图像文件格式本身相关,而如何在屏幕上显示像素矩阵则仅与操作系统相关。而上面的设计方式将图像文件解析和像素矩阵显示这两种完全不同的职责融合在一起,任意一个职责发生改变都需要修改它们,系统扩展困难。
如果是将图像文件格式(对应图像格式的解析)与操作系统(对应像素矩阵的显示)两个维度分离,使得它们可以独立变化,增加新的图像文件格式或者操作系统时都对另一个维度不造成任何影响。那么如何在软件中实现将两个维度分离呢?这就是本文所讲的设计模式-桥接模式。
桥接模式
桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
桥接模式结构类图如下:
理解桥接模式,重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。
- 抽象化就是忽略一些信息,把不同的实体当作同样的实体对待。在面向对象中,将对象的共同性质抽取出来形成类的过程即为抽象化的过程。
- 针对抽象化给出的具体实现,就是实现化,抽象化与实现化是一对互逆的概念,实现化产生的对象比抽象化更具体,是对抽象化事物的进一步具体化的产物。
- 脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。
桥接模式的适用环境
- 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
- 抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
- 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
- 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
- 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
桥接模式优缺点
桥接模式的主要优点是分离抽象接口及其实现部分,是比多继承方案更好的解决方法,桥接模式还提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,实现细节对客户透明,可以对用户隐藏实现细节;其主要缺点是增加系统的理解与设计难度,且识别出系统中两个独立变化的维度并不是一件容易的事情。
在本文中介绍的图片浏览系统,不同图片的格式都可以解析为像素矩阵Matrix,而且在不同的操作系统显示的也是像素矩阵Matrix,实际上相当于对于像素矩阵的的解析和像素矩阵的显示这两个维度进行了区分。这里的不同图片的解析和操作系统对图片的显示的共性是像素矩阵Matrix,上文所说的将对象的共同性质抽取出来,这里所指共同性质就是像素矩阵。在实际应用过程中想要抽取出来一个共同性质类似这里的像素矩阵,可以说是相当有挑战性的工作,而且还要能够识别出共性不同维度的独立变化,更是难上加难的一件事情。但是一旦可以识别出不同维度的变化性质,再进行设计的时候,我们在使用的时候就类似于堆积木了,不同积木的对象组合就会实现不同的效果。
桥接模式VS适配器模式
桥接模式和适配器模式用于设计的不同阶段,桥接模式用于系统的初步设计,对于存在两个独立变化维度的类可以将其分为抽象化和实现化两个角色,使它们可以分别进行变化;而在初步设计完成之后,当发现系统与已有类无法协同工作时,可以采用适配器模式。但有时候在设计初期也需要考虑适配器模式,如Android开发时系统提供的各种Adapter,还有就是那些涉及到大量第三方应用接口的情况。
桥接模式示例
还是针对上面的图片浏览系统进行设计,为了减少所需生成的子类数目,实现将操作系统和图像文件格式两个维度分离,使它们可以独立改变,我们使用桥接模式来重构跨平台图像浏览系统的设计。重新设计的类图如下,可以发现子类数目明显减少了许多,而且扩展也相当方面。