From Now On,Let us begin Design Patterns。
桥接模式
定义
- 将抽象和实现解耦,使得两者独立地变化。 Decouple an abstraction from its implementation so that two can vary independently.
通用类图:
角色说明:
Abstraction类:业务抽象类,定义一个抽象接口,维护对Impementor的引用.
RefinedAbstraction类:具体实现类,被提炼的抽象
* Implementor类:定义一个抽象实现类,此抽象类与Abstraction类不一定完全相同。Implementor类提供了一些原始的操作,而Abstraction类是对这些原始操作一个更高层次的封装.*
ConcreteImplementor类:具体实现
桥接模式通用代码:
桥接模式优点:
抽象与实现分离:实现不用再绑定在一个固定的抽象层次上
实现细节对客户透明:实现已经由抽象层通过聚合关系完成了封装
对复杂的层次结构进行了降维,用聚合替代了继承的关系,代码更加容易维护
桥接模式的缺点:
- 有一定难度,需要好好理解,需要一定的经验才可以熟练使用
桥接模式的使用场景:
重点内容
当系统有多维角度分类时,而每一种分类又有可能变化,可以考虑使用桥接模式,避免在两个层次之间建立静态的联系(继承)
不希望或不适用使用继承的场合:如继承层次过渡,无法更细化设计颗粒等场景
虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者
接口或抽象类不稳定的场景,明知道接口不稳定还想通过实现或者继承来实现业务续期,那是得不偿失
注意事项:
1. 尽量使用聚合或者组合,尽量不使用类继承
主要考虑如何拆分抽象和实现,并不是一涉及继承就要考虑使用该模式。其意图是对变化的封装,尽量把变化的因素封装到最细、最小的逻辑单元中,避免风险扩散。
桥接模式的反例:源自《设计模式之禅》
反例类图:(多结构,每层结构都可以独立扩展的时候,随着层数的增加,类会爆炸)
第一层继承 2^1:2个实体类
第二层继承 2^2:4个实体类
运行场景:
运行结果:
缺点:
虽然现在代码没什么问题,但是我们说这样的设计是脆弱的,仔细分析就可以发现,它还是存在很多问题,首先它在遵循开放-封闭原则的同时,违背了类的单一职责原则,即一个类只有一个引起它变化的原因,而这里引起变化的原因却有两个,即路类型的变化和汽车类型的变化;其次是重复代码会很多,不同的汽车在不同的路上行驶也会有一部分的代码是相同的;
再次是类的结构过于复杂,继承关系太多,难于维护,最后最致命的一点是扩展性太差。如果变化沿着汽车的类型和不同的道路两个方向变化,我们会看到这个类的结构会迅速的变庞大。假如我们需要实现“谁”驾驶了汽车在哪里跑,就要变成2^3 = 8个实体类了。会指数爆炸。
桥接模式:源自《设计模式之禅》
类图:
当两个类都可以独立进行扩展变化的时候用组合而不再使用继承这种强耦合关系:降维了
降维后两个对象的关系:聚合
降维后两组对象可以独立变化:
运行场景:
运行结果:
可以看到,通过对象组合的方式,Bridge 模式把两个角色之间的继承关系改为了耦合的关系,从而使这两者可以从容自若的各自独立的变化,这也是Bridge模式的本意。
这样增加了客户程序与路与汽车的耦合。其实这样的担心是没有必要的,因为这种耦合性是由于对象的创建所带来的,完全可以用创建型模式去解决(工厂方法模式)。在应用时结合创建型设计模式来处理具体的问题。
桥接模式做(多维度变化);
结合上面的例子,增加一个维度”人”,不同的人开着不同的汽车在不同的路上行驶(三个维度); 结合上面增加一个类”人”,并重新调用.
类图:
新添加的人类代码:
新的场景:
效果及实现要点:
1.Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
2.所谓抽象和实现沿着各自维度的变化,即“子类化”它们,得到各个子类之后,便可以任意它们,从而获得不同路上的不同汽车。
3.Bridge模式有时候类似于多继承方案,但是多继承方案往往违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。很有效的降低维度。
4.Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。