定义

一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。

Software entities like classes, modules and functions should be open for extension but closed for modification.

开闭原则总结为如下两点:

  • 软件模块对扩展是开放的
    • 当需求发生改变时,可以对模块进行扩展
  • 软件模块对修改是封闭的
    • 对模块进行扩展时, 无须改动模块的源代码。

似乎是矛盾的 ?

案例:购物车

首先这里定义了两种水果类,苹果和橙子,他们的名字和价格都不同。

  1. class Apple {
  2. public String getName() {
  3. return "Apple";
  4. }
  5. public float getPrice() {
  6. return 5.0f;
  7. }
  8. }
  9. class Orange {
  10. public String getName() {
  11. return "Orange";
  12. }
  13. public float getUnitPrice() {
  14. return 6.0f;
  15. }
  16. }

之后定义一个购物车类,里面定义一个用于保存所有水果的list,然后分别有添加苹果和橙子的方法,最后计算他们的总价格。

  1. class ShopCart {
  2. List items = new ArrayList();
  3. public void addApple(Apple apple) {
  4. items.add(apple);
  5. }
  6. public void addOrange(Orange orange) {
  7. items.add(orange);
  8. }
  9. public float calculateTotalPrice() {
  10. float total = 0.0f;
  11. for (Object o : items) {
  12. if (o instanceof Apple) {
  13. Apple apple = (Apple) o;
  14. total += apple.getPrice();
  15. }
  16. if (o instanceof Orange) {
  17. Orange orange = (Orange) o;
  18. total += orange.getUnitPrice();
  19. }
  20. }
  21. return total;
  22. }
  23. }

我们思考一下这里满不满足开闭原则的两点要求:
对扩展开放: 可以添加新的水果类
但是每次添加新的水果类,就需要修改ShopCart中的逻辑 !
所以它不满足开闭原则的第二点:对修改是封闭的。

使用开闭原则进行优化
我们注意到苹果和橙子获取单价的方法名都不同,这里我们可以抽象出一个水果的父类,屏蔽水果的名字和单价属性,之后苹果和橙子分别继承水果类。

  1. // 水果类
  2. public abstract class Fruit {
  3. public String getName() {
  4. return "Fruit";
  5. }
  6. public abstract float getPrice();
  7. }
  8. // 苹果类
  9. class Apple extends Fruit {
  10. @Override
  11. public String getName() {
  12. return "Apple";
  13. }
  14. @Override
  15. public float getPrice() {
  16. return 5.0f;
  17. }
  18. }
  19. // 橙子类
  20. class Orange extends Fruit {
  21. @Override
  22. public String getName() {
  23. return "Orange";
  24. }
  25. @Override
  26. public float getPrice() {
  27. return 6.0f;
  28. }
  29. }
  30. // 购物车
  31. class ShopCart {
  32. List<Fruit> items = new ArrayList<Fruit>();
  33. public void addFruit(Fruit f) {
  34. items.add(f);
  35. }
  36. public float calculateTotalPrice() {
  37. float total = 0.0f;
  38. for (Fruit f : items) {
  39. total += f.getPrice();
  40. }
  41. return total;
  42. }
  43. }

现在看看优化之后的代码是否满足开闭原则的两点要求:

  • 对扩展开放
    • 可以任意的添加新的水果类:香蕉,西瓜…
  • 对修改封闭
    • 对于ShopCart中的计算逻辑不用修改。

开闭原则的重点在于抽象!