concept
Software entities (classes, modules, functions) should be open for extension but closed for modification
软件实体(包括类、模块、功能等)应该对扩展开放,但是对修改关闭()
analyse
- 相对于其他的几种设计原则来讲,开闭原则的思想就像一个纲领【用抽象构建框架,用实现扩展细节】,其他几大原则更像在这个纲领的之上进行拓展
- 在设计一个模块的时候,应对使这个模块可以在【不被修改】的前提下被扩展,换言之,应对可以【不必修改源代码】的情况下【改变这个模块的行为】
- 实现开闭原则的关键就在于“抽象”
- 利用接口或抽象类抽象出系统的抽象层,抽象层不变,利用实现层进行扩展;
- 对可变性的封装,将可变的元素封装起来,防止改变扩散到整个应用;
- 继承是用来封装可变性的,一般的继承层次不要超过两层;
- 注意控制封装的粒度,不要将两种可变性封装到一起;
example
以书店销售书籍为例
// 书籍类的接口
public interface IBook{
public String getName();
public String getPrice();
}
//小说类的书籍
public class NovelBook implements IBook{
private String name;
private int price;
public NovelBook(String name,int price){
this.name = name;
this.price = price;
}
public String getName(){
return this.name;
}
public int getPrice(){
return this.price;
}
}
public class Client{
public static void main(Strings[] args){
IBook novel = new NovelBook("java设计模式",100);
System.out.println("书籍名字:"+novel.getName()+"书籍价格:"+novel.getPrice());
}
}
项目投产生,书籍正常销售,但是我们经常因为各种原因,要打折来销售书籍,这是一个变化,我们要如何应对这样一个需求变化呢?
我们有下面三种方法可以解决此问题:
修改接口
在IBook接口中,增加一个方法getOffPrice(),专门用于进行打折处理,所有的实现类实现此方法。但是这样的一个修改方式,实现类NovelBook要修改,同时IBook接口应该是稳定且可靠,不应该经常发生改变,否则接口作为契约的作用就失去了。因此,此方案否定。
修改实现类
修改NovelBook类的方法,直接在getPrice()方法中实现打折处理。此方法是有问题的,例如我们如果getPrice()方法中只需要读取书籍的打折前的价格呢?这不是有问题吗?当然我们也可以再增加getOffPrice()方法,这也是可以实现其需求,但是这就有两个个读取价格的方法,因此,该方案也不是一个最优方案。
通过扩展实现变化
我们可以增加一个子类OffNovelBook,覆写getPrice方法。此方法修改少,对现有的代码没有影响,风险少,是个好办法。
public class OffNovelBook extends NovelBook{
public OffNovelBook(String name,int price){
super(name,price);
}
//覆写价格方法,当价格大于40,就打8析,其他价格就打9析
public int getPrice(){
if(this.price > 40){
return this.price * 0.8;
}else{
return this.price * 0.9;
}
}
}
软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能(Functions)等等,应该在不修改现有代码的基础上,引入新功能。开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。
用抽象构建架构,用实现扩展细节。开闭原则就是一个总纲,他告诉我们要对扩展开放,对修改关闭。
-
单一职责原则(原则详解)是从类的功能的角度去设计,将不同的职责分别归于不同的类中,这样使得设计更加清晰、易修改。
-
接口隔离原则(原则详解)是从接口的方法设计角度去思考,强调不能过度设计接口,尽量使得接口内的方法能充分的提供给其实现类需要的功能,不要过度设计导致方法的冗余,也不要设计不充分导致,实现类中有未能抽取的公共部分。
-
依赖倒置原则(原则详解)是从类之间依赖关系的角度去设计,强调高层不依赖于低层,低层依赖于高层,减少实现细节的依赖。
-
迪米特法则(原则详解),从类与类之间耦合的角度去思考,降低耦合,减少不必要的耦合,不要跨越多层去调用方法,最佳的方式是只调用其朋友类的方法,之后行为由朋友类负责实施。
-
里氏替换原则(原则详解)是从类的继承的角度去设计,强调父类被子类继承后,在父类出现的地方,可以替换为其子类,而且行为不会发生变化。
-
组合优于继承原则(原则详解)告诉我们在系统中应尽量多使用对象间的关联关系,尽量少使用甚至不使用继承关系来达到复用已有对象的目的