定义
一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。
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 {
@Override
public String getName() {
return "Apple";
}
@Override
public float getPrice() {
return 5.0f;
}
}
// 橙子类
class Orange extends Fruit {
@Override
public String getName() {
return "Orange";
}
@Override
public 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中的计算逻辑不用修改。
开闭原则的重点在于抽象!