Open Close Principle
动机
开明的应用设计和代码模块应当关注应用在发展和维护阶段中频繁发生的变化。通常,给应用添加一个新功能的时候会涉及到很多改动。应尽量少地在已有代码中做改动,因为已有代码一般都已通过单元测试,在其中做的改动可能莫名其妙地就影响到现有功能。
开放 - 封闭原则 强调设计和代码编写应当以在现有代码上改动最小的方式来完成新功能的添加。软件设计应尽可能在添加新功能类的时候不去改动现有代码。
目的
软件实体,如类、模块和函数,应当对扩展开放,但对修改关闭。
个人理解:对使用方的修改关闭, 对提供方的功能扩展开放
例子
以下是一个违背 OCP 的例子。 它实现了绘制各种图形的 graph editor
。 显然,GraphicEditor
没有遵循 OCP ,因为每次添加一个 shape
子类,都要对其进行修改。这有以下几个缺点:
- 每次添加新的
shape
子类,GraphicEditor
的单元测试都要重新做 - 每次添加新的
shape
子类所需时间都比较多,因为开发人员需要理解GraphicEditor
的内部逻辑 - 尽管新添加的
shape
能很好的工作,但它可能会意外地影响现有功能
// Open-Close Principle - Bad Example
class GraphicEditor{
public void drawShape(Shape s){
if(s.m_type == 1)
drawRectangle(s);
else if(s.m_type == 2)
drawCircle(s);
}
public void drawRectangle(Rectangle r){...}
public void drawCircle(Circle r){...}
}
class Shape{
int m_type;
}
class Rectangle extends Shape{
Rectangle(){
super.m_type = 1;
}
}
class Circle extends Shape{
Circle(){
super.m_type = 2;
}
}
以下是一个遵循 OCP 要求的例子。 在新的设计中,我们在 GraphicEditor
中使用抽象的 draw()
方法,而将具体实现移至派生类实例中。通过使用 OCP ,我们就避免了上述例子中的问题,当添加新 shape
子类的时候, 不需要对GraphicEditor
进行修改。
- 不需要额外的单元测试
- 不需要知晓
GraphicEditor
的源码 - 由于具体的绘图代码移到具体的
shape
实现子类中,新功能添加进来的时候免除了影响有功能的风险
// Open-Close Principle - Good example
class GraphicEditor{
public void drawShape(Shape s){
s.draw();
}
}
class Shape{
abstract void draw();
}
class Rectangle extends Shape{
@Override
public void draw(){
// draw Rectangle
}
}
class Circle extends Shape{
@Override
public void draw(){
// draw Circle;
}
}
总结
和其他原则一样 OCP , 也只是一个原则。构思一个灵活的设计需要耗费额外的时间和精力,这会引入更高层次的抽象,而抽象则会提高代码的复杂度。所以此原则应该应用于最可能发生变化的地方。
有很多设计模式可以帮助我们在不改动现有代码的情况下进行扩展。比如说装饰者模式可以帮助我们遵循 OCP 原则。工厂方法或观察者模式也可以用于将应用程序设计成易于调整,同时最小限度地对原有代码进行改动。
上一篇:软件设计原则
下一篇:DIP 依赖倒置原则
目录: //www.greatytc.com/p/af861220a6cc
jdk中的设计模式: //www.greatytc.com/p/734c62d1fb3d