1、“;class ConcreteImplementorB extends Implementor /具体实现化(Concrete Implementor)角色public function operation()echo “ConcreteImplementorB Operation“;class Abstraction /抽象化(Abstraction)角色protected $_implementor = null;public function setImplementor($implementor)$this-_implementor = $implementor;public fun
2、ction operation()$this-_implementor-operation();class RefinedAbstraction extends Abstraction /修正抽象化 (Refined Abstraction)角色class ExampleAbstraction extends Abstraction$objRAbstraction = new RefinedAbstraction();$objRAbstraction-setImplementor(new ConcreteImplementorB();$objRAbstraction-operation();$
3、objRAbstraction-setImplementor(new ConcreteImplementorA();$objRAbstraction-operation();$objEAbstraction = new ExampleAbstraction();$objEAbstraction-setImplementor(new ConcreteImplementorB();$objEAbstraction-operation();结果:ConcreteImplementorB OperationConcreteImplementorA OperationConcreteImplemento
4、rB Operation桥梁模式所涉及的角色有:1.抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。2.修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。3.实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接 口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层 的操作。4.具体实现化(Concrete Implementor)角色:这个角色给出实现化角色
5、接口的具体实现。4.1、桥接模式的特点桥接模式的主要目的是将一个对象的变化因素抽象出来,不是通过类继承的方式来满足这个因素的变化,而是通过对象组合的方式来依赖因素的抽象,这样当依赖的因素的具体实现发生变化后,而我们的具体的引用却不用发生改变,因为我们的对象是依赖于抽象的,而不是具体的实现。而且,通过这样的依赖抽象,我们在多个对象共享这样的因素的时候,就成为可能,如果我们使用的是具体的因素的共享,当我们改变这个变化因素的时候,我们必须把使用这个因素的所有的对象,都进行相应的修改,而如果所有的引用这个变化因素的对象都依赖于抽象而不是具体的依赖呢?这也为我们的共享的提供了变化性。4.2、桥接模式的使
6、用场景1、当一个对象有多个变化因素的时候,通过抽象这些变化因素,将依赖具体实现,修改为依赖抽象。2、当某个变化因素在多个对象中共享时。我们可以抽象出这个变化因素,然后实现这些不同的变化因素。3、当我们期望一个对象的多个变化因素可以动态的变化,而且不影响客户的程序的使用时。Bridge(桥接)模式的优点:1 分离接口及其实现部分。2 提高可扩充性。3 实现细节对客户透明。3.1 认识桥接模式(1)什么是桥接在桥接模式里面,不太好理解的就是桥接的概念,什么是桥接?为何需要桥接?如何桥接?把这些问题搞清楚了,也就基本明白桥接的含义了。一个一个来,先看什么是桥接?所谓桥接,通俗点说就是在不同的东西之间
7、搭一个桥,让他们能够连接起来,可以相互通讯和使用。那么在桥接模式中到底是给什么东西来搭桥呢?就是为被分离了的抽象部分和实现部分来搭桥,比如前面示例中抽象的消息和具体消息发送之间搭个桥。但是这里要注意一个问题:在桥接模式中的桥接是单向的,也就是只能是抽象部分的对象去使用具体实现部分的对象,而不能反过来,也就是个单向桥。(2)为何需要桥接 为了达到让抽象部分和实现部分都可以独立变化的目的,在桥接模式中,是把抽象部分和实现部分分离开来的,虽然从程序结构上是分开了,但是在抽象部分实现的时候,还是需要使用具体的实现的,这可怎么办呢?抽象部分如何才能调用到具体实现部分的功能呢?很简单,搭个桥不就可以了,搭
8、个桥,让抽象部分通过这个桥 就可以调用到实现部分的功能了,因此需要桥接。(3)如何桥接 这个理解上也很简单,只要让抽象部分拥有实现部分的接口对象,这就桥接上了,在抽象部分就可以通过这个接口来调用具体实现部分的功能。也就是说,桥接在程序上就体现成了在抽象部分拥有实现部分的接口对象,维护桥接就是维护这个关系。(4)独立变化 桥接模式的意图:使得抽象和实现可以独立变化,都可以分别扩充。也就是说抽象部分和实现部分是一种非常松散的关系,从某个角度来讲,抽象部分和实现部分是可以完全分开的,独立的,抽象部分不过是一个使用实现部分对外接口的程序罢了。如果这么看桥接模式的话,就类似于策略模式了,抽象部分需要根据
9、某个策略,来选择真实的实现,也就是说桥接模式的抽象部分相当于策略模式的上下文。更原始的就直接类似于面向接口编程,通过接口分离的两个部分而已。但是别忘了,桥接模式的抽象部分,是可以继续扩展和变化的,而策略模式只有上下文,是不存在所谓抽象部分的。那抽象和实现为何还要组合在一起呢?原因是在抽象部分和实现部分还是存在内部联系的,抽象部分的实现通常是需要调用实现部分的功能来实现的。(5)动态变换功能 由于桥接模式中的抽象部分和实现部分是完全分离的,因此可以在运行时动态组合具体的真实实现,从而达到动态变换功能的目的。从另外一个角度看,抽象部分和实现部分没有固定的绑定关系了,因此同一个真实实现可以被不同的抽
10、象对象使用,反过来,同一个抽象也可以有多个不同的实现。 就像前面示例的那样,比如:站内短消息的实现功能,可以被普通消息、加急消息或是特急消息等不同的消息对象使用;反过来,某个消息具体的发送方式,可以是 站内短消息,或者是 Email,也可以是手机短消息等具体的发送方式。(6)退化的桥接模式 如果 Implementor 仅有一个实现,那么就没有必要创建 Implementor 接口了,这是一种桥接模式退化的情况。这个时候 Abstraction 和 Implementor 是一对一的关系,虽然如此,也还是要保持它们的分离状态,这样的话,它们才不会相互影响,才可以分别扩 展。也就是说,就算不要
11、Implementor 接口了,也要保持 Abstraction 和 Implementor 是分离的,模式的分离机制仍然是非常有用的。(7)桥接模式和继承 继承是扩展对象功能的一种常见手段,通常情况下,继承扩展的功能变化纬度都是一纬的,也就是变化的因素只有一类。对于出现变化因素有两类的,也就是有两个变化纬度的情况,继承实现就会比较痛苦。比如上面的示例,就有两个变化纬度,一个是消息的类别,不同的消息类别处理不同;另外一个是消息的发送方式。从理论上来说,如果用继承的方式来实现这种有两个变化纬度的情况,最后实际的实现类应该是两个纬度上可变数量的乘积那么多个。比如上面的示例,在消息类别的纬度上,目前
12、的可变数量是 3 个,普通消息、加急消息和特急消息;在消息发送方式的纬度上,目前的可变数量也是 3 个,站内短消息、Email 和手机短消 息。这种情况下,如果要实现全的话,那么需要的实现类应该是:3 X 3 = 9 个。如果要在任何一个纬度上进行扩展,都需要实现另外一个纬度上的可变数量那么多个实现类,这也是为何会感到扩展起来很困难。而且随着程序规模的加大,会越来越难以扩展和维护。而桥接模式就是用来解决这种有两个变化纬度的情况下,如何灵活的扩展功能的一个很好的方案。其实,桥接模式主要是把继承改成了使用对象组合,从而把两个纬度分开,让每一个纬度单独去变化,最后通过对象组合的方式,把两个纬度组合起
13、来,每一种组合的方式就相当于原来继承中的一种实现,这样就有效的减少了实际 实现的类的个数。从理论上来说,如果用桥接模式的方式来实现这种有两个变化纬度的情况,最后实际的实现类应该是两个纬度上可变数量的和那么多个。同样是上面那个示例,使用桥接模式来实现,实现全的话,最后需要的实现类的数目应该是:3 + 3 = 6 个。这也从侧面体现了,使用对象组合的方式比继承要来得更灵活。(8)桥接模式的调用顺序示意图 桥接模式的调用顺序如图 8 所示:图 8 桥接模式的调用顺序示意图适用性(Applicability)使用桥接模式的时机如下: 当你要避免固定的绑住一个属性及其实现。例如其实做在执行期必须被选择或
14、切换。 属性及其实现在子类中需有扩充性。例如让不同的属性及实现结合(combine)及个别独立的扩充。 改变一个属性的实现对使者不产生影响;亦即其程序无须重新编译。 在 C+你希望对使用端完全隐藏一个属性的实现,在 C+可看到的是类的接口。 你对于类有需要扩增(proliferation)如同前面动机第一个图所示;此种类层级架构代表切割对象成两部分。Runbaugh 称此类层级架构为巢状建立(nested generalization) 。 你需要分享(share)一个实现给多个对象(可能是使用参考计算 refreence counting) ;而且必须对使用端隐藏。桥接模式的利益是:1. 降
15、低属性与实现的耦合性:实现未绑在接口因此一个属性的实现可以再执行期组态(configure);甚至对象可以在执行期改变其实现。降 低属性与实现的耦合性同时可以降低编译期实现的关连性。改变实现类无须重新编译 Abstraction 类即使用端,这是最基本的特性当你要确保不同版本类 程序馆(library)的位兼容性(binary compatibility)。更甚者低耦合可以有更好的结构系统。系统高阶的部分只需知道 Abstraction 及 Implementor。2. 提高扩充性:你可以独立的扩充 Abstraction 及 Implementor 层级架构。3. 对使用端隐藏详细的实现:你
16、可以隐藏详细的实现;像共享的 Implementor 对象及伴随的参考计数机制(如果有的话) 。3.5 桥接模式的优缺点1.分离抽象和实现部分桥接模式分离了抽象和实现部分,从而极大地提高了系统的灵活性。让抽象部分和实现部分独立开来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。2.更好的扩展性由于桥接模式把抽象和实现部分分离开了,而且分别定义接口,这就使得抽象部分和实现部分可以分别独立的扩展,而不会相互影响,从而大大的提高了系统的可扩展性。3.可动态切换实现由于桥接模式把抽象和实现部分分离开了,那么在实现桥接的
17、时候,就可以实现动态的选择和使用具体的实现,也就是说一个实现不再是固定的绑定在一个抽象接口上了,可以实现运行期间动态的切换实现。4.可减少子类的个数根据前面的讲述,对于有两个变化纬度的情况,如果采用继承的实现方式,大约需要两个纬度上的可变化数量的乘积个子类;而采用桥接模式来实现的话,大约需要两个纬度上的可变化数量的和个子类。可以明显地减少子类的个数。3:何时选用桥接模式 建议在如下情况中,选用桥接模式:1.如果你不希望在抽象和实现部分采用固定的绑定关系,可以采用桥接模式,来把抽象和实现部分分开,然后在程序运行期间来动态的设置抽象部分需要用到的具体的实现,还可以动态切换具体的实现。2.如果出现抽
18、象部分和实现部分都应该可以扩展的情况,可以采用桥接模式,让抽象部分和实现部分可以独立的变化,从而可以灵活的进行单独扩展,而不是搅在一起,扩展一边会影响到另一边。3.如果希望实现部分的修改,不会对客户产生影响,可以采用桥接模式,客户是面向抽象的接口在运行,实现部分的修改,可以独立于抽象部分,也就不会对客户产生影响了,也可以说对客户是透明的。4.如果采用继承的实现方案,会导致产生很多子类,对于这种情况,可以考虑采用桥接模式,分析功能变化的原因,看看是否能分离成不同的纬度,然后通过桥接模式来分离它们,从而减少子类的数目。缺点1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。 2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。缺点:1. 增加了复杂度 桥接模式(Bridge)缺点:增加了设计复杂度。抽象类的修改影响到子类。桥接模式(Bridge)优点:将实现予以解耦,让它和界面之间不再永久绑定。抽象和实现可以独立扩展,不会影响到对方。对于具体实现的修改,不会影响到客户端。