定义
一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。
Software entities like classes, modules and functions should be open for extension but closed for modification.
开闭原则总结为如下两点:
- 软件模块对扩展是开放的
- 当需求发生改变时,可以对模块进行扩展
- 软件模块对修改是封闭的
- 对模块进行扩展时, 无须改动模块的源代码。
案例:购物车
首先这里定义了两种水果类,苹果和橙子,他们的名字和价格都不同。
class Apple {public String getName() {return "Apple";}public float getPrice() {return 5.0f;}}class Orange {public String getName() {return "Orange";}public float getUnitPrice() {return 6.0f;}}
之后定义一个购物车类,里面定义一个用于保存所有水果的list,然后分别有添加苹果和橙子的方法,最后计算他们的总价格。
class ShopCart {List items = new ArrayList();public void addApple(Apple apple) {items.add(apple);}public void addOrange(Orange orange) {items.add(orange);}public float calculateTotalPrice() {float total = 0.0f;for (Object o : items) {if (o instanceof Apple) {Apple apple = (Apple) o;total += apple.getPrice();}if (o instanceof Orange) {Orange orange = (Orange) o;total += orange.getUnitPrice();}}return total;}}
我们思考一下这里满不满足开闭原则的两点要求:
对扩展开放: 可以添加新的水果类
但是每次添加新的水果类,就需要修改ShopCart中的逻辑 !
所以它不满足开闭原则的第二点:对修改是封闭的。
使用开闭原则进行优化
我们注意到苹果和橙子获取单价的方法名都不同,这里我们可以抽象出一个水果的父类,屏蔽水果的名字和单价属性,之后苹果和橙子分别继承水果类。
// 水果类public abstract class Fruit {public String getName() {return "Fruit";}public abstract float getPrice();}// 苹果类class Apple extends Fruit {@Overridepublic String getName() {return "Apple";}@Overridepublic float getPrice() {return 5.0f;}}// 橙子类class Orange extends Fruit {@Overridepublic String getName() {return "Orange";}@Overridepublic float getPrice() {return 6.0f;}}// 购物车class ShopCart {List<Fruit> items = new ArrayList<Fruit>();public void addFruit(Fruit f) {items.add(f);}public float calculateTotalPrice() {float total = 0.0f;for (Fruit f : items) {total += f.getPrice();}return total;}}
现在看看优化之后的代码是否满足开闭原则的两点要求:
- 对扩展开放
- 可以任意的添加新的水果类:香蕉,西瓜…
- 对修改封闭
- 对于ShopCart中的计算逻辑不用修改。
开闭原则的重点在于抽象!
