Design mode from the encapsulation of change Logic

Source: Internet
Author: User

Generally, for a class that meets most of our needs, you can create a subclass of the class and only change what we don't expect (the part that needs to be changed ). Just inherit a class, you can reuse the code of this class. What a wonderful thing! However, as with most beautiful things, over-using is often bad. According to the replaceable principle (LSP), Public inheritance has a conceptual practical significance. It represents a is-a relationship. Before using inheritance, be sure to check whether the relationship actually belongs to is-a. Otherwise, inheritance is very easy to be over-used. Based on this, we recommend that you use object composition instead of class inheritance ). The Strategy Mode encapsulates the logic to be changed outside, and then transmits the variable logic to the topic class through combination. Strategy has various variants. Let's discuss them one by one:

Method 1: If the logic to be changed can be packaged into a function, the Strategy mode can be implemented through function pointers. (Example taken from <>)

class GameCharacter;int defaultHealthCalc(const GameCharacter& gc);class GameCharacter {public:typedef int (*HealthCalcFunc) (const GameCharacter&);explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc):healthFunc(hcf){}int healthValue() const{ return healthFunc(*this);}//...private:HealthCalcFunc healthFunc;};

This is a simple application of the stategy design pattern. Compared with the practice of virtual inheritance, You can bind different functions or even provide the possibility of binding different function pointers at runtime. The disadvantage is that the non-public component of the category object cannot be used. (If you have this requirement, you may need to consider the friend function or the private inheritance method, which will not be discussed here)

Method 2: complete the strategy mode by using the function object.

Objects using the tr1: function can hold any callable objects (function pointers, function objects, or member function pointers), as long as their signatures are compatible with the demand side. The essence is the same as method 1. The key lies in the use of function objects.

Method 3: Classic Strategy Mode: make the logic to be changed into a separated virtual member function in the inheritance system.


The standard strategy mode makes it easier to identify and communicate. The attraction of this method lies in the more pure object-oriented design (taking into account the feelings of non-C ++ programmers, the following object-oriented design is converted into a more object-oriented C # language for expression .). In addition, if more than one logical unit is changed, multiple virtual methods can be created. To apply a new health algorithm policy, you only need to add a derivedclass to the healthcalcfunc inheritance system.

Continue our discussion. What should we do if the healthcalcfunc logic takes into account not only the differences between algorithms, but also the differences between computing objects (gamecharacter?

The changes are as follows:


class EvilBadGuy:public GameCharacter {public://...override int healthValue() const{healthCalcObj.calcHealthValue(this);}private:HealthCalcFunc healthCalcObj;};

This involves two polymorphism distributions. The first distribution is the healthvalue function. The distribution identifies the object type of the called healthvalue method. The second distribution is the calchealthvalue method, which identifies the specific function to be executed. The two distributions jointly enable different subclass instances to call different calchealthvalue functions.

Healthcalcfuncclass only provides the computing logic. In fact, we must keep its instance status in gamecharacter. You only need to pass in the object as needed. Remove the aggregated data member, and the healthvalue function interface is changed:

int healthValue(HealthCalcFunc) const 
In this way, the aggregation relationship becomes a dependency relationship. If we change the name of the class and function interface, the complete UML diagram should be as follows:


Ah, I ran the question! Isn't this the visitor mode?

The above UML diagram is indeed a type diagram of the visitor mode. :-) Let's take care of it, and proceed to our story. Although this model can solve our problems. Do you have any problems or room for improvement?

Note! In the base class of the visitor hierarchy, each derived class in the accessed hierarchy has a corresponding function. Therefore, a dependency ring binds all the accessed Derived classes together. What's the problem?

In this way, it is difficult to incrementally compile the visitor structure, and it is difficult to add a new derived class to the accessed hierarchy. If the accessed hierarchy is unstable and many new Derived classes need to be created, when a new derived class is added to the accessed hierarchy, you must modify and re-compile the Visitor Base class and all its derived classes. In C ++, the situation is worse. Whenever a new derived class is added, the entire accessed hierarchy must be re-compiled and re-deployed.

The key to the problem is to release such a strongly bound dependency ring. Let's make the following changes:



public override int accept (DegeneratedVisitor v){    int value = 0;    try    {        EvilBadGuyVisitor ev = (EvilBadGuyVisitor)v;        value = ev.visit(this);    }    catch (ClassCastException e)    {    }    return value;}

Degeneratedvisitor is transformed into an empty interface. Each derived class of the accessed hierarchy has a corresponding visitor interface. In this way, dependency becomes a stable interface dependency. Even if expansion occurs, the changes are concentrated in the specific visitor subclass. The accept function in the accessed derived class converts the Visitor Base Class (CAST) into an appropriate visitor interface. If the transformation is successful, this method calls the corresponding visit function.

In fact, the visitor Derived classes do not need to implement the visit function for each accessed derived class, only when they need to interact. Give the evolved mode a new name: acyclicvisitor mode.

This method removes the dependency ring and makes it easier to add accessed Derived classes and perform incremental compilation. Of course, there are all disadvantages. The disadvantage is that the model is more complex. Since the transformation takes a lot of execution time, it will be worse when the width and depth of the accessed hierarchy increase.

Bring the topic back to our starting point-encapsulate and expand the logic that requires changes. The strategy model focuses on the diversity of applied policies, so as to encapsulate and expand beyond the inheritance system. Visitor focuses more on different processing needs of the accessed subject. If there is a data structure that needs to be processed in different ways, you can use the visitor mode. Both of them achieve logical extension without changing the existing class hierarchy. In fact, there are more ways to expand functions without changing the class hierarchy. For example, decorator mode and extensionobject mode. Different modes and methods have different application scenarios and focuses.

In the end, I want to emphasize that we should never stick to the design pattern. Before starting object-oriented design, let yourself have a deeper understanding of inheritance, encapsulation, and polymorphism. Object-oriented thinking!


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.