开发-关闭原则(Open-Closed Principle)本质是在扩展时不改变原有模块源代码,通过抽象和继承,利用多态性,扩展模块行为。但这样做是有成本的,会增加程序复杂性和开发成本,不要滥用。 Resisting premature abstraction is as important as abstraction itself.
Strategy模式和Template method模式是两种最常见的用来满足OCP的方法。书中提到的“表驱动”也是一个好方法,后面会附上JAVA实现的“表驱动”实现代码。
Strategy模式
在上面的例子中,如果要改变BubbleSorter的行为,可以新增新的类,新增类要求实现SortHandle接口,这样就达到了对BubbleSorter close的目的。在这里可以看到Strategy模式关键在于接口,要有接口的概念。BubbleSorter持有的是实现SortHandle接口的对象实例,而不是某个具体类的实例。
大家都知道,开发过程中需求千变万化,再好的模型也不能满足所有场景。There is no model that is natural to all contexts! How do we know which changes are likely? We do the appropriate research, we ask the appropriate questions, and we use our experience and common sense. And after all that, we wait until the changes happen! 预测将来的变化不是一件轻松的活,需要向用户提出合适的问题进行挖掘,需要根据已有经验和常识进行预测。我们不能预测到所有的变化,很多时候,我们需要等待变化的到来。
“Fool me once, shame on you. Fool me twice, shame on me.”
如果不能有效的预测需求变化,我们就先认为没有变化。当变化发生时,我们再进行抽象,避免后续类似变化的冲击。
这样就有一个问题,当到了开发后期,需求发生变化,修改成本很高。因此我们需要激发变化Stimulating Change,提前发现变化。 如何激发变化?
- 测试驱动开发,提前让系统可测试性增强。Therefore change in testability will not surprise us later.
- 快速迭代开发
- 周期性向用户展示新开发的特性
- 优先开发重要特性
- 周期性release software,尽可能的贴近用户
总结:OCP是面向对象设计的核心。遵循这个原则会给你带来很多面向对象技术声称的好处,比如flexible, reusability, and maintainability等,这些好处不是说使用面向对象语言就可以得到,会进行抽象设计是关键。但我们也不能把抽象应用到所有地方,因为这会增加开发成本和代码复杂度。我们只应在经常发生变化的地方去做这些事情
附“表驱动”代码,在示例中如果需要改变各个形状的优先级,只需要修改priority即可:
1 package com.yangjun.ocp; 2 3 import java.util.ArrayList; 4 5 public abstract class Shape { 6 final static ArrayList<String> priority = new ArrayList<String> (); 7 { 8 priority.add(Circle.class.getName()); 9 priority.add(Square.class.getName()); 10 } 11 12 public boolean isHigher(Shape s) { 13 String thisName = this.getClass().getName(); 14 String argName = s.getClass().getName(); 15 16 return priority.indexOf(thisName) < priority.indexOf(argName); 17 } 18 19 abstract public double getPremeter(); 20 21 public static void main(String[] args) { 22 // TODO Auto-generated method stub 23 24 } 25 }