软件实体(函数、类、模板等等)应该是可扩展的、但是不可修改的。
可扩展- 增加新的功能。 应对新的功能需求。
不可修改- 不修改源码。避免关联源码的修改、重新编译、链接。
一个软件实体,例如一个函数,怎样才能做到即增加新的功能,又不修改它的源码呢。
答案就是抽象。
先看一个不遵守OCP原则的例子:
client类调用server类实现某些功能。
如果有新的需求,client需要使用另外一种server,那么就要修改client中的对应部分的代码。
并且与client关联的代码,也需要重新链接、或打包等等。
修改为遵守OCP原则: (strategy 策略模式)
此时,client依赖的是一个接口(也可以是一个抽象类),server只是interface的一个具体实现。
面对同样的需求,只需新增新的server实现, 但对client来说,是符合OCP原则的。
遵循OCP也是要付出代价的。创建正确的抽象是要花费时间和精力的,同时也增加了软件的复杂度。
所以我们应当把OCP应用在确实会发生变化的地方, 或者一直等到变化发生时再采取行动。
为了防止软件背着不必要的复杂性,我们应该允许自己犯最初的错误。
这意味我们在最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。
那这就意味着,如果第一次错误来的越早越好。
因此,我们应该去刺激变化的发生。
1、首先编写测试。 让测试去激发变化的发生。
2、使用很短的迭代周期。 让客户和使用人员尽可能快的激发变化。
3、首先开发最重要的特性。
总结:
OCP是面向对象的核心所在。遵循这个原则可带来巨大的好处:灵活性、扩展性、重用性、维护性。
但是,在软件的任何地方都遵循OCP原则而进行抽象同样不是一个好主意,
开发人员应该仅仅对频繁变化的那部分做出抽象。
拒绝不成熟的抽象,和抽象本身一样的重要。