**开闭原则**
开闭原则是指一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭,也就是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。
开闭原则是面向对象的可复用设计的基石。其他设计原则是实现开闭原则的手段和工具。
开闭原则的意思就是说,你设计的时候,时刻要考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新需求来,我们增加一些类就完事了,原来的代码能不动则不动。
这个原则有两个特性:
* 一个是说对于扩展是开放的
* 一个是说对于更改是封闭的
面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。这就是“开放-封闭原则”的精神所在。
简单的用一句经典的话来说:过去的事已成历史,是不可修改的,因为时光不可倒流,但现在或明天计划做什么,是可以自己决定(即扩展)的。
-----------------------
**开闭原则好处**
**如果一个软件系统符合开闭原则的,那么从软件工程的角度来看,它至少具有这样的好处:**
**对软件测试友好**
软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运行。
**可复用性好**
我们可以在软件完成以后,仍然可以对软件进行扩展,加入新的功能,非常灵活。因此,这个软件系统就可以通过不断地增加新的组件,来满足不断变化的需求。
**可维护性好**
由于对于已有的软件系统的组件,特别是它的抽象底层不去修改,因此,我们不用担心软件系统中原有组件的稳定性,这就使变化中的软件系统有一定的稳定性和延续性。
------------------------
**开闭原则示例:**
以课程为例说明什么是开闭原则,首先定义课程接口以及英语课程接口实现:
```
/**
* 定义课程接口
*/
public interface ICourse {
String getName(); // 获取课程名称
Double getPrice(); // 获取课程价格
Integer getType(); // 获取课程类型
}
/**
* 英语课程接口实现
*/
public class EnglishCourse implements ICourse {
private String name;
private Double price;
private Integer type;
public EnglishCourse(String name, Double price, Integer type) {
this.name = name;
this.price = price;
this.type = type;
}
@Override
public String getName() {
return null;
}
@Override
public Double getPrice() {
return null;
}
@Override
public Integer getType() {
return null;
}
}
// 测试
public class Main {
public static void main(String[] args) {
ICourse course = new EnglishCourse("小学英语", 199D, "Mr.Zhang");
System.out.println(
"课程名字:"+course.getName() + " " +
"课程价格:"+course.getPrice() + " " +
"课程作者:"+course.getAuthor()
);
}
}
```
项目上线,课程正常销售,但是我们产品需要做些活动来促进销售,比如:打折。那么问题来了:打折这一动作就是一个变化,而我们要做的就是拥抱变化,现在开始考虑如何解决这个问题,可以考虑下面三种方案:
**修改接口**
```
public interface ICourse {
// 获取课程名称
String getName();
// 获取课程价格
Double getPrice();
// 获取课程类型
String getAuthor();
// 新增:打折接口
Double getSalePrice();
}
```
在之前的课程接口中添加一个方法 getSalePrice() 专门用来获取打折后的价格;
如果这样修改就会产生两个问题,所以此方案否定
* ICourse 接口不应该被经常修改,否则接口作为契约的作用就失去了
* 并不是所有的课程都需要打折,加入还有语文课,数学课等都实现了这一接口,但是只有英语课打折,与实际业务不符
**修改实现类**
在接口实现里直接修改 getPrice()方法,此方法会导致获取原价出问题;或添加获取打折的接口 getSalePrice(),这样就会导致获取价格的方法存在两个,所以这个方案也否定。
**通过扩展实现变化**
直接添加一个子类 SaleEnglishCourse ,重写 getPrice()方法,这个方案对源代码没有影响,符合开闭原则,所以是可执行的方案,代码如下,代码如下:
```
public class SaleEnglishCourse extends EnglishCourse {
public SaleEnglishCourse(String name, Double price, String author) {
super(name, price, author);
}
@Override
public Double getPrice() {
return super.getPrice() * 0.85;
}
}
```
综上所述,如果采用第三种,即开闭原则,以后再来个语文课程,数学课程等等的价格变动都可以采用此方案,维护性极高而且也很灵活。