首页 > 代码库 > 例说桥接模式(Bridge Pattern)

例说桥接模式(Bridge Pattern)

官方定义

   将抽象部分和实现部分相分离,使他们可以独立的变化 —— GOF
 
   看GOF的书籍有这样的感觉,每个汉字都认识,连成句子就是读不懂,这就是抽象的魅力,把很多具体的现象抽象成一句话,然后告诉我们这就是某某模式。类似于博士论文,初看高深,翻译成白话就亲民很多,最近RTFSC,遇到桥接模式比较多,把查阅的资料和心得整理下,共同学习。
 

场景

   定义里说,抽象和实现相分离,使其可以独立变化。可以说这是对现有继承关系的颠覆,被广泛运用的继承,抽象父类和实现子类间有着较强的耦合关系,这里的抽象是指对该类本质的抽象,通常说来,抽象是稳定的,而具体实现则是灵活多变的,总不能对应于每一种变化,我们都要新去新建一个子类来表示吧,理论太晦涩,还是举例说明吧。
 

实例

   五一将至,有必要制定一个旅游计划,简单起见,一份旅游计划需要包括以下几个方法,去景点(arrive),从景点回(depart),在景点游玩(enjoy),这三个方法就是稳定的,基本上任何一份旅游计划都必须要有这三个方法,这就是GOF定义里的“抽象部分”,任何一份具体景点的旅游计划都必须要包含以上三个部分,假定制定了两份具体计划,青岛和三亚,那么一个简单的类结构图便产生了
     

   看起来真不错,一份包含目的地的旅游计划,抽象部分的三个方法也很清晰,只需要在子类里分别把目的地设置成青岛和三亚,基本骨架就完成了,以目的地来分(维度1),我们可以产生有限的子类,这在逻辑上也是合理的,每个地方制定一套旅游计划,接着进入实现部分。
    
   抽象的部分搭建好以后,实现需要考虑的第一个问题,使用什么样的交通工具呢(维度2),毕竟对于arrive、depart这两个抽象方法来说,不同的交通工具意味着不同的具体实现,以去青岛为例,乘坐火车还是飞机,两个抽象方法的实现有着天壤之别,去三亚也是如此,于是,按照传统的思路,又有了下面的类关系。

 

   看到这个类结构后,隐约感觉类爆炸仿佛在向你招手,仅仅以交通工具这点来考虑,因为不同的交通工具影响到了抽象部分的实现,我们就新建了很多子类,那如果再考虑出游方式(维度3)呢,选择自由行还是跟团游,不同的出游方式影响着enjoy方法的具体实现,那是不是继续新建子类呢,如果还有其它方面的考虑(维度4、维度5),这个思路显然出问题了。
    
   重新梳理下逻辑关系,在考虑具体实现时,对于每一个新的维度(如交通工具、出行方式),不同选择都意味着抽象方法的实现会有变化,抽象部分就是arrive、depart和enjoy,对于一份旅游计划来说,这三个部分是一般的稳定的,所以被抽象出来,但能够影响其实现的具体维度太多了,我们需要把抽象部分和那些会影响其具体实现的维度部分分离开了,面向接口而非继承,得到了下面的图示结构
  

 

   可以看到,在旅游计划类中,加入了代表交通工具的变量,类型时IVehicle接口,对于交通工具这个维度来说,无论vehicle变量怎么变,抽象部分都不再关心了,具体到青岛旅游计划这样的子类中,只需要调用特定交通工具的具体实现即可。飞机和火车作为两个特定的交通工具子类,实现接口IVehicle中的impl(),抽象部分的arrive、depart中,仅需关联上IVehicle.impl(),这就封装维度2(交通工具)的变化,使得抽象部分和实现部分相分离。
 
   如果有新的维度需要考虑,比如是自由行还是跟团游,不同的出游方式也使得抽象部分的enjoy方法有着不同实现,桥接模式符合OCP原则,对于新维度的变化请求,扩展也比较容易。
 
   可以看到面向接口的编程实现了抽象部分和实现部分的分离,旅行计划表可以将每个可能会影响具体实现的维度因素都组合进自己的基类中,在具体的子类中,如青岛旅游计划表,再选择是乘坐飞机还是火车、跟团还是自由行,实现部分被隔离开来同时也有利于扩展,比如添加一个新的维度或者是为旧维度添加新的实现类,这些都可以很容易达到。
 
   这种最终实现很熟悉,这不就是多用组合少用继承么原则么,怎么发展成桥接模式了,这其中有个很重要的部分就是这些被组合的维度都是会影响的抽象部分的具体实现的,比如旅游计划类中的抽象部分,arrive、depart和enjoy,这些抽象的具体实现都是依赖于被组合的维度的,正是由于被组合维度的灵活多变有可能引起类爆炸,我们才采用组合的手段来隔离,面向接口编程是手段,目的是隔离变化,同时也因为抽象部分与被组合的维度存在依赖关系,这才是构成了桥接模式的基本结构。
 
 

 

  

实例代码

   注意,示例代码中arrive、depart、enjoy都不是abstract方法,这不影响其作为抽象部分的重要作用,GOF中所提的抽象部分是指本质的、稳定的部分,不一定必须abstract。
   实例代码已上传至http://download.csdn.net/detail/klpchan/7277539,说明性代码,不够规范,仅供参考。
 
   参照前面所举实例,解释一些理论知识,有兴趣同学参考《设计模式:可复用面向对象软件的继承》

适用性

  1) 不希望抽象和实现部分由固定绑定关系,可以自由切换。以青岛旅游计划为例,arrive的时候你选择了火车,depart时你可能想飞。
  2) 抽象和实现部分都可以通过生成子类的方式进行扩充,你可以生成更为详细的旅游计划,包含更多抽象部分,交通工具的选择上,坐游轮去青岛也不错哦~
  3)对一个抽象及实现部分的修改不会影响客户,对客户隔离了变化,较易理解。
   

结构

 
   在该结构中,Abstraction就是上述例子中的旅游计划基类,Implementor是具体维度的接口,比如交通工具,
ConcreteImplementroA和ConcreteImplementorB则对应了不同的具体的出行方式,RefinedAbstraction则表示一份具体目的地的出行计划,如青岛旅游计划,旅游计划中的方法会调用交通工具的具体实现,在该机构中,Implementor具体实现所提供的方法粒度较小,Abstraction中的操作往往由一系列的Implementor具体实现构成。

效果

   1) 分离接口和实现
   2) 提高可扩展性
   3) 实现细节对客户透明
      前例中均有所提及。

和其它模式关系

  桥接模式在结构上和策略模式有点类似,但应用场景及条件差异较大,抽象工厂模式可以用来配置一个桥接模式的组合维度。

应用场景

   一个关于桥接模式的经典应用场景在平台移植上,一款FPS游戏有诸如move,shoot等基本方法,这些基本方法都是要调用平台的API来实现的,比如图像渲染、声效播放等,具体在不同的平台(维度),比如Windows和Linux,Android或者Ios,提供有不同的API,通常说来move或者shoot的方法需要大量的API来实现,也就是说,平台维度所提供的具体实现粒度较小,都是基本操作,move/shoot就是调用一系列的这样基本API来实现的。

结尾

   桥接模式是一个被《HEAD FIRST设计模式》忽略的模式,这个模式支持SOLID原则,常适用于多维度变化的应用场景,由于接触该模式时间不长,理解不深,错误难免,欢迎讨论~版权所有~转载请注明~

例说桥接模式(Bridge Pattern)