6开闭原则
开闭原则
开闭原则的英文名称是Open-Closed Principle,简称OCP。
1开闭原则的定义
开闭原则的英文原文是:
SoftWare entities should be open for extension,but closed for modification.
意思是:一个软件实体应当对扩展开放,对修改关闭。
这个原则说的是,在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即应当可以在不必修改源代码的情况下改变这个模块的行为。
在面向对象的编程中,开闭原则是最基础的原则,起到总的指导作用,其他有原则(单一职责、里氏替换、依赖倒置、接口隔离、迪米特法则)都是开闭原则的具体形态,即其他原则都是开闭原则的手段和工具。开闭原则的重要性可以通过以下几种方式体现:
- 开闭原则提高复用性。在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑,代码粒度越小,被复用的可能性就越大,避免相同的逻辑重复增加。开闭原则的设计保证系统是在一个高层次上实现了复用。
- 开闭原则提高灵活性。所有的软件系统都有一个共同的性质,即对系统的需求都会随着时间的推移而发生变化。在软件系统面临新的需求时,系统的设计必须是稳定的。开闭原则可以通过扩展已有的软件系统,提供新的行为,能快速应对变化,以满足对软件新的需求,使变化中的软件系统有一定的适应性和灵活性。
- 开闭原则易于测试。测试是软件开发过程中必不可少的环节。测试代码不仅要保证逻辑的正确性,还要保证苛刻条件(高压力、异常、错误)下不产生"有毒代码"(Poisonous Code),因此当有变化提出时,原有健壮的代码尽量不需要修改,而是通过扩展来实现。否则,就需要把原有的测试过程回笼一遍,需要进行单元测试、功能测试、集成测试,甚至是验收测试。开闭原则的使用,保证软件是通过扩展来实现业务的逻辑变化,而不是修改。因此,对于新增加的类,只需要新增相应的测试类,编写对相应的测试方法,只要保证新增的类是正确的就可以了。
2开闭原则的应用
案例:书店售书案例
IBook的接口
package com.eric.设计模式原则.开闭原则;
/**
* @author Eric
* @ProjectName my_design_23
* @description 书籍接口IBook
* @CreateTime 2020-11-23 19:44:34
*/
public interface IBook {
//获取书名
public String getName();
//获取数的价格
public double getPrice();
//获取书的作者
public String getAuthor();
}
书的类 继承了 IBook接口
package com.eric.设计模式原则.开闭原则;
/**
* @author Eric
* @ProjectName my_design_23
* @description 书的实体类
* @CreateTime 2020-11-23 19:46:31
*/
public class NovelBook implements IBook {
private String name;
private double price;
private String author;
public NovelBook(String name, double price, String author) {
this.name = name;
this.price = price;
this.author = author;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public String getAuthor() {
return author;
}
}
书店类
package com.eric.设计模式原则.开闭原则;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eric
* @ProjectName my_design_23
* @description 书店类
* @CreateTime 2020-11-23 19:48:31
*/
public class BookStore {
//图书列表
private List<IBook> bookList = new ArrayList<IBook>();
//构造函数
public BookStore(){
//对图书列表进行初始化
bookList.add(new NovelBook("数论",33.59,"哈塞"));
bookList.add(new NovelBook("图论",99.89,"欧拉"));
bookList.add(new NovelBook("博弈论",49.79,"冯·诺依曼与奧斯卡·摩根斯特恩"));
bookList.add(new NovelBook("管理学",77.35,"泰勒、法约尔、梅奥"));
}
//展示图书
public void showBooks(){
System.out.println("---------------书店售书列表--------------");
System.out.println("书名\t\t价格\t\t作者");
for (IBook book : bookList) {
System.out.println(book.getName()+"\t\t¥"+book.getPrice()+"元\t\t"+book.getAuthor());
}
}
public static void main(String[] args) {
BookStore bookStore = new BookStore();
bookStore.showBooks();
}
}
运行结果:
改进:
为了扩大图书的销售量,书店要按照9折销售图书,这就需要图书对现有的售书系统进行修改。遵照"开闭原则"中的对修改关闭原则,此时不能直接修改IBook接口 和NovelBook类,而是通过增加一个子类OffNovelBook来完成。
通过OffNovelBook类继承NovelBook实现。
OffNovelBook.java
package com.eric.设计模式原则.开闭原则;
/**
* @author Eric
* @ProjectName my_design_23
* @description 扩展后的图书类
* @CreateTime 2020-11-23 20:08:27
*/
public class OffNovelBook extends NovelBook{
//构造函数
public OffNovelBook(String name, double price, String author) {
super(name, price, author);
}
//重写getPrice()方法
public double getPrice(){
//图书的价格打9折
return super.getPrice()*0.9;
}
}
在BookStore.java中只需要将原来的new NovelBook改为new OffNovelBook即可。
修改后的BookStore.java为:
package com.eric.设计模式原则.开闭原则;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eric
* @ProjectName my_design_23
* @description 书店类
* @CreateTime 2020-11-23 19:48:31
*/
public class BookStore {
//图书列表
private List<IBook> bookList = new ArrayList<IBook>();
//构造函数
public BookStore(){
//对图书列表进行初始化
bookList.add(new OffNovelBook("数论",33.59,"哈塞"));
bookList.add(new OffNovelBook("图论",99.89,"欧拉"));
bookList.add(new OffNovelBook("博弈论",49.79,"冯·诺依曼与奧斯卡·摩根斯特恩"));
bookList.add(new OffNovelBook("管理学",77.35,"泰勒、法约尔、梅奥"));
}
//展示图书
public void showBooks(){
System.out.println("---------------书店售书列表--------------");
System.out.println("书名\t\t价格\t\t作者");
for (IBook book : bookList) {
System.out.println(book.getName()+"\t\t¥"+book.getPrice()+"元\t\t"+book.getAuthor());
}
}
public static void main(String[] args) {
BookStore bookStore = new BookStore();
bookStore.showBooks();
}
}
测试结果:
就这样通过增加一个OffNovelBook类,修改BookStore中少量的代码,就可以实现图书价格的9折销售,而其他部分没有任何变动,体现了开闭原则的应用。
注意 开闭原则对扩展开放,对修改关闭,并不意味着不作任何修改,低层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段。
开闭原则解决问题的关键在于抽象化,把系统所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法特征,给系统定义出一个一劳永逸、不再更改的抽象设计,此设计允许有无穷无尽的行为在实现层被实现。在Java中,可以定义一个或多个抽象Java类或接口,规定出所有的具体类必须提供的方法特征作为系统设计的抽象层。作为系统设计的抽象层,要预见所有可能的扩展,因此在任何扩展情况下系统的抽象底层不需要修改,从而满足了开闭原则的第二条:对修改关闭。同时,由于从抽象层导出一个或多个新的具体类可以改变系统的行为,因此系统的设计对扩展是开放的,这满足了开闭原则的第一条。
在实际开发过程中,一直都是提倡需求导向的。这就要求在设计的时候,要非常清楚的了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。
在实际开发过程的设计开始阶段,就要罗列出系统所有可能的行为,并把这些行为加入到抽象底层根本就是不可能的,并且是效益非常低的事情。另外,在设计开始阶段,对所有的可变因素进行预计和封装也不太现实,很难做得到。所以,开闭原则描绘的远景只是一种理想情况或是极端状态,现实世界是很难被完全实现的,只能在某些组件,某种程度上符合开闭原则的 要求。
开闭原则是面向对象设计的终极目标,其他设计原则都可以看做是开闭原则的实现方法。
只要你不停下来,慢一点也没关系。