定义
类应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。
我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。
主要特征
对于扩展是开放的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。
对于修改是关闭的,这意味着对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。
典型场景
在Java设计模式中,装饰者模式是一个很好的例子,完全遵循开放-关闭原则。
代码讲解
这里先以一个简单的例子讲解开放-关闭原则。
第一版需求:首先抽象出一个课程接口,然后Java,Python,UI等的课程来实现这个接口,打印出相关的课程信息。
定义一个接口Course:
public interface Course {
/**
* 课程Id
* @return
*/
Integer getId();
/**
* 课程名称
* @return
*/
String getName();
/**
* 课程价格
* @return
*/
Double getPrice();
}
定义一个接口Course的实现类JavaCourse:
public class JavaCourse implements Course {
private Integer id;
private String name;
private Double price;
public JavaCourse(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public Integer getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public Double getPrice() {
return price;
}
@Override
public String toString() {
return "JavaCourse{" + "id=" + id + ", name='" + name + '\'' + ", price=" + price + '}';
}
}
定义测试类:
public class Test {
public static void main(String[] args) {
Course javaCourse = new JavaCourse(1, "Java课程", 100d);
System.out.println(javaCourse);
}
}
打印出如下信息:
JavaCourse{id=1, name='Java课程', price=100.0}
上面对应的类图如下:
第二版需求:这个时候又来了一个需求,Java课程由于做活动,打8折,这个时候应该如何实现呢?
有如下几种实现方式:
1.在接口Course中,增加打折的方法getDiscountPrice(),如下:
Double getDisCountPrice();
这样JavaCourse实现这个方法即可。
2.直接在JavaCourse类中的getPrice方法中,直接将price*0.8。
这两种方式虽然实现起来很快,但是在应对需求变化时,是明显不足的。比如:
要求不同价格的折扣粒度不同
要求显示原来的价格
等等。
这个时候我们可以定义个折扣类来继承自JavaCourse,来实现对价格打折的支持,如下:
public class JavaDiscountCourse extends JavaCourse {
public JavaDiscountCourse(Integer id, String name, Double price) {
super(id, name, price);
}
@Override
public Double getPrice() {
return super.getPrice() * 0.8;
}
}
然后修改测试类,如下:
Course javaCourse = new JavaDiscountCourse(1, "Java课程", 100d);
System.out.println("课程Id: " + javaCourse.getId() + ", 课程名称: " + javaCourse.getName() + ", 课程价格: " + javaCourse.getPrice());
输出如下:
课程Id: 1, 课程名称: Java课程, 课程价格: 80.0
这个时候有个问题,无法获取到原价,我们可以在JavaDiscountCourse增加获取原价的方法,如下:
public Double getOriginPrice() {
return super.getPrice();
}
修改测试类,如下:
Course course = new JavaDiscountCourse(1, "Java课程", 100d);
JavaDiscountCourse javaCourse = (JavaDiscountCourse) course;
System.out.println("课程Id: " + javaCourse.getId() + ", 课程名称: " + javaCourse.getName() + "课程原价:" + javaCourse.getOriginPrice() + ", 课程价格: " + javaCourse.getPrice());
这时我们的类图如下:
到此,我们的程序就修改完成了,而且不会对之前的代码有任何影响,这也就是开始提到的开放-关闭原则。