9th OCP: Open-closed principle
Software entities (classes, modules, functions, and so on) should be extensible, but not modifiable.
9.1 OCP Overview
Modules designed to follow the open-closed principle have two main features:
(1) is open for extensions (open for extension). This means that the behavior of the module can be extended. When the requirements of the application change, we can extend the module to the new behavior that satisfies those changes.
(2) for the modification is closed (closed for modification). When you extend a module, you do not have to change the module's source code or the binaries. The binary executable version of the module, whether it is a linked library, DLL, or. exe file, does not have to be changed.
In C # or any other OOPL (object-oriented programming language), you can create an abstract body that is fixed but can describe a set of arbitrary possible behaviors. This abstract body is an abstract base class. Any possible behavior of this group is represented by a possible derived class.
The module may operate on the abstract body. Because the module relies on a fixed abstract body, it can be closed for changes. At the same time, by deriving from this abstraction, you can extend the behavior of this module.
9.2 Shape Application
9.2.1 violation of OCP
See the following code:
//--shape.h--------------------------------------- enumShapeType {circle, square}; structShape {ShapeType itstype; };//--circle.h--------------------------------------- structCircle {ShapeType itstype; DoubleItsradius; Point Itscenter; }; voidDrawcircle (structcircle*);//--square.h--------------------------------------- structSquare {ShapeType itstype; DoubleItsside; Point Itstopleft; }; voidDrawsquare (structsquare*);//--drawallshapes.cc-------------------------------typedefstructShape *PrivateShapepointer; voidDrawallshapes (Shapepointer list[]Private,Private intN) {inti; for(i =0; I < n; i++) {structShape* s =List[i]; Switch(s->Itstype) { CaseSquare:drawsquare ((structsquare*) s); Break; CaseCircle:drawcircle ((structcircle*) s); Break; } } }
The Drawallshapes function does not conform to the OCP because its addition to the new shape type is not closed. If you want this function to be able to draw a list that contains triangles, you must change the function. In fact, each addition to a new shape type has to change this function.
9.2.2 Follow OCP
View Ood solutions for square/circle issues such as the following
Public InterfaceShape {voidDraw (); } Public classSquare:shape { Public voidDraw () {//Draw a square } } Public classCircle:shape { Public voidDraw () {//Draw a circle } } Public voiddrawallshapes (IList shapes) {foreach(Shape shapeinchshapes) shape. Draw (); }
9.2.3 predicting changes and "pertinent" structures
In general, no matter how "closed" the module is, there will be some changes that cannot be closed to it. There are no models that are appropriate for all situations.
Since it cannot be completely closed, there must be a strategic approach to the problem. In other words, the designer must choose which package of changes the module he designs should make. He must first guess the classes that are most likely to change, and then construct abstractions to isolate those changes.
This requires the designer to have some predictive power from experience. Experienced designers want to know the user and the application area well enough to judge the possibilities of various changes. It then allows the design to follow the OCP principle for the most likely changes.
This is not easy to do. And in most cases, they will guess the error.
The cost of following the OCP is also expensive. Creating an appropriate abstraction takes development time and effort. At the same time, those abstractions add complexity to software design.
In the end, we will wait until the change takes place to take action!
9.2.4 Placing Hooks
In the last century, we will "place the hook" where we think it might change. We think this will make the software a little more flexible.
However, the hooks we put on are often wrong. To make things worse, even if you don't use these hooks, you have to support and maintain them, so you have an unnecessary odor of complexity. Often, we prefer to wait until we need those abstractions and then put them in.
9.2.5 using abstraction for explicit closure
Closure is based on abstraction. Therefore, in order for the drawallshapes to change in the drawing order is closed. We need a "sequence abstract body". This abstraction defines an abstract interface that can represent any possible sorting strategy.
A sort strategy means that given two objects, you can push the export to draw which one first. C # provides such an abstraction. IComparable is an interface that provides only one method: CompareTo. This method takes an object as an input parameter, and when the object that accepts the message is less than, equal to, or greater than the parameter number object, the method returns -1,0,1, respectively.
If you want circle to draw before square, look at the following code:
Public Interfaceshape:icomparable {voidDraw (); } Public classSquare:shape { Public voidDraw () {//Draw a square } Public intCompareTo (Objectobj) { if(obj isCircle) { return 1; } Else { return 0; } } } Public classCircle:shape { Public voidDraw () {//Draw a circle } Public intCompareTo (Objectobj) { if(obj isSquare) { return-1; } Else { return 0; } } } Public voiddrawallshapes (ArrayList shapes) {shapes. Sort (); foreach(Shape shapeinchshapes) shape. Draw (); }
For such a code:
Public int CompareTo (object obj) { if is Square) { Return -1; } Else { return0; } }
Obviously does not meet the OCP. Every time you create a derived class of a new shape class, all of the CompareTo () functions need to be changed.
9.2.6 using a "data-driven" approach for sealing
Table-driven methods can be used if we do not want to make the various derived classes of the shape class unknown to each other. Table-driven shape sorting mechanism:
/// <summary>///This comparer would search the priorities///hashtable for a shape ' s type. The Priorities///table defines the odering of shapes. Shapes///That is not found precede shapes , that is found./// </summary> Public classshapecomparer:icomparer{Private StaticHashtable priorities =NewHashtable (); StaticShapecomparer () {priorities. ADD (typeof(Circle),1); Priorities. ADD (typeof(Square),2); } Private intpriorityfor (Type type) {if(priorities. Contains (type))return(int) Priorities[type]; Else return 0; } Public intCompare (ObjectO1,ObjectO2) { intPriority1 =priorityfor (O1. GetType ()); intPriority2 =priorityfor (O2. GetType ()); returnPriority1.compareto (Priority2); }}
To modify the Drawallshapes method:
Public void drawallshapes (ArrayList shapes) { shapes. Sort (new shapecomparer ()); foreach inch shapes) Shape. Draw ();}
9.3 Conclusion
In many ways, OCP is at the heart of object-oriented design. Following this principle leads to the great benefits that object-oriented technology claims: flexibility, reusability, and flexibility. However, it is not that the use of an object-oriented language follows this principle. It is also not a good idea to make an arbitrary abstraction of every part of the application. The right approach is for developers to abstract only those parts of the program that are frequently changing. Rejecting immature abstractions is as important as abstraction itself.
Excerpt from: "Agile Software Development: principles, patterns and Practices (C # Edition)" Robert C.martin Micah Martin
Reprint please specify the source:
Jesselzj
Source: http://jesselzj.cnblogs.com
Agile Software Development: principles, patterns and practices--9th OCP: Open-closed principle