当前位置:编程学习 > asp >>

桥接模式 (Bridge)

1.1.1 摘要
     在软件系统中,某些类型由于自身的逻辑,它具有两个或两个以上的维度变化,那么如何应对这种“多维度的变化”呢?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度呢?这就是即将要介绍的桥接模式(Bridge)。

 

使用频率:   clip_image001medium

 

定义
        桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。

意图
        将抽象与实现解耦。

动机
        当一种抽象类型可能有多种实现方式时,一般情况我们可以考虑使用继承来解决抽象类型的多种实现,在抽象类型中定义接口,而子类负责接口的具体实现。但这种做法缺乏灵活性,由于抽象类型和子类之间紧紧地绑定在一起,使得这种关系在运行时不能再修改,这使得它难以修改、扩展和重用不利于抽象和实现解耦,而且这也违背OOP原则:“优先使用对象聚集,而不是继承”。

结构图
 

clip_image001[4]

图1 桥接模式(Bridge)

 

参与者
        Abstraction

        1. 定义抽象接口。

        2. 拥有一个Implementor类型对象引用。

 

RefinedAbstraction
        1. 扩展Abstraction中的接口定义。

 

Implementor
        1. Implementor是具体实现的接口,Implementor和Abstraction接口并不一定完全一致(注:Proxy和ISubject接口一一对应),实际上这两个接口可以完全不一样,Implementor提供具体操作方法,而Abstraction提供更高层次的调用。

 

ConcreteImplementor
        1. 实现Implementor接口,给出具体实现。

 

 

1.1.2 正文
      首先根据桥接模式(Bridge)的定义:将抽象部分与它的实现部分分离,但令我不解的是,怎样才能将抽象与其实现的具体方式分离呢?其实我的迷惑主要是因为误解了实现的含义。这里实现指的是抽象类及其派生类用来实现自定的对象(而不是抽象类的派生类,这些派生类被称为具体类)。不过这样还是难以理解,现在让我们通过具体的例子说明。

      我们必需了解桥接模式(Bridge)存在的价值,然后推迟该模式,OK我们通过一个简单的例子说明为什么需要桥接模式(Bridge)吧!

      从一个绘制形状的简单问题开始,假设我们接受一个任务:编写一个程序,使用两个绘图程序DrawProgram1和DrawProgram2之一绘制图形(矩形,圆形等),而且我们被告知,实例化图形的时候,它会知道应该使用绘图程序DP1还是DP2。

     通过分析我们可以找出抽象类Shape,然后定义Retangle和Circle类继承抽象类Shape,还有就是绘图程序DP1和DP2。

 bridge1

 

图2绘图程序类图

 

      我们现在初步定义了相关的方法和类,而把Shape,Retangle和Circle都定义为抽象类型方便以后扩展。但我们可以发现图形类型并没有与绘图程序关联起来,而且前面需求中提到图形实例化时候知道具体调用哪个绘图程序。OK那么现在让我们把图形和绘图程序关联起来。

 bridge2

 

 

图3绘图程序类图

 

      我们定义了Retangle1和Retangle2继承于抽象类Retangle,添加DrawLine()方法分别调用DP1和DP2的DrawLine()方法,并且Circle1和Circle2中的实现基本相同。我们使用了一种直截了当的方法,实现了两种图形和两个绘图程序的关联。

      现在Shape有四个具体类型(Retangle1,Retangle2,Circle1和Circle2),而且每个具体类型都和相应绘图程序对于,但我们要记住“没有不变的需求,世上的软件都改动过三次以上,唯一一个只改动过两次的软件的拥有者已经死了,死在去修改需求的路上”,所以需求总是在不断的变化之中,如果添加新的绘图程序DP3,那么我们就要增加两个具体类型,而且具体类型中调用DP3方法跟之前调用DP1、DP2并没有太大的区别(冗余问题),现在有两种图形(Retangle和Cirle)和三个绘图程序(DP1,DP2和DP3),那么就拥有六种不同Shape(2种图形 * 3个绘图程序),如果我们继续扩展成三种图形那么就具有九种不同Shape(3种图形 * 3个绘图程序),这就会导致“类爆炸”问题。

      上面的解决方法,因为抽象类型(Shape)和绘图程序之间是紧耦合,于是存在严重的“类爆炸”问题(每种形状都必须知道自己用哪个绘图程序)。我们需要一种方式将抽象上的变化和实现变化进行解耦。

      将抽象与实现解耦这不就是桥接模式(Bridge)的意图吗?在介绍桥接模式(Bridge)之前,我们总结一下前面方式中的问题。

 

存在冗余
低内聚
紧耦合
      由于前面的例子按照不同图形来进行继承的分类,如果我们按照不同绘图程序分类结果又如何呢?OK,那么让我们画出按照不同绘图程序分类类图。

 bridge3

 

图4绘图程序类图

 

      现在我们继续使用四个类表示现有的图形的组合,但这里我们按照不同绘图程序派生不同图形,所以我们消除了图形类(Retangle1,Retangle2, Circle1和Circle2)和绘图程序的之间的紧耦合,从而消除了它们之间的冗余。现在把耦合转移到更高的继承层次,但问题有出现了当有新的绘图程序加入时,我们的确可以轻松地进行扩展,只要增加ShapeDP3类继承抽象类Shape就OK了,但是要实现一套一模一样的Retangle3和Circle3了。

      尽管这种方式对前面的方式有所改进,但冗余和耦合问题依然存在。

      在我们每次使用设计模式时,我们应该根据设计原则去设计,而不是直接使用已有设计模式去套用,我们要明白的一点是设计模式是根据一定的设计原则而产生的。

      这次我们要遵循两个基本原则:

找出变化封装之
优先使用对象聚集,而不是继承
     首先我们可以很快的找出需求中变化:图形和绘图程序,然后我们使用抽象来封装变化就OK了。

 bridge6

 

图5封装绘图程序中变化

 

     现在我们已经找出了变化图形和绘图程序,注意这里的OperationalDP1和OperationalDP2作为调用绘图程序(DP1和DP2)的接口,因为DP1和DP2是两个已经存在的程序所为我们无法直接抽象出DP1和DP2的高层接口,通过一种间接方式抽象出高层接口,使用抽象类把变化封装在它的“后面”,接着我们就是要在抽象类Shape和DrawingProgramming直接建立依赖关系了(优先使用对象聚集,而不是继承),所以可以通过在其中一个抽象类中保持对方的引用就OK了,但究竟是哪个类依赖于哪个类呢?

      这里存在两种情形:

 &nbs

补充:Web开发 , ASP.Net ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,