手机发展到今天,在残酷的竞争至下,你方唱罢我登场,原来的老大诺基亚、黑莓等厂商,现在已日薄西山,新兴的安卓(三星)、苹果手机早已将他们远远的抛在了身后。某日,突发奇想:造成今天的这个局面是不是也有“桥接模式”的一份功劳呢?
想当年,不论是用的Nokia3210C还是7610,每次下载软件都得找符合自己手机型号和系统(S40、S60V2、S60V3等)的软件。如今,安卓系统的手机,只要符合系统(Android2.3、Android4.0等)要求,不论型号,不论厂商,基本上都可以直接使用了。这样就大大方便了我们手机用户,为什么呢,且看下面结构设计的演化:
假设:生产一个M品牌和一个N品牌的手机,它们都需要通讯录和游戏的功能。那么,此时我们可以按照品牌分类实现结构图:
也可以按照软件分类实现结构图:
如果现在手机需要增加一个MP3的功能,那么直接按照所分的类,添加分枝即可。如果再生产一个S品牌的手机,MP3、通讯录、游戏的功能都需要有,此时,上述两种分类方法的分枝将会非常臃肿!在这里我就不画出来了。这就是一直用继承造成的不良后果!事实上,很多情况用继承会带来麻烦,比如:对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类的实现与它的负累有非常紧密的依赖关系,以至于父类视线中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更合适的类替换。这种依赖关系限制了灵活性并最终限制了复用性。其本质就是:继承是一种强耦合的结构,父类变,子类就必须要变。
那么,我们可以换一种方式,采用如下形式的分类:
同样的,增加一个MP3功能,再增加一个具有所有功能的S品牌手机,在这种模式下,只需要在“手机软件”类和“手机品牌”类上个增加一个分支即可。体系结构比上述的两种分类方法要精简很多,而且封装更加彻底。上述两种分类方法可以代表诺基亚手机和塞班系统,最后一种分类方法可以代表安卓手机和安卓系统。
在这种分类方式中运用到了“合成/聚合复用原则”:尽量使用合成/聚合,尽量不要使用类继承。它的好处是:优先使用对象的合成/聚合将有助于我们保持每个类被封装,并被集中在单个任务上。这样的类和类继承层次会保持较小规模,并且不太可能增长成为不可控制的庞然大物。
其实,这个分类方法就是桥接模式的思想精髓,下面一起来认识这神通广大的大仙:桥接模式
桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
通俗一点就是说:实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分类出来让它们独立变化,减少它们之间的耦合。就是上述三种分类方法中的第三种分法。
其UML图:
相关代码:
Implementor类
abstract class Implementor { public abstract void Operation(); }
ConcreteImplementorA和ConcreteImplementorB等派生类
class ConcreteImplementorA :Implementor { public override void Operation() { Console.WriteLine("具体实现A的方法执行"); } }
class ConcreteImplementorB :Implementor { public override void Operation() { Console.WriteLine("具体实现B的方法执行"); } }
Abstraction类
class Abstraction { protected Implementor implementor; public void SetImplementor(Implementor implementor) { this.implementor = implementor; } public virtual void Operation() { implementor.Operation(); } }
RefinedAbstraction类
class RefinedAbstraction :Abstraction { public override void Operation() { implementor.Operation(); } }
客户端实现
class Program { static void Main(string[] args) { Abstraction ab = new RefinedAbstraction(); ab.SetImplementor(new ConcreteImplementorA ()); ab.Operation(); ab.SetImplementor(new ConcreteImplementorB ()); ab.Operation(); Console.Read(); } }
通过这个例子我们可以了解到,类并不是越多越好,继承也并不是用的越多越好。当只用继承会造成大量的类增加,不能满足开放-封闭原则时,就应该考虑到用桥接模式了。
大话设计模式敲到现在,很多时候我们并不值是在单纯的利用某个模式,而是更多的遵守设计过程中需要利用的五个设计原则:单一职责原则、开放封闭原则、依赖倒转原则、迪米特法则和合成/聚合复用原则。以后,等这些设计模式使用熟练了,也就不再会有模式之分,而只需遵循设计原则即可。