Chap4 “开—闭”原则(OCP)
4.1 What
一个软件实体应当对扩展开放,对修改关闭。(在设计一个模块的时候,应当使这个模块可以在不修改的前提下被扩展,即应当可以在不必修改源代码的情况下改变这个模块的行为。)
优越性:
(1)通过扩展已有的软件系统,可以提供新的行为。
(2)已有的软件模块,特别是最重要的抽象层模块不能再修改,是变化中的软件系统有一定的稳定性和延续性。
4.2 How
抽象化是关键
对可变性的封装原则(Principle of Encapsulation of Variation,常写作EVP)
即找到一个系统的可变因素,将之封装起来。
“对可变性的封装原则”意味着两点:
(1) 一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象中。同一种可变性的不同表象意味着同一个继承等级结构中的具体子类。
继承应当被看作是封装变化的方法,而不应当被认为是从一般的对象生成特殊的对象的方法
(2) 一种可变性不应当与另一种可变性混合在一起。(所有的类图的继承结构一般都不超过两层,不然就意味着将两种不同的可变性混合在了一起。)
4.3 与其他设计原则的关系
里氏代换原则(LSP):任何基类可以出现的地方,子类一定可以出现。
LSP是对OCP的补充。LSP是对实现抽象化的具体步骤的规范。
一般来说,违反LSP的,也违反OCP,反过来不一定成立。
倚赖倒转原则(DIP):要依赖于抽象,不要依赖于实现。
OCP是目标,而DIP是手段。
违反DIP,就不可能达到OCP的要求。
合成/聚合复用原则(ACRP):尽量使用合成/聚合,而不是继承关系达到复用的目的。
ACRP与LSP是相辅相成的,都是对实现OCP的具体步骤的规范,ACRP是实现OCP的必要条件。
迪米特法则(LoD):一个软件实体应当尽可能少的与其他实体发生相互作用。
一个遵守LoD设计出来的系统在功能需要扩展时,会相对更容易的做到对修改的关闭。LoD是一条通向OCP的道路。
接口隔离原则(ISP):应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。
ISP所限制的是通信的宽度,即通信要尽可能的窄。
遵循ISP和LoD,会使一个软件系统在功能扩展的过程当中,不会将修改的压力传递到其他的对象。
4.4 策略模式对OCP的支持
策略模式说的是:如果有一组算法,那么就将每一个算法封装起来,使他们可以互换。
4.5 在其他设计模式中的体现
所有的设计模式都是对不同的可变性的封装,从而使系统在不同的角度上达到OCP的要求。
简单工厂模式:
每次增加一个新的产品,都需要修改工厂角色,但产品的消费者可以避免进行修改。
工厂方法模式:
具体工厂类都有共同的接口,他们“生产”出很多的处于同一个等级结构中的产品对象。可以允许向系统加入新的产品类型,而不必修改已有代码。
抽象工厂模式:
封装了产品对象家族的可变化性,从而一方面可以使系统动态的决定将哪一个产品族的产品实例化,另一方面可以在新的产品对象引进到已有的系统中时不必修改已有的系统。即:在产品对象家族发生变化时,这一设计可以维持系统的OCP特性。
建造模式:
封装了一个有内部结构的产品对象的过程,因此,这样的系统是向产品内部表象的改变开放的。
桥梁模式:
是“对可变性的封装原则”的极好例子。在这种模式中,具体实现化类代表不同的实现逻辑,但是所有的具体实现化类又有共同的接口。新的实现逻辑可以通过创建新的具体实现化类加入到系统里面。
门面模式:
调停者模式:
访问者模式:
迭代子模式:
4.6 一个重构做法的讨论
例子:将条件转移语句改写成为多态性。
寻找可变性的线索
何时使用这种重构做法
如果一个条件转移语句确实封装了某种业务逻辑的可变性,那么将此种可变性封装袭来就符合OCP设计思想了。
但是,如果一个条件转移语句没有设计重要的业务逻辑,或者不会随着时间的变化而变化,也不意味着任何的可扩展性,那么他就没有涉及热河有意义的可变性。如果将之改写为多态性就是“多态性污染”