Design Patterns ----- Open Closed Principle

Open Closed Principle

In object-oriented design has many popular ideas, such as "all member variables should be set to private (Private)", "to avoid the use of global variables (Global Variables)", "using the runtime type identification (RTTI: Run Time Type Identification, for example, dynamic_cast) is dangerous "and so on. So, what is the source of these ideas? Why do they do this defined? These ideas are always correct? This article describes the basis of these ideas: Open Closed Principle (Open Closed Principle).

Ivar Jacobson once said, "All systems vary in its life cycle, as long as the system is to develop a version of the above it would need to keep in mind.".

All systems change during their life cycles. This must be borne in mind when developing systems expected to last longer than the first version.

So in the end how we can build a stable design to the face of these changes, so that the software life cycle lasts longer do?

As early as 1988, Bertrand Meyer gives guidance on the suggestion, he created a very famous contemporary open closed principle. Apply His exact words: "Software entities (classes, modules, functions, etc.) to deal with extended open but closed for modification.".

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

When a program needs changes result in more dependent cascade modules have undergone a change, then the program will show the characteristics of what we call "bad design (bad design)" is. Application accordingly become fragile, rigid, unpredictable and can not be reused. Open Closed Principle (Open Closed Principle) that is to solve these problems arising, it is emphasized that you should never change the design of the module. When requirements change, you can extend this behavior module by adding new code, rather than to change the code that already exists can work.

Open Closed Principle (Open Closed Principle) Description

Module compliant with the Open Closed Principle has two main features:

1. They "open for expansion (Open For Extension)".

That is the behavior of the module can be extended. When application requirements change, we can make the module show new or different from the past behavior in order to meet new demands.

2. They "closed for modifications (Closed For Modification)".

Module source code can not be violated, and no one has been allowed to modify the source code.

Appears above two characteristics are conflicting, since generally conventional manner is to modify the behavior of the expansion module the module. A module can not be modified are generally considered to have a fixed behavior. So how do these two opposing properties coexist it?

Abstraction is the key.

Abstraction is the Key.

When using object-oriented design techniques, you can create a fixed set of abstract and may conduct unlimited community to express. Abstract herein refers to an abstract base class, infinitely many circles may behave by subclass derived may be represented. A module for an abstract class tampering is possible, and this module may be closed for modification, because it relies on a fixed abstraction. Then the module's behavior can be extended by creating abstract derived classes.

Example: Client / Server references

Figure 1 shows a simple design does not conform to the principles of open closed.

(FIG. 1: closed Client)

Client and Server classes are specific categories (Concrete Class), we can not guarantee that a member function Server is virtual. Here Client class uses the Server class. If we want to use a different Client Object Server object, you must modify the class to use the new Client Server classes and objects.

Figure 2 shows the corresponding design in line with the principle of opening up closed.

(FIG. 2: Open Client)

In this example, AbstractServer class is an abstract class, and includes a pure virtual member function. Client class relies on the abstract, but to use the object class instance of Client Server derived class. If we need to use a different target Client Server class, the class can be derived from AbstractServer a new subclass of the Client class remains unchanged.

Example: Shape abstract

Consider the following example. We have an application needs to draw a circle (Circle) and square (Square) on standard GUI window. Round and square must be drawn in a particular order. Round and square will be created in the same list, and to maintain the proper sequence, and the program must be able to traverse the list in order and draw all round and square.

In the C language, the use of technology is unable to meet the open closed principle. We could solve this problem by way of the following code shows.

enum ShapeType {circle, square};

struct Shape 
{
    ShapeType itsType;
};

struct Square 
{
    ShapeType itsType;
    double itsSide; 
    Point itsTopLeft;
};

struct Circle 
{
    ShapeType itsType;
    double itsRadius; 
    Point itsCenter;
};

void DrawSquare(struct Square*);
void DrawCircle(struct Circle*);
typedef struct Shape *ShapePointer;

void DrawAllShapes(ShapePointer list[], int n)
{
    int i;
    for (i=0; i<n; i++)
    {
        struct Shape* s = list[i];
        switch (s->itsType)
        {
            case square:
                DrawSquare((struct Square*)s);
                break;
            case circle:
                DrawCircle((struct Circle*)s);
                break;
        }
    }
}

Here we see the definition of a set of data structures, these structures are the same except that the first element, the other is different. This structure is identified by the type code of the first element is represented by a circle (Circle) or a square (Square). DrawAllShapes structure traversal function pointer array, and then check the type code that matches a function call (or DrawCircle DrawSquare).

Here function DrawAllShapes does not comply with the principle of opening up closed because it is not guaranteed to remain closed to new Shape species. If we want to extend this function, so that it can support a graphic list and contains a triangle (Triangle) is defined, then we will have to modify this function. In fact, whenever we need to draw a new type of figure, we have to modify this function.

Of course, this program is just one example. In practice DrawAllShapes function of the switch statement will continue within the application of various inter-function continues to call, and each function is just a little bit different. Increase in such applications implies the need for a new Shape Search places all similar switch statement (or if / else chain) exists, and then add new Shape functions. In addition, to make all switch statement (or if / else chain) has a similar function DrawAllShapes such a good structure is also unlikely. More likely, and the if statement is a number of logical operators to bind together, or stacked switch case statement clause. So to find and understand these issues in all positions, and then add a new graphic definition is not a simple matter.

The following code shows a solution Cicle / Square issue in line with the principle of open closed.

public abstract class Shape
{
    public abstract void Draw();
}

public class Circle : Shape
{
    public override void Draw()
    {
      // draw circle on GUI
    }
}

public class Square : Shape
{
    public override void Draw()
    {
      // draw square on GUI
    }
}

public class Client
{
    public void DrawAllShapes(List<Shape> shapes)
    {
      foreach (var shape in shapes)
      {
        shape.Draw();
      }
    }
}

In this example, we create a Shape abstract class, the abstract class contains a pure virtual function Draw. And it is derived from and Square Circle Shape class.

Note here that if we want to extend the behavior DrawAllShapes function to draw a new graphic type, we need to do is add a subclass derived from the Shape class. The DrawAllShapes function you do not need to be modified. Therefore DrawAllShapes in line with the principle of opening up closed, its behavior can be modified and extended its not through.

In the more realistic case, Shape class may contain a number of methods. But add a new graphics application is still very simple, because only need to do is to create a derived class to implement these functions. At the same time, we no longer need to find all you need to modify the location within the application.

Because change the program in line with the open closed principle is that by adding new code, rather than modifying existing code, the kind of cascading changes described previously it would not exist.

Strategic closure (Strategic Closure)

To understand the program is not 100% complete closed. For example, imagine Shape the above example, if we decide Circle should first of all be drawn before the Square, the DrawAllShapes function of what will happen then? DrawAllShapes function is not possible for such a change to keep closed. Generally speaking, no matter how close the module design, there is always a wide variety of changes to break this closed.

Therefore, it is unrealistic to completely closed, it is necessary to pay attention to strategy. In other words, programmers must design its closed screening of what changes . This requires some experience-based prediction. Experienced designers would do well to understand where users and industry to determine the likelihood of various changes. You can then determine to remain open closed principle is most likely to change.

Abstract display closed to obtain

How do we draw the DrawAllShapes function changes the logic of the sort of keeping it closed? Remember closure is based on the abstract. Therefore, in order to make DrawAllShapes sort of closure, then we need to sort some level of abstraction. The above example is a special case with regard to the sort of some sort of graphics need to be drawn before the image of the other categories.

A sequencing strategy is, given any two objects can be found which should be drawn first. Thus, we can define a method named Precedes Shape, it can accept another Shape as a parameter and returns a result of type bool. If the result is true it indicates that the call should be received Shape object ahead of Shape of the object as a parameter.

We can use the overloaded operator technology to achieve such a comparison function. In this way we obtain by comparing the relative order of two Shape objects, then after the sort order may be drawn.

The following shows the code that is simple to implement.

public abstract class Shape
{
    public abstract void Draw();

    public bool Precedes(Shape another)
    {
      if (another is Circle)
        return true;
      else
        return false;
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
      // draw circle on GUI
    }
}

public class Square : Shape
{
    public override void Draw()
    {
      // draw square on GUI
    }
}

public class ShapeComparer : IComparer<Shape>
{
    public int Compare(Shape x, Shape y)
    {
      return x.Precedes(y) ? 1 : 0;
    }
}

public class Client
{
    public void DrawAllShapes(List<Shape> shapes)
    {
      SortedSet<Shape> orderedList =
        new SortedSet<Shape>(shapes, new ShapeComparer());

      foreach (var shape in orderedList)
      {
        shape.Draw();
      }
    }
}

This object was reached Shape sorting objects, and can be sorted in the proper order. But we still do not have a proper sort of abstraction. In this present case, the individual would have to override Precedes Shape object method to specify the order. How will this work? What we need to write the code to ensure Precedes the Circle Square before it can be drawn?

public bool Precedes(Shape another)
{
    if (another is Circle)
        return true;
    else
        return false;
}

As can be seen, this function does not meet the open closed principle. Unable to keep it closed to new Shape subclass derived. Each time a new Shape-derived class is created, this method will always be modified.

Use "data driver (Data Driven)" method to achieve closed

Table drive (Table Driven) method can achieve closure of the Shape class derived, without forcing modify each derived class.

The following shows a possible design.

private Dictionary<Type, int> _typeOrderTable = new Dictionary<Type, int>();

private void Initialize()
{
    _typeOrderTable.Add(typeof(Circle), 2);
    _typeOrderTable.Add(typeof(Square), 1);
}

public bool Precedes(Shape another)
{
    return _typeOrderTable[this.GetType()] > _typeOrderTable[another.GetType()];
}

By using this method we have succeeded in maintaining the seal of DrawAllShapes function scheduling problems in general, and each derived Shape class to the new class or sub-Shape modifications ordering policy (e.g., modified so that the first collation draw Square) and other remains closed.

Here it is still unable to maintain a closed table (Table) itself to order a variety of Shape. But we can use this table definitions in a separate module, the module is isolated from the other table, so that changes to the table will no longer have any impact on other modules.

Further expansion is closed

This is not the end of the story.

We can control the Shape hierarchy and DrawAllShapes closing function for the collation of different types of Shape basis. Nevertheless, Shape derived classes without determining the type of pattern collation non-closed. It may seem we hope to sort Shape based on higher-level structure. A complete study of this issue is beyond the scope of this article, but interested readers can consider how to implement. Such as allowing a class to hold OrderedShape OrderedObject an abstract class, and implement its own inherit from Shape and OrderedObject class.

to sum up

About Open Closed Principle (Open Closed Principle), there are many can speak. In many respects this principle is the core object-oriented design. Always follow the principle to continue to get the most benefit from object-oriented technology, for example: reusability and maintainability. At the same time, the principle is not followed by the use of an object-oriented programming language that can be reached. Rather, it requires programmers to focus more on the application of technology to abstract the part of those programs tend to change.

Reference material

Guess you like

Origin www.cnblogs.com/MessiXiaoMo3334/p/11808533.html