学习设计模式 - 六大基本原则之开闭原则
设计模式总共有六大基本原则,统称为SOLID (稳定)原则,分别是S-单一职责原则(Single Responsibility Principle), O-开闭原则(Open closed Principle),L-里氏替换原则(Liskov Substitution Principle),L-迪米特法则(Law of Demeter),I-接口隔离原则(Interface Segregation Principle),D-依赖倒置原则(Dependence Invension Principle)。
O-开闭原则(Open closed Principle)
一、定义
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。(Software entities like classes, modules and functions should be open for extension but closed for modificatins.)
二、理解
一个软件实体应该尽量通过扩展来实现变化,而不是通过修改已有的代码来实现变化。因为修改已有的代码会引入额外的风险,可能会导致其他的模块或功能出错。在一个软件的生命周期中,变化必不可少,既然如此,我们必须以积极的态度拥抱“变化”。我们需要为未来可能出现的变化作出相应的设计。
三、注意事项
开闭原则对扩展开放,对修改关闭,并不意味这不做任何修改,低层模块的的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段。
变化可以归纳为3种:
1. 逻辑变化
只变化一个逻辑,而不改变其他模块。可以修改原有类中的方式来完成,前提条件是所有依赖或关联类都按照相同的逻辑处理。
2. 子模块变化
一个模块变化,必然会对其他的模块产生影响,特别是一个低层次的模块变化必然引起高层次模块的变化。因此,在通过扩展完成变化是,高层次的模块修改是必然的。
3. 可见视图变化
可见视图是提供给客户使用的界面,如JSP程序,Swing界面等,该部分的变化一般会引起连锁反应。最司空见惯的就是业务耦合变化。比如,按原有需求需要展示6列,然后突然有一天需要增加1列,而且这一列需要跨N张表,处理M个逻辑才能展现出来,这样的变化是比较恐怖的,但还是可以通过扩展来完成变化,这就需要看我们原有的设计是否灵活了。
什么是灵活的设计?将项目中使用某个接口的所有实现类,扩展某个类,看看是否仍可正常使用,就可以知道是否灵活使用了。
一个项目的基本路径应该是这样的:项目开发、重构、测试,投产、因为,其中的重构可以对原有的设计和代码进行修改,运维尽量减少对原有代码的修改,保持历史代码的纯洁性,提高系统的稳定性。
四、开闭原则的重要性
开闭原则是最基础的一个原则,其他五个原则都是它的具体形态。开闭原则是其精神领袖,其他五个原则是指导设计的工具和方法。
1. 开闭原则对测试的影响
单元测试通过一个方法一般需要三种方法:正常数据测试,边界数据测试,异常抛出测试,特别重要的可能需要十几个测试方法。如果修改一个实现方法,可能就要修改多个测试方法,这也可能会出现纰漏。所以需要通过扩展来实现业务需求变化。
2. 开闭原则可以提高复用性
在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑。如何提高复用性?缩小逻辑力度,知道一个逻辑不可在拆分为止。
3. 开闭原则可以提高可维护性
一款软件投产后,维护人员的工作不仅仅是对数据进行维护,还可能要对程序进行扩展,维护人员最乐意的是就是对一个类进行扩展,而不是修改一个类。
4. 面向对象开发的要求
万物皆对象,我们需要把所有的事物都抽象成对象,然后针对对象进行操作,但是万物皆运动,有运动就有变化,有变化就要有策略去应对变化。怎样快速应对呢?这就需要在设计之初考虑到所有可能变化的因素,然后留下接口,等待“可能”转变为“现实"。
五、如何使用开闭原则
1. 对扩展开放的前提是抽象约束
抽象是对一组事物的通用描述,没有具体的实现,也就意味着包含多种可能的变化,其具体实现可以随需求变化而变化。
通过接口或抽象类约束一组可能变化的行为,并且能够对扩展开放,有三层含义:
a. 抽象对扩展进行边界限定,不允许出现在接口或抽象类不存在的public方法
b. 参数类型、引用对象尽量使用抽象或接口
c. 抽象层尽量保持稳定,一旦确定即不允许修改。
2. 元数据(metadata)控制模块行为
3. 指定项目章程
4. 封装变化
a.将相同的变化封装到一个接口或抽象类中
b.将不同的变化封装到不同的接口或抽象类中,不应有两个不同的变化出现在统一接口或抽象类中
封装变化,即是找出预计有变化或不稳定的点,并为这些变化点创建稳定的接口。
23个设计模式都是从不同的角度对变化进行封装。
学习资料:
《设计模式之禅》秦小波 著