背景
在产品的开发过程中,需求变更
不足为奇。面对更变的需求基于开闭原则的思想是对程序的改动是通过增加代码新代码
来实现新的需求,而不是更改已经实现就需求的代码。因为旧的代码可能再别的需求有调用,越是底层的类的修改影响别的类就会越多。
定义
开闭原则(The Open-Closeed Principle 简称OCP)一个软件实体例如类、模块和函数应该对扩展操作开放,而对修改操作是关闭的。用抽象构建框架,是实现扩展细节。
优点
遵循OCP可以提高我们软件系统的可维护性、可扩展性、可复用性、灵活性;对程序中可能频繁变更的部分做出抽象。
案例
场景一
一个图书销售系统中需要书本的销售信息。例如书本的作者、书名、售价(简单点举三个属性);基础面向接口编程的思想将这个获取书名、作、价格折三个功能定义在接口中。实现该接口的方法的不同书本类实现实现具体的业务逻辑即可。
1.接口的定义
package com.mark.design.principle.openclose2;
/**
* @Description: 定义一个销售书本的接口:定义获取书本作者,书名,价格的接口方法
* @Author: Kingsley
* @CreateDate: 2018/11/25 18:33
* @Version: 2.0
* @Copyright : 豆浆油条个人非正式工作室
*/
public interface IBook {
/**
* @return :书本的作者名称
*/
String queryAuthor();
/**
* @return :书本名称
*/
String queryBookName();
/**
* @return :书本的售价
*/
double queryPrice();
}
接口其中一个实现类,实际中该接口会很更多的实现类。
package com.mark.design.principle.openclose2;
/**
* @Description: 书本接口的一个实现类:《悲伤逆流成河》书本
* @Author: Kingsley
* @CreateDate: 2018/11/25 18:38
* @Version: 2.0
* @Copyright : 豆浆油条个人非正式工作室
*/
public class CryMeASadRiverBookImpl implements IBook{
private String author;
private String bookName;
private double price;
public CryMeASadRiverBookImpl(String author, String bookName, double price) {
this.author = author;
this.bookName = bookName;
this.price = price;
}
/**
* @return :书本的作者名称
*/
public String queryAuthor() {
return this.author;
}
/**
* @return :书本名称
*/
public String queryBookName() {
return this.bookName;
}
/**
* @return :书本的售价
*/
public double queryPrice() {
return this.price;
}
}
模拟客户端的测试方法
/**
* 需求1:录入书本《悲伤逆流成河的信息》,并打印信息
*/
@Test
public void test1(){
IBook iBook = new CryMeASadRiverBookImpl("小四","《悲伤逆流成河》",10);
System.out.println("书名:"+iBook.queryBookName());
System.out.println("作者:"+iBook.queryAuthor());
System.out.println("售价:"+iBook.queryPrice());
}
运行结果
书名:《悲伤逆流成河》
作者:小四
售价:10.0
场景二
新需求:4月23日读书日到了;书本《悲伤逆流成河》书本售价打5折。输出该书本书本的打折后的信息。
分析:
思路一:若在接口iBook
接口中新增获取打折后的价格的方法,在实现类中实现方法。这样虽然可以达到目的,但是。如果当接口有多个实现类的时候,那么需要在每个实现类中都实现接口方法。
思路二:直接修改CryMeASadRiverBookImpl
中的获取价格方法。这样也是可以滴,但是。。。如果原本的实现方法如果逻辑复杂,或者存在被其他的业务调用的可能,这样直接修改现有的代码是不可取的。
思路三:基于OPC思想,通过继承类CryMeASadRiverBookImpl
达到既可以不修改原本的类也扩展了新的功能的目的。代码如下:
package com.mark.design.principle.openclose2;
/**
* @Description: 《悲伤逆流成河》读书日折后价的计算实现
* @Author: Kingsley
* @CreateDate: 2018/11/25 18:54
* @Version: 2.0
* @Copyright : 豆浆油条个人非正式工作室
* 扩展业务:计算打折后的价格
* 不修改原来的接口和类
* 通过继承实现
*/
public class CryMeASadRiverBookDisCountImpl extends CryMeASadRiverBookImpl{
public CryMeASadRiverBookDisCountImpl(String author, String bookName, double price) {
super(author, bookName, price);
}
/**
* 计算五折后的书本价格
* @return :折后价
*/
public double queryDicountPrice() {
return queryPrice()* 0.5;
}
}
模拟客户端的测试方法
/**
* 新需求:4月23日读书日到了书本《悲伤逆流成河》书本售价打5折
*/
@Test
public void test2(){
IBook iBook = new CryMeASadRiverBookDisCountImpl("小四","《悲伤逆流成河》",10);
System.out.println("书名:"+iBook.queryBookName());
System.out.println("作者:"+iBook.queryAuthor());
System.out.println("售价:"+iBook.queryPrice());
CryMeASadRiverBookDisCountImpl disCount = (CryMeASadRiverBookDisCountImpl) iBook;
System.out.println("折后价:"+disCount.queryDicountPrice());
}
运行结果
书名:《悲伤逆流成河》
作者:小四
售价:10.0
折后价:5.0