前言:阅读本章,OCP原则是{dy}章单一职责原则的基础上的延伸。对于构建一个实用的稳定的类十分重要,无论使用何种设计模式,OCP原则都是我们划分抽象类的基础。“模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体。所以它对于更改可以是关闭的。同时,通过从这个抽象体派生,可以扩展此模块的行为。”是核心思想。
“任何系统在其生命周期中都会发生变化。如果我们期望开发出的系统不会在{dy}版否就必须牢牢地记住这一点”那么怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在{dy}版以后不断推出新的版本呢?开放一封闭原则为我们提供了指引。
1.1开放一封闭原则(OCP)
软件实体(类、模块、函数)应该是厉以扩展的,但是不可修改的.
如果程序巾的一处改动就会产生连锁反应,导致一系列相关模块的改动,那公设计就具有僵化性的臭味.OCP建议我们应该对系统进行重构,这样以后对系统再进行那柽的改动时,就不会导致更多的修改,
2.2描述
遵循开放-封闭原则设计出的模块具有两个主要的特征。他们是:
1.对于扩展是开放的
这意味这模块行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。换而言之,我们可以改变模块的功能。
2.对于更改是封闭的
对模块行为进行扩展时,不必改动模块的源代码。模块的二进制编译码不必修改。
这两个特性好像是相互矛盾的。扩展模块行为的通常方式就是修改该模块的源代码。不允许修改的模块常常都是被认为是具有固定模式的行为。
怎样可能在不改动模块源代码的情况下去更改它的行为呢?怎样才能在无需对模块进行改动的情况下就改变它的功能呢?
2.3关键是抽象
在所有OOPL语言中,可以创建出固定却能够描述一组任意个可能行为的抽象体。这个抽象体就是抽象基类。而这一组任意个可能的行为则表现为可能的派生类。
模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体。所以它对于更改可以是关闭的。同时,通过从这个抽象体派生,可以扩展此模块的行为。
下图展示了一个简单的不遵循OCP原则的设计。
Client类和Server类都是具体的类,Client类使用Server类。如果我们希望Client类对象使用另一个不同的服务器对象,那么必须要把Client类中使用Server类的地方更改为新的服务器类。(这样就模块了我们之前的原则,设计的臭味不言而喻)
注意下图:
上图展示了一个针对上述问题遵循OCP的设计。在这个设计中,Clientinterface类是一个拥有抽象成员函数的抽象类。Client类使用这个抽象类;然而Client类的对象却是用Server类的派生类对象。如果我们希望Client对象使用一个不同的服务器对象,那么只要从ClientInterface类派生实现一个新的类,不用修改Client类。稍后我们会举一个实际的例子来表现如何进行这一抽象与分离。
也许你想知道我为何把抽象接口命名为ClientInterface。为何不把它命名为AbstractServer呢?因为(后面将会看到)抽象类和他们的客户的关系要比和实现他们的类的关系更为密切一些。
上图展示了另一个可选的结构。Policy类具有一组实现了某种策略的共有函数。这些策略函数使用了一些抽象接口描述了一些要完成的功能。不同的是,在这个结构中,这些抽象接口是Policy类本身的一部分。这些函数在Policy的子类中实现。这样,可以通过从Policy类派生出新类的方式,对Policy中指定的行为进行扩展或者更改。(这中方式其实就是利用继承的特性)
这两种模式(一种是接口一种是继承派生)是满足OCP的最常用的方法。应用他们,可以把一个功能的通用部分和实现细节部分清晰的分离开来。