什么是开放封闭原则?
定义:软件实体如类、模块、函数应该对扩展开放,对修改关闭。
开放封闭原则的主要思想是:当系统需求发生改变时,尽量不修改系统原有代码功能,应该扩展模块的功能,来实现新的需求。
开放封闭原则的作用?
当发生需求变化时,若是在原有代码上进行修改,不仅可能会导致依赖其的功能模块发生变动,产生不可预见的问题(鬼知道会是什么问题?),还会导致程序代码变得脆弱、难以复用。另外对测试人员也有很大的影响,由于在原有的基础上修改,测试人员还得对原有功能模块测试,测试人员不打死你才怪,且会延长项目开发周期。
遵守开放封闭原则具有以下优点:
- 提高代码的可复用性。在粒度小的功能模块上进行扩展,有助于代码的复用性;我们在开发前就要预想未来可能存在的扩展,并为此留有扩展空间。
- 提高系统的稳定性和可维护性。不修改原有代码有助于系统稳定性,以扩展的方式开发新功能有助于快速开发与维护。
- 提高软件测试的友好性。以拓展的方式开发功能,不会影响原有功能,有利于加快测试进度。
为什么要遵守开放封闭原则?
软件开发过程中需求是一定会变化的,所以我们在代码设计的时候要注意代码的稳定性,以减低需求变化带来的不良影响,以便提高系统稳定性和灵活性,而开放封闭原则正是为了处理这些问题而生。我们简单以商店价格变化为例子来描述为什么要遵守开放封闭原则。
代码语言:javascript复制//商品接口
interface Goods {
//获得商品名称
String getName();
//获取商品价格
int getPrice();
}
//牛奶
class Milk implements Goods {
@Override
public String getName() {
return "牛奶";
}
@Override
public int getPrice() {
return 59;
}
}
我们定义一个商品接口,其接口方法包含获取商品:名称、价格。
现有如下场景:这款新品牛奶上线后发现销量不太好,商家决定打折降价销售提高销量。我们一下子就可以想到有以下方案来实现它。
1、修改商品接口
增加一个获取打折后价格的接口方法,这是个好办法。但是我们之前讲过抽象层不应经常变化,不然系统不稳定!对于会出现打折销售这种情况,我们应该在程序开发前就该想到了,现我们没有想到这种场景,就想想有没有别的办法。
2、修改商品实现类
我们又可以想到在商品实现类里面写多一个获取打折后价格的方法啊,这似乎是挺好的方法!之前对于一部分需求,我也是这么做的,开发效率快嘛,不会带来额外类的管理也不会影响原有代码逻辑。但是有一点就是不利于代码的复用。因为把一个方法写特定类里面,无论是对自己还是其他开发者,一旦时间过长,都不会知道有这么一段代码在这个类里面。我们再思考下有没有其他方法。
3、拓展英雄实现类
我们可以新增一个牛奶打折销售类 OffPriceMilk,重写 getPrice 方法。这种方法不会修改原有牛奶类的代码,保持原有业务逻辑不变,改动较小,风险较小。
由上对比,我们会选择第三种方案来处理这次的需求,有利于原有系统的稳定性,测试的友好性。开发过程中,之前的设计有他们设计的原因,应尽量避免对原有代码的修改。
开放封闭原则的实现方法
1、抽象约束
抽象层不易变化,只要抽象得合理,就可以保持系统架构的稳定性。我们在《设计模式(三):依赖倒置原则》的时候说过,抽象层为基础搭建的工程架构会比由具体实现为基础搭建的工程架构稳定得多,依赖倒置原则其实也是开放封闭原则的具体实现。抽象约束有以下三个指导原则:
- 通过接口或抽象类约束扩展,对扩展进行边界限定,一般不允许出现在接口或抽象类中不存在的public方法;
- 参数类型、引用对象出现的地方应尽量使用接口或者抽象类代替实现类;
- 抽象层应尽量保持稳定,一般情况下不允许修改;
2、封装变化
客户的需求是不断变更的,我们难以把控所有的需求变化。通过封装变化的方法,将系统中经常变化的部分和稳定的部分分开来,有助于增加复用性,并降低系统耦合度,可以最大限度的保证系统的可扩展性。封装变化有以下两个指导原则:
- 将相同的变化因素封装到一个接口或抽象类中;
- 将不同的变化因素封装到不同的接口或抽象类中;
往期文章:
《设计模式(一):单一职责原则》
《设计模式(二):里氏替换原则》
《设计模式(三):依赖倒置原则》
《设计模式(四):接口隔离原则》
《设计模式(五):最少知识原则》
以上就是今天《开放封闭原则》的讲解,良好的代码风格需要长期不断的积累学习。各位读者大人若有问题,欢迎后台留言,我将第一时间回复!
6大设计原则已经讲述完毕,接下来是关于 23 个设计模式的分享!下期文章将介绍《设计模式(七):单例模式》
更多知识干货,欢迎关注我们的微信公众号:IT界的泥石流