2.1 Creative Design Mode

1. Knowledge Points

  • Basics
  • Design Principles
  • Template Method
  • Observer mode
  • Policy Mode

2. Recommended Books

  • Design Patterns - Fundamentals of Reusable Object-Oriented Software
  • Reconstructing and Models

3. Foundation

  • Design Mode
    A design pattern is a validated solution to a specific problem that occurs repeatedly in a particular environment in software development

  • Class Model

  • Class Relationships

    A brief description of Picture 2

  • Stranger can only access a of TonyFather

  • Tony is a subclass of TonyFather and a public inheritance, so Tony can access TonyFather's a,b

  • Beauty is a friend class of TonyFather, so Beauty can access a, b, c of TonyFather

  • TonyMother and TonyFather are friend classes, so TonyMother can access TonyFather's a, b, c, TonyFather can access TonyMother's d

4. Design Principles

  1. Dependency Inversion Principle
  • High-level modules should not depend on low-level modules, and both should depend on abstraction
  • Abstraction should not depend on concrete implementation; concrete implementation should depend on abstraction
  • Auto-driving systems companies are at the top level and auto manufacturers are at the bottom level. They should not depend on each other. One side of the company changes and the other follows.
    Change; Instead, it should abstract an industry standard for auto-driving, which is relied on by both the top and the bottom. This decouples the changes between the two sides.
    Auto-driving systems, auto manufacturers are concrete implementations, they should all rely on auto-driving industry standards (abstract)
  1. Open and Closed
  • A class should be open to extensions and closed to modifications
  1. Interface-oriented
  • A variable type is not declared as a specific specific class, but as an interface
  • The client does not need to know the specific type of object, only the interface it has
  • Reduce dependencies on parts of the system to achieve a "highly cohesive, loosely coupled" type design
  1. Encapsulation Change Point
  • Separate the stability point from the change point, expand the modification change point
  • Hierarchical separation of stability and change points
  1. Single responsibility
  • A class should have only one cause for its change
  1. Richter Replacement
  • Subtype must be able to replace its parent type
  • Mainly occurs when a subclass overrides a parent implementation. Programs that originally used the parent type may have errors that override the parent method but fail to implement its responsibilities
  1. Interface Isolation
  • Customers should not be forced to rely on methods they do not use
  • Typically used to handle a class with many interfaces that involve many responsibilities
  1. Combination is better than inheritance
  • High inheritance coupling and low combination coupling

5. Design method

5.1 Template Method

  1. Definition
    Defines the skeleton of the algorithm in an operation, delaying some steps to subclasses. Template Method allows subclasses to redefine certain steps of an algorithm without changing its structure. Design Mode GoF
  2. Main points
  • The most common design pattern is that subclasses can replicate parent-child processes to enrich the parent's skeleton processes
  • Typical application of reverse control process
  • The parent protected protected protects child processes that need to be replicated; Subprocesses of subclasses can only be called by the parent class
  1. essence
  • Constraining the behavior of subclasses by fixing the algorithm skeleton
  1. Structural Diagram
  2. Example
    Demand: A brand zoo has a fixed set of performance processes, but there are several performance sub-processes that can be innovatively replaced to try iteration
    Update the performance process;
  • Implement without using design patterns:
#include <iostream>
using namespace std;

#if 0
class ZooShow {
public:
    void Show0() {
        cout << "show0" << endl;
    }
    void Show2() {
        cout << "show2" << endl;
    }
};

class ZooShowEx {
public:
    void Show1() {
        cout << "show1" << endl;
    }
    void Show3() {
        cout << "show3" << endl;
    }
};

#else if 2

class ZooShow {
public:
    ZooShow(int type = 1) : _type(type) {}

public:
    void Show() {
        Show0();
        Show1();
        Show2();
        Show3();
    }

private:
    void Show0() {
        cout << _type << " show0" << endl;
    }

    void Show1() {
        if (_type == 1) {
            cout << _type << " Show1" << endl;
        } else if (_type == 2) {
            cout << _type << " Show11" << endl;
        } else if (_type == 3) {

        }
    }

    void Show2() {
        if (_type == 20) {
            cout << "base Show22" << endl;
        }
        cout << "base Show2" << endl;
    }

    void Show3() {
        if (_type == 1) {
            cout << _type << " Show3" << endl;
        } else if (_type == 2) {
            cout << _type << " Show33" << endl;
        }
    }
private:
    int _type;
}

#endif

int main () {
#if 0
    ZooShow *zs = new ZooShow;
    ZooShowEx *zs1 = new ZooShowEx;
    zs->Show0();
    zs1->Show1();
    zs->Show2();
    zs1->Show3();
#else if 2
    ZooShow *zs = new ZooShow(1);
    zs->Show();
#endif
    return 0;
}

In the pseudocode above, we implemented the requirement in two ways
First, two performance classes are defined, and two performance subclasses are implemented in each performance class. When we need innovative replacements, we need to reorder their appearance
The second way is to define only one performance class, in which the order of sub-performances is determined by using a method show; When we need to modify the content of the sub-show, we need to modify _ type

  • Implement using a template method
#include <iostream>
using namespace std;

class ZooShow {
public:
    // Fixed process encapsulated here
    void Show() {
        Show0();
        Show1();
        Show2();
        Show3();
    }
protected:
    // Subprocesses use protected to protect them from client calls but allow subclass extensions
    virtual void Show0(){
        cout << "show0" << endl;
    }
    virtual void Show2(){
        cout << "show2" << endl;
    }
    virtual void Show1() {

    }
    virtual void Show3() {

    }
};
class ZooShowEx : public ZooShow {
protected:
    virtual void Show1(){
        cout << "show1" << endl;
    }
    virtual void Show3(){
        cout << "show3" << endl;
    }
    virtual void Show4() {
        //
    }
};

class ZooShowEx1 : public ZooShow {
protected:
    virtual void Show0(){
        cout << "show1" << endl;
    }
    virtual void Show2(){
        cout << "show3" << endl;
    }
};

class ZooShowEx2 : public ZooShow {
protected:
    virtual void Show1(){
        cout << "show1" << endl;
    }
    virtual void Show2(){
        cout << "show3" << endl;
    }
};
/*
*/
int main () {
    ZooShow *zs = new ZooShowEx;
    // ZooShow *zs1 = new ZooShowEx1;
    // ZooShow *zs2 = new ZooShowEx2;
    zs->Show();
    return 0;
}

In demand, we can know that the stability point is "fixed process", the change point is "performance sub-process". In pseudocode implementation, we fix the performance sub-process in the show method, declare the performance sub-process as virtual function, and modify the content of the performance sub-process when inheriting.

  1. Other examples
    Template Method Patterns for C++ Design Patterns

5.2 Observer Mode

  1. Definition
    Defines a one-to-many (change) dependency between objects so that when the state of an object changes, the
    Objects that depend on it are notified and updated automatically. Design Mode GoF

  2. Main points

  • The observer model allows us to change the target and observer independently, thus loosely coupling the relationship between them
  • The observer decides whether to subscribe to the notification or not, and the target does not care who subscribes.
  • The observer does not depend on the order of notifications, nor does the target object know the order of notifications
  • Commonly used in event-based ui frameworks and as part of MVC
  • Commonly used in distributed systems, actor frameworks
  1. essence
  • Trigger Linkage
  1. Structural Diagram
  2. Example
    Requirements: Weather stations issue meteorological data to data centers, which process and update the meteorological information to two different display terminals (A and B)
  • Do not implement in design mode
class DisplayA {
public:
    void Show(float temperature);
};

class DisplayB {
public:
    void Show(float temperature);
};

class WeatherData {
};

class DataCenter {
public:
    float CalcTemperature() {
        WeatherData * data = GetWeatherData();
        // ...
        float temper/* = */;
        return temper;
    }
private:
    WeatherData * GetWeatherData(); // Different ways
};

int main() {
    DataCenter *center = new DataCenter;
    DisplayA *da = new DisplayA;
    DisplayB *db = new DisplayB;
    float temper = center->CalcTemperature();
    da->Show(temper);
    db->Show(temper);
    return 0;
}
// Terminal changes (add and delete) Data centers should not be affected by terminal changes

  • Implement using observer mode
#include <vector>

class IDisplay {
public:
    virtual void Show(float temperature) = 0;
    virtual ~IDisplay() {}
};

class DisplayA : public IDisplay {
public:
    virtual void Show(float temperature);
};

class DisplayB : public IDisplay{
public:
    virtual void Show(float temperature);
};

class WeatherData {
};

class DataCenter {
public:
    void Attach(IDisplay * ob);
    void Detach(IDisplay * ob);
    void Notify() {
        float temper = CalcTemperature();
        for (auto iter = obs.begin(); iter != obs.end(); iter++) {
            (*iter)->Show(temper);
        }
    }

private:
    virtual WeatherData * GetWeatherData();

    virtual float CalcTemperature() {
        WeatherData * data = GetWeatherData();
        // ...
        float temper/* = */;
        return temper;
    }
    std::vector<IDisplay*> obs;
};

int main() {
    DataCenter *center = new DataCenter;
    IDisplay *da = new DisplayA();
    IDisplay *db = new DisplayB();
    center->Attach(da);
    center->Attach(db);
    center->Notify();
    
    //-----
    center->Detach(db);
    center->Notify();
    return 0;
}
  1. Other examples
    Observer mode for C++ design mode

5.3 Policy Mode

  1. Definition
    Define a series of algorithms, encapsulate them one by one, and make them interchangeable. This mode allows the algorithm to change independently of the client program that uses it. Design Mode GoF

  2. Main points

  • Policy patterns provide a series of reusable algorithms that make it easy for types to run between algorithms as needed
    Switch
  • The strategy pattern eliminates conditional judgment statements (which are actually conditional judgment statements), that is, decoupling;
  1. essence
    Separation algorithm, choose implementation

  2. Structural Diagram

  3. Example
    Demand: A certain mall has regular promotional activities on holidays. In order to increase the intensity of promotion, the specifications of National Day promotional activities are now upgraded.

  • Implement without design mode
enum VacationEnum {
	VAC_Spring,
    VAC_QiXi,
	VAC_Wuyi,
	VAC_GuoQing,
    //VAC_ShengDan,
};

// Stable changing
class Promotion {
    VacationEnum vac;
public:
    double CalcPromotion(){
        if (vac == VAC_Spring){
            // Spring Festival
        }
        else if (vac == VAC_QiXi){
            // The seventh evening of the seventh moon
        }
        else if (vac == VAC_Wuyi){
            // May 1
        }
		else if (vac == VAC_GuoQing){
			// National Day
		}
     }
    
};
  • Implement using policy mode
class Context {

};

class ProStategy {
public:
    virtual double CalcPro(const Context &ctx) = 0;
    virtual ~ProStategy();
};
// cpp
class VAC_Spring : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_QiXi : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
class VAC_QiXi1  : public VAC_QiXi {
public:
    virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_Wuyi : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_GuoQing : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};

class VAC_Shengdan : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};

// Stable changing
class Promotion {
public:
    Promotion(ProStategy *sss) : s(sss){}
    ~Promotion(){}
    double CalcPromotion(const Context &ctx){
        return s->CalcPro(ctx);
    }
private:
    ProStategy *s;
};

int main () {
    Context ctx;
    ProStategy *s = new VAC_QiXi1();
    Promotion *p = new Promotion(s);
    p->CalcPromotion(ctx);
    return 0;
}
  1. Other examples

Strategic Patterns for c++ Design Patterns

Keywords: Design Pattern

Added by Cugel on Tue, 19 Oct 2021 19:59:00 +0300