开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。
开闭原则的定义: 一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。
遵循开闭原则设计出的模块具有两个主要特征:
(1)对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。
(2)对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。
开闭原则的定义已经明确地告诉我们,软件实体应该对扩展开放,对修改关闭,其信念是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。其中的软件实体包括以下几个部分;
1. 项目或软件产品 中按照一定的逻辑规则划分的模块
2. 抽象和类
3. 方法(函数)
一个软件产品 只要在生命期内,都会发生变化,既然变化是一具既定的事实,我们就应该 在设计时尽量适应这些变化,以提高项目 的稳定性和灵活怀,真正实现”拥抱变化经“,。武装原则告诉我们应尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来完成变化,它是为软件实体的未来事件而制定的对现行开发设计时行约束的一个原则。
开闭原则对扩展开放,对修改关闭,并不意味着不做任何修改,低层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段。
可以把变化归纳为以下三种类型:
1. 逻辑变化 只变化一个逻辑,而不涉及其他模块,可以通过修改原有类中的方法的方式来完成,前提提所有依赖或关联类都按照相同 的逻辑处理
2. 子模块变化
一个模块变化。会对其他的模块产生影响,特别是一个低层模块变化必然引起高纪念层模块的变化。因此在通过扩展完成变化时,高层次的模块修改是必然的。
3. 可见视图变化 可见视图是提供给客户使用的界面,该部分变化一般会引起连锁反应。最司空见惯的是业务耦合变化,什么意思呢? 一个展示列表,按照需求是6列,突然有一天要增加1列,而且这一列要跨N 张表,处理 M个逻辑才能展现出来,这样 的变化是比较恐怖的,但还是可以通过扩展来完成变化,这是要看我们原有的设计是否灵活。
1.开闭原则对测试的影响
仅测试扩展修改的部分,对原有的代码不做重复的测试。
2.开闭原则可以提高复用性
3. 开闭原则可以提高可维护性。
4. 面向对象开发的要求
实现方法:
实现开闭原则的关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。
我们在软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。
关于系统可变的部分,还有一个更具体的对可变性封装原则(Principle of Encapsulation of Variation, EVP),它从软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。
我们在实际开发过程的设计开始阶段,就要罗列出来系统所有可能的行为,并把这些行为加入到抽象底层,根本就是不可能的,这么去做也是不经济的。因此我们应该现实的接受修改拥抱变化,使我们的代码可以对扩展开放,对修改关闭
开闭原则的具体使用:
1.抽象约束
抽象是对一组事物的通用的描述,没有具体的实现,也就是表示它可以有非常多的可能性,可以跟随需求的变化而变化,因此,通过接口或抽象类可以约束一组可能变化的行为,并且能够实现对扩展开放,其包含三层含义:第一,通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现接口或抽象类中不存在的 public 方法,第二,参数类型,引用尽量使用接口或者抽象类,百不是实现类; 第三,抽象层尽量保持稳定,一旦确定即不允许修改
2.元数据控制模块行为
尽量使用元数据来控制程序的行为,减少重复开发,元数据指:用来描述环境和数据的数据,通俗地说就是配置参数,参数可以从文件中获得,也可以从数据库中获得
3. 制定项目章程
在一个团队中,建立项目章程是非常重要的,因为章程中指定了所有人员都必须遵守的约定。对项目来说,约定优于配置。这就需要项目内约束,每个项目成员都必须遵守,该方法城厢姨个团队有较高的自觉性,需要一个较长时间的磨合,一旦项目成员都熟悉这样的规则,比通过接口或抽象类进行约束效率更高,而扩展属性一点也没有减少。
4.封闭变化 对变化的封装包含两层含义: 第一,将相同的变化封装到一个接口或抽象类中;第二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。封装谈兴经,也就是爱保护的变化,找出预计有变化或不稳定的点,我们为这些变化点创建稳定的接口,准确地说就是封装可能发生的变化。
使用中注意以下几点;
1.开闭原则也只是一个原则 开闭原则只是精神口号,实现拥抱变化的方法非常多,并不局限于这6大设计原则,但是遵循这6大设计原则枯朽上可以应对大多数的变化。因此,我们在项目中应尽量采用这6大原则,适当时候可以进行扩充。
2.项目规章非常重要 如果你是一个项目经理或架构师,应尽量让自己的项目成员稳定,稳定后才能建立高效的团队文化,章程是一个团队所有成员共同的知识结晶,也是所有成员必须遵守的约定,瓦特的章程能带给项目非常多的好处。
3. 预知变化 在实践中,架构师或项目经理一旦发现有发生变化的可能,或者变化发生过,则需要考虑现架构是否轻松地实现这一变化,架构师设计一套系统不仅要符合现有的需求,还要适应可能发生的变化,变才是一个的架构。
开闭原则可以指导我们如何创建一个稳定的,灵活的系统。