开放-封闭原则(The Open-Close Principle)
软件实体(类、模块、函数等)应该是可以扩展的,但是不可以修改的。
两个特征
1、对于扩展是开放的(Open for extension);
2、对于更改是封闭的(Close for modification)。
关键是抽象
既不开放又不封闭的Client
Client类和Server类都是具体类。Client类使用Server类。如果我们希望Client对象使用另外一个不同的服务器对象,那么就必须把Client类中使用Server类的地方更改为新的服务器类。
Strategy 模式:既开放又封闭的Client
Client需要实现一些功能,可以使用ClientInterface接口去描述那些功能。子类可以选择它们的方式去实现这个接口。这样,就可以通过创建ClientInteface的新的子类的方式去扩展、更改Client的行为。
Why?把接口命名为ClientInterface,而非AbstractServer。因为抽象类和Client的关系更紧密。
Shape 应用程序
1、违反OCP
drawAllShapes()不符合OCP,因为它对于新的形状类型的添加不是封闭的。每添加一种新的形状类型,都必须要更改这个函数。
public enum ShapeTpye {CIRCLE, SQUARE
}public class Shape {ShapeType itsType;
}public class Circle extends Shape {ShapeType itsType;double itsRadius;Point itsCenter;
}public class Square extends Shape {ShapeType itsType;double itsSide;Point itsTopLeft;
}//----------------------drawAllShapes---------------------
public void drawAllShapes(ShapePointer list[], int n) {int i;for (int i=0; i<n; i++) {Shape s = list[i];switch (s.itsType) {case SQUARE:drawSquare((Square)s);break;case CIRCLE:drawCircle((Circle)s);break;}}}
2、遵循OCP
drawAllShapes()符合OCP,对它的改动是增加新的模块,以及为了能够实例化新类型而围绕main的改动。
public interface Shape {public void draw();
}public class Square implements Shape {public void draw() {// 业务细节...}
}public class Circle implements Shape {public void draw() {// 业务细节...}
}//----------------------drawAllShapes---------------------
public void drawAllShape(List<Shape> list) {for(Shape s : list) {s.draw();}
}
3、预测变化与“合适”的结构
假设我们要求所有的圆必须在正方形之前绘制,drawAllShapes()无法对这种变化做到封闭。
无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。不存在对所有情况都合适的模型。既然不可能完全封闭,那么就必须有策略地对待这个问题。设计人员必须对他设计的模型应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生变化种类,然后构造抽象来隔离那些变化。
遵循OCP的代价也是昂贵的。创建正确的抽象是要花费时间和精力的。同时,那些抽象也增加了软件设计的复杂性。因而,我们希望把OCP的应用限定在可能会发生的变化上。
应对之道
1、只受一次愚弄
谚语:“愚弄我一次,应该羞愧的是你;再次愚弄我,应该羞愧的是我。”这意味着在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后同类变化。
2、刺激变化
如果我们决定接受第一颗子弹,那么子弹到来的越早越好、越快就对我们越有利。
首先编写测试——迫使系统可测试,构建了使系统可测试的抽象。
使用很短的迭代周期进行开发
在加入基础结构前就开发特性,并经常性地把那些特性展示给涉众。
首先开发最重要的特性
尽早地、经常性地发布软件
使用抽象获得显式封闭
public interface Shape extends Comparable<Shape> {public void draw();public boolean precedes(Shape s);
}public class Circle implements Shape {// 根据对象类型排序...public boolean precedes(Shape s) {if (s instanceof Square) {return true;}return false;}public int compare(Shape s1, Shape s2) {if (s1.precedes(s2)) {return 1;}return -1;}
}public void drawAllShapes(List<Shape> list) {sort(list);for (Shape s : list) {s.draw();}
}
表格驱动法获取封闭性
public class Shape {private static final Class<Shape>[] typeOrderTable = new Class[]{Circle.class, Square.class};public void draw() {//...}public boolean precedes(Shape s) {Class thisType = this.getClass();Class argType = s.getClass();boolean done = false;int thisOrder = -1;int argOrder = -1;for (int i=0; !done; i++) {Class tableEntity = typeOrderTable[i];if (tableEntity.equals(thisType)) {thisOrder = i;}if (tableEntity.equeals(argType)) {argOrder = i;}if (thisOrder >= 0 && argType >= 0) {done = true;}}return thisOrder < argOrder;}}