设计模式 之 开放封闭原则 (Open Close Principle)
Motivation
动机
A clever application design and the code writing part should take care of the frequent changes that are done during the development and the maintaining phase of an application. Usually, many changes are involved when a new functionality is added to an application. Those changes in the existing code should be minimized, since it's assumed that the existing code is already unit tested and changes in already written code might affect the existing functionality in an unwanted manner.
良好的设计和编码需要应对开发和维护阶段的频繁改动。通常向应用中添加新功能就会引入改动,假设现存代码已经经过了单元测试,则改动很可能影响现存功能,所以对于已有代码的改动应该最小化。
The Open Close Principle states that the design and writing of the code should be done in a way that new functionality should be added with minimum changes in the existing code. The design should be done in a way to allow the adding of new functionality as new classes, keeping as much as possible existing code unchanged.
开放封闭原则要求设计和编码应该以对现存代码改动最小的方式来添加新功能。设计应该提供以新类的方式添加新功能,尽量保持现存代码不改动。
Intent
目的
Software entities like classes, modules and functions should be open for extension but closed for modifications.
类、模块、函数等软件实体应该对扩展开放,对修改封闭。
Example
示例
Bellow is an example which violates the Open Close Principle. It implements a graphic editor which handles the drawing of different shapes. It's obviously that it does not follow the Open Close Principle since the GraphicEditor class has to be modified for every new shape class that has to be added. There are several disadvantages:
- for each new shape added the unit testing of the GraphicEditor should be redone.
- when a new type of shape is added the time for adding it will be high since the developer who add it should understand the logic of the GraphicEditor.
- adding a new shape might affect the existing functionality in an undesired way, even if the new shape works perfectly
下面这个示例违反了开放封闭原则。这个例子实现了一个图形编辑器,用来画不同得到形状。这个例子明显不符合开发封闭原则,因为每当增加新图形时都需要修改图形编辑器类。它有如下缺点:
- 每当增加新图形时,图形编辑器类的单元测试就要重做。
- 新加形状类型的时间成本较高,开发者需要理解GraphicEditor的逻辑。
- 添加新的形状可能以不期望的方式影响到现存的功能,即便新形状能够完美工作。
In order to have more dramatic effect, just imagine that the Graphic Editor is a big class, with a lot of functionality inside, written and changed by many developers, while the shape might be a class implemented only by one developer. In this case it would be great improvement to allow the adding of a new shape without changing the GraphicEditor class.
为了加深说明效果,假设GraphicEditor是一个大类,内部包含很多功能,并且被多个开发者开发和更改,而形状是只被一个开发者实现的类。这个例子中,在不改动GraphicEditor类的情况下添加新形状将会是很大的进步。
1 // Open-Close Principle - Bad example 2 class GraphicEditor { 3 4 public void drawShape(Shape s) { 5 if (s.m_type==1) 6 drawRectangle(s); 7 else if (s.m_type==2) 8 drawCircle(s); 9 } 10 public void drawCircle(Circle r) {....} 11 public void drawRectangle(Rectangle r) {....} 12 } 13 14 class Shape { 15 int m_type; 16 } 17 18 class Rectangle extends Shape { 19 Rectangle() { 20 super.m_type=1; 21 } 22 } 23 24 class Circle extends Shape { 25 Circle() { 26 super.m_type=2; 27 } 28 }
Bellow is a example which supports the Open Close Principle. In the new design we use abstract draw() method in GraphicEditor for drawing objects, while moving the implementation in the concrete shape objects. Using the Open Close Principle the problems from the previous design are avoided, because GraphicEditor is not changed when a new shape class is added:
- no unit testing required.
- no need to understand the sourcecode from GraphicEditor.
- since the drawing code is moved to the concrete shape classes, it's a reduced risk to affect old functionallity when new functionallity is added.
下面的例子支持开放封闭原则。在新的设计中我们在GraphicEditor类中使用抽象方法draw()来画对象,方法实现移动到具体的形状对象中。使用开放封闭原则后,上面设计中的问题将会被避免,因为添加新形状将不需要对GraphicEditor类做修改:
- 不需要单元测试。
- 不需要理解GraphicEditor类的源代码逻辑。
- 因为绘制代码移动到了具体形状类中,所以也减小了在新功能添加过程中对原功能的影响。
1 // Open-Close Principle - Good example 2 class GraphicEditor { 3 public void drawShape(Shape s) { 4 s.draw(); 5 } 6 } 7 8 class Shape { 9 abstract void draw(); 10 } 11 12 class Rectangle extends Shape { 13 public void draw() { 14 // draw the rectangle 15 } 16 }
Conclusion
结论
Like every principle OCP is only a principle. Making a flexible design involves additional time and effort spent for it and it introduce new level of abstraction increasing the complexity of the code. So this principle should be applied in those area which are most likely to be changed.
像每个原则一样,开放封闭原则只是一个原则。做灵活的设计花费了额外的时间和精力,而且这引入了一个新的抽象层,提高了代码的复杂性,所以这个原则应该应用到大概率可能被修改的的区域中。
There are many design patterns that help us to extend code without changing it. For instance the Decorator pattern help us to follow Open Close principle. Also the Factory Method or the Observer pattern might be used to design an application easy to change with minimum changes in the existing code.
有一些设计模式帮助我们在不改动代码的基础上扩展代码。比如装饰器模式帮我们遵循开放封闭原则。工厂方法或者观察者模式也可以用来设计对已有代码做最小改动的应用。