C + + design pattern

Posted by goclimb on Fri, 24 Dec 2021 05:50:10 +0100

Design pattern

1, Overview

1. Definition: a set of widely known, widely used, classified and catalogued reliable template code design experience summary.

2. Features: high cohesion and low coupling (also the ultimate goal)

3. Core idea: isolate changes and encapsulate changes

4. Significance: using design patterns can enhance the reusability of code, make the code more readable and reliable. Learning design patterns helps to improve professional quality and better understand software structure

2, Classification of design patterns:

1. Creation mode: new decoupling (5 kinds)

Features: focus on object creation and object instantiation. When creating an object, hide the logical process of creation instead of directly instantiating an object with new.

2. Structure: decoupling of objects (7 kinds)

Features: focus on the combination of classes and objects. The concept of inheritance is used to combine interfaces and define the way for composite objects to obtain new functions, so as to form a larger structure

3. Behavior type: (11 kinds)

Features: used to describe how classes and objects interact and how to assign responsibilities.

3, Design principles

1. Open and closed principle: better add than do

A software should be open to extensions, not modifications (class changes are implemented by adding code, not changing code.)

2. Dependency Inversion Principle: interface oriented

Interface oriented (abstract class) programming, not implementation oriented programming (Abstract variables and methods in abstract classes); rely on abstract interfaces rather than specific implementation classes.

3. Interface isolation principle: one interface, one function

An interface only provides one function and does not encapsulate multiple operations into the same interface (the client program does not need to rely on the unnecessary interface); the dependency between classes should be based on the smallest interface.

Benefits: interface design is limited. The smaller the granularity of interface design, the more flexible the system is

Disadvantages: the structure becomes complex, which increases the difficulty of development and reduces the maintainability

4. Richter substitution principle: implement class to replace abstract class

Any case where a parent class can be called can be replaced by a child class.

Richter's replacement principle has four meanings:

① A subclass must implement an abstract method of the parent class, but must not override an implemented method

② Subclasses can extend their own unique methods

③ When the subclass overrides or implements the parent method, the parameters should be more relaxed, otherwise the parent method will be called

④ When the subclass implements the abstract method of the parent class, the post parameter (return value) should be stricter than the parent class, otherwise the parent class method will be called

Add: advantages and disadvantages of inheritance?

Advantages: improve code reusability and scalability; Improve the openness of the project

Disadvantages: improved coupling, pulling one hair and moving the whole body; Directly inherit the properties and methods of the parent class, reducing flexibility

5. Synthetic Reuse Principle:

Use the existing object in the new object to make it a part of the existing object. Composition is preferred over inheritance. If inheritance is used, any change in the parent class may affect the child class, and composition will reduce this dependency.

Benefits: composition makes the system more flexible and reduces the coupling between classes

Disadvantages: once a class is modified, many classes need to be maintained.

6. Dimitri principle:

Classes should be as independent as possible to reduce the coupling between objects and improve the maintainability of the system

Benefits: classes are decoupled from each other, and the coupling is very low

Disadvantages: many transit classes are generated, which makes the system structure very complex.

Create - singleton mode

1, Overview

1. The singleton mode should ensure that a class has only one instantiated object and provide a global access method.

2. Implementation ideas:
① Privatization of constructors;

② Provide global static methods;

③ Define a static pointer to a variable of this class in the class.


3. Achieve:
There are about seven implementation methods of singleton mode, which are understood and analyzed by lazy and hungry.

① The difference between the hungry and lazy implementation methods lies in the timing of creation.

② Hungry Han style creates objects during initialization. It uses multithreading and will not create multiple objects. If the objects are not used, it will waste space.

③ The lazy mode is created only when it is used (whether it exists or not is created). Multithreading may produce multiple objects, which can be solved by locking (code example). The significance of the singleton mode is to share resources and reduce the memory performance overhead required for operation. It is commonly used to implement the online process pool.

2, Code example

1. Simple understanding of singleton mode

#include <iostream>
using namespace std;
​
class Singleton
{
public:
    static Singleton *getinstance()  //Provides an interface for creating objects externally
{
        static Singleton *instance = NULL; 
        //Static pointer to this class of objects, unique
        //static modifies a class member variable, indicating that this variable is shared by all instantiated objects
        if(instance == NULL)  //Once created, it will not be empty and will not be created again
        {
            objcount++;
            instance = new Singleton;
        }
        return instance;
    }
​
    static int getObjcount()
{
        return objcount;
    }
​
    static void release(Singleton *instance)
{
        objcount--;
        if(objcount == 1 && instance != NULL)
        {
            delete instance;
            instance = NULL;
        }
    }
private:
    static int objcount;
};
​
int Singleton::objcount = 0;
​
int main()
{
    Singleton *s1 = Singleton::getinstance();
    Singleton *s2 = Singleton::getinstance();
    Singleton *s3 = Singleton::getinstance();
​
    cout << Singleton::getObjcount() << endl;  
    //The print result is 1, which indicates that objcount is created only once after three objects are created,
    //Only one object was created
​
    cout << s1 << endl;
    cout << s2 << endl;
    cout << s3 << endl;  
    //The printed addresses of s1, s2 and s3 are the same, which further verifies the above statement
    Singleton::release(s1);
​
    return 0;
}

2. Lazy singleton mode

​//Lazy singleton: using multithreading before an object is created may create multiple objects
//This example solves the defect by locking and mutual exclusion
#include <iostream>
#include <pthread.h>
using namespace std;
​
pthread_mutex_t mutex;
​
class Singleton
{
public:
    static Singleton *getinstance()  //Provides an interface for creating objects externally
{
        if(instance == NULL)  //Judge whether it is empty before creating an object
        {
            objcount++;
            instance = new Singleton;
        }
        return instance;
    }
​
    static int getObjcount()
{
        return objcount;
    }
​
    static void release(Singleton *instance)
{
        objcount--;
        if(objcount == 1 && instance != NULL)
        {
            delete instance;
            instance = NULL;
        }
    }
private:
    Singleton()//Constructor privatization
    {
        cout << "Singleton" << endl;  //Printed once
    } 
//Supplement: the significance of constructor privatization
//After the constructor is privatized, it can only be created through the static static member function (getinstance in this example),
//Objects of the class and its subclasses cannot be constructed outside the class
private:
    static int objcount;
    static Singleton *instance;  //Create objects internally
};
​
int Singleton::objcount = 0;
Singleton *Singleton::instance = NULL;
​
void *thread(void *arg)
{   
    pthread_mutex_lock(&mutex);
    Singleton *s = Singleton::getinstance();
    cout << s << endl;  //Multiple threads print the same address
    pthread_mutex_unlock(&mutex);
    return NULL;
}
​
int main()
{
    void *status;
​
    pthread_t pid[10];
    for(int i = 0; i < 10; i++)
    {
        pthread_create(&pid[i], NULL, thread, NULL );
    }
    for(int i = 0; i < 10; i++)
    {
        pthread_join(pid[i],&status);
    }
    return 0;
}

3. Hungry Han single instance mode

//Hungry man singleton: there will be no thread synchronization problem
#include <iostream>
#include <pthread.h>
using namespace std;
​
pthread_mutex_t mutex;
​
class Singleton
{
public:
    static Singleton *getinstance()  //Provides an interface for creating objects externally
{
        objcount++;
        return instance;
    }
​
    static int getObjcount()
{
        return objcount;
    }
​
    static void release(Singleton *instance)
{
        objcount--;
        if(objcount == 0 && instance != NULL)
        {
            delete instance;
            instance = NULL;
        }
    }
private:
    Singleton()//Constructor privatization
    {
        cout << "Singleton" << endl;  //Printed once
    } 
private:
    static int objcount;
    static Singleton *instance;
};
​
int Singleton::objcount = 0;
Singleton *Singleton::instance = new Singleton;  //Direct initialization creation
​
void *thread(void *arg)
{   
   // pthread_ mutex_ lock(&mutex);// No locking required
    Singleton *s = Singleton::getinstance();
    cout << s << endl;  //The same address is printed without lock
    //pthread_mutex_unlock(&mutex);
    return NULL;
}
​
int main()
{
    void *status;
    
    pthread_t pid[10];
    for(int i = 0; i < 10; i++)
    {
        pthread_create(&pid[i], NULL, thread, NULL);
    }
    for(int i = 0; i < 10; i++)
    {
        pthread_join(pid[i],&status);
    }
    return 0;
}

Creation - factory mode

1, Overview

1. Factory mode decouples the process of creating new objects to reduce their coupling.

2. Classification:
① Simple factory mode: also known as static factory method, a core factory is responsible for the creation of all products

Composition: abstract product - > concrete product, factory (producing different concrete products)

Benefits: the product pointer is separated from the specific product to realize the decoupling of new

Disadvantages: the factory type needs to be changed to add products, which does not comply with the opening and closing principle

② Factory method pattern: also known as polymorphic factory pattern, different products are allocated to different subclasses to produce through abstract factory classes.

Composition: abstract product class - > concrete product class, abstract factory class - > concrete factory class

Advantages: it meets the opening and closing principle, and the new products do not need to change the original factory class

③ Abstract factory pattern
Composition: basically the same as the factory method mode, the difference is that different product families can be produced.

④ Advantages and disadvantages: understood in code examples.

2, Code example

1. Simple factory mode

//Simple factory mode:
//Automobile - > BMW / Mercedes Benz / Audi (three different sub categories)  
//Factory - > can produce BMW / Mercedes Benz / Audi   
#include <iostream>
using namespace std;
​
class Car
{
public:
    virtual void construct() = 0; //Abstract a function to create a product
};
​
class BMW : public Car
{
public:
    void construct()
{
        cout << "BMW" << endl;
    }
};
​
class BENZ : public Car
{
public:
    void construct()
{
        cout << "BENZ" << endl;
    }
};
​
class AUDI : public Car
{
public:
    void construct()
{
        cout << "AUDI" << endl;
    }
};
​
class Factory  //Factory: responsible for creating objects
{
public:
    Car *createBMW()
{
        return new BMW;
    }
​
    Car *createBENZ()
{
        return new BENZ;
    }
​
    Car *createAUDI()
{
        return new AUDI;
    }
};
​
int main()
{
    //Car *bmw = new BMW / / create a car object directly
    Car * c = NULL;  //The car pointer does not directly point to the car object
    Factory *f = new Factory; //Create a car factory
    c = f->createBMW();  //The creation of objects is left to the factory
    c->construct();
​
    c = f->createBENZ();
    c->construct();
​
    return 0;
}

2. Factory method mode

//Factory method mode:
//Automobile - > BMW / Mercedes Benz / Audi (three different sub categories)
//Factory - > BMW / Mercedes Benz / Audi (three different sub categories)   
#include <iostream>
using namespace std;
​
class Car
{
public:
    virtual void construct() = 0; //Create product function
};
​
class BMW : public Car
{
public:
    void construct()
{
        cout << "BMW" << endl;
    }
};
​
class BENZ : public Car
{
public:
    void construct()
{
        cout << "BENZ" << endl;
    }
};
​
class AUDI : public Car
{
public:
    void construct()
{
        cout << "AUDI" << endl;
    }
};
​
class BIEKE : public Car
{
public:
    void construct()
{
        cout << "BIEKE" << endl;
    }
};
​
class Factory  //Abstract factory
{
public:
   virtual Car *create() = 0;
};
​
class BMWf : public Factory //BMW factory
{
public:
    Car *create()
{
        return new BMW;
    }
};
​
class BENZf   : public Factory//Mercedes Benz factory
{
public:
    Car *create()
{
        return new BENZ;
    }
};
​
class AUDIf  : public Factory //Audi factory
{
public:
    Car *create()
{
        return new AUDI;
    }
};
​
class BIEKEf : public Factory //Buick factory
{
public:
    Car *create()
{
        return new BIEKE;
    }
};
​
int main()
{
    //Car *bmw = new BMW / / create a car object directly
​
    Car * c = NULL;  //The car pointer does not directly point to the car object
    Factory *f = new BMWf; //Create a car factory
    c = f->create();  //The creation of objects is left to the factory
    c->construct();
    return 0;
}

3. Abstract factory mode

//Abstract factory pattern:
//Fruits can be divided into pears and apples, as well as North and south. Southern apples and Northern apples are the same family
//To some extent, the abstract factory conforms to the opening and closing principle, such as adding a South factory after the original North factory
//But it does not conform to the opening and closing principle. For example, if I add a new fruit peach, I need to change the factory class
#include <iostream>
using namespace std;
​
class Fruit
{
public:
    virtual void show() = 0;
};
​
class NorthPear :public Fruit
{
public:
    void show()
{
        cout << "Northern pear" << endl;
    }
};
class SouthPear :public Fruit
{
public:
    void show()
{
        cout << "Southern pear" << endl;
    }
};
class NorthApple :public Fruit
{
public:
    void show()
{
        cout << "Northern apple" << endl;
    }
};
class SouthApple :public Fruit
{
public:
    void show()
{
        cout << "Southern apple" << endl;
    }
};
​
class Factory
{
public:
    virtual Fruit *createPear() = 0;  //Production of pears
    virtual Fruit *createApple() = 0; //Apple production
};
​
class Northf : public Factory
{
public:
    Fruit *createPear()
{
        return new NorthPear;
    }
​
    Fruit *createApple()
{
        return new NorthApple;
    }
};
​
class Southf : public Factory
{
public:
    Fruit *createPear()
{
        return new SouthPear;
    }
​
    Fruit *createApple()
{
        return new SouthApple;
    }
};
​
void create(Factory *f)
{
    Fruit *fr = NULL;
    fr = f->createApple();
    fr->show();
​
    fr = f->createPear();  
    fr->show();
}
​
int main()
{
    Factory *f = new Northf;  //Create a northern factory
    create(f);
    delete f;
​
    f = new Southf;  //Create Southern factory
    create(f);
    delete f;
​
    return 0;
}

Creative - builder mode

1, Overview

1. The builder mode separates the creation of complex objects and components, and selects components to construct objects through the builder.

2. Composition: specific product, abstract Builder - > specific builder, command

3. Advantages: good encapsulation

4. Defects: if the production process changes, many classes need to be modified

5. Difference from factory mode: compared with factory mode, builder mode considers the specific assembly process of products, while factory mode only cares about product categories.

2, Code demonstration

1. Automobile production

//Builder pattern 
//Car - > engine, tires, body, etc. need to be assembled, and different models can be matched
#include <iostream>
#include <string>
using namespace std;
​
class Car
{
public:
    virtual void setEngine(string engine){m_engine = engine;} //engine
    virtual void setWheel(string wheel){m_wheel = wheel;} //tyre
    virtual void setBody(string body){m_body = body;} //body
    void display()
{
        cout << "The configuration of this car is:" << m_engine << "," 
        << m_wheel << "," << m_body << endl;
    }
private:
    string m_engine; //engine
    string m_wheel; //tyre
    string m_body;  //body
};
​
class Assemble //Abstract classes for assembling cars
{
public:
    virtual void assembleEngine() = 0; //Assemble engine
    virtual void assembleWheel() = 0; //Assemble the tire
    virtual void assembleBody() = 0; //Assemble the body
    virtual Car *getCar() = 0;
};
​
class AssembleA : public Assemble
{
public:
    AssembleA(){carA = new Car;} //Create Car object
    void assembleEngine(){carA->setEngine("engine A");}
    void assembleWheel(){carA->setWheel("tyre A");}
    void assembleBody(){carA->setBody("body A");}
    
    Car *getCar(){return carA;}
private:
    Car *carA;
};
​
class AssembleB : public Assemble
{
public:
    AssembleB() {carB = new Car;} //Create Car object
    void assembleEngine(){carB->setEngine("engine B");}
    void assembleWheel(){carB->setWheel("tyre B");}
    void assembleBody(){carB->setBody("body B");}
    
    Car *getCar(){return carB;}
private:
    Car *carB;
};
​
class Constructor
{
public:
    Constructor(Assemble *assemble){m_Assemble = assemble;}
    void assembleCar()
{
        m_Assemble->assembleEngine();
        m_Assemble->assembleWheel();
        m_Assemble->assembleBody();
    }
    Car *getCar(){return m_Assemble->getCar();}
private:
    Assemble *m_Assemble;
};
​
int main()
{
    Assemble *m = new AssembleA();  //Type A vehicle order
    Constructor *f = new Constructor(m);  //Instruct the construction of A type A vehicle
    f->assembleCar();//Build - > getcar ()
    f->getCar()->display();
    delete f; 
    delete m;
​
    m = new AssembleB();  //B-type car order
    f = new Constructor(m);  //Instruction construction
    f->assembleCar();//Build - > getcar ()
    f->getCar()->display();
    delete f; 
    delete m;
​
    return 0;
}

2. House construction

/*
Builder mode: building a house
 House properties: windows, doors, walls
 House type: ordinary, high-grade, builder
*/
#include <iostream>
#include <string>
using namespace std;
​
class House
{
public:
    virtual void setDoor(string door){m_door = door;};
    virtual void setWindow(string window){m_window = window;};
    virtual void setWall(string wall){m_wall = wall;} ;
    void display()
{
        cout << "House configuration:" << m_door << " ," 
        << m_window << " ," << m_wall << endl;
    }
private:
    string m_door;
    string m_window;
    string m_wall;
};
​
class Assemble : public House
{
public:
    virtual void assembleDoor() = 0;
    virtual void assembleWindow() = 0;
    virtual void assembleWall() = 0;
    virtual House* getHouse() = 0;
};
​
​class AssembleA : public Assemble
{
public:
    AssembleA(){houseA = new House;}
​
    void assembleDoor(){houseA->setDoor("High end door");}
    void assembleWindow(){houseA->setWindow("High end window");}
    void assembleWall(){houseA->setWall("High end wall");}
  
    House* getHouse(){return houseA;}
private:
    House *houseA;
};
​
class AssembleB : public Assemble
{
public:
    AssembleB(){houseB = new House;}
    void assembleDoor(){houseB->setDoor("Low end door");}
    void assembleWindow(){houseB->setWindow("Low end window");}
    void assembleWall(){houseB->setWall("Low end wall");}
​
    House* getHouse(){return houseB;}
private:
    House *houseB;
};
​
class AssembleC : public Assemble
{
public:
    AssembleC(){houseC = new House;}
    void assembleDoor(){houseC->setDoor("No door");}
    void assembleWindow(){houseC->setWindow("No windows");}
    void assembleWall(){houseC->setWall("Low end wall");}
​
    House* getHouse(){return houseC;}
private:
    House *houseC;
};
​
class Constructor
{
public:
    Constructor(Assemble *as){a = as;}
    void assembleHouse()
{
        a->assembleDoor();
        a->assembleWindow();
        a->assembleWall();
    }
​
    House * getHouse()
{
        return a->getHouse();
    }
private:
    Assemble *a;
};
​
int main()
{
    Assemble *a1 = new AssembleA();
    Constructor *h1 = new Constructor(a1);
    h1->assembleHouse();
    h1->getHouse()->display();
    delete a1,h1;
    
    a1 = new AssembleB();
    h1 = new Constructor(a1);
    h1->assembleHouse();
    h1->getHouse()->display();
    delete a1,h1;
   
    a1 = new AssembleC();
    h1 = new Constructor(a1);
    h1->assembleHouse();
    h1->getHouse()->display();
    delete a1,h1;
    a1 = NULL;
    h1 = NULL;
​
    return 0;
}

Create - prototype mode (clone mode)

1, Overview

1. Prototype mode creates new objects by copying objects. Simplify the process of creating new objects.

2. Implementation: write a clone function inside the class

2, Code demonstration

//Clone mode
#include <iostream>
using namespace std;
​
class Person
{
public:
    Person(){}
    Person(string n):name(n){}
    string getName()
{
        return name;
    }
​
    virtual void show() = 0;
    virtual void setId(int i) = 0;
​
    virtual Person *clone() = 0;  //Clone function, clone mode
​
protected:
    string name;
};
​
class Student : public Person
{
public:
    Student(){}
    Student(string n,int i):Person(n),id(i){}
    void show()
{
        cout << "Student Name:" << name << ", id: " << id << endl;
    }
    void setId(int i)
{
        id = i;
    }
    Person *clone()
{
        Student *temp = new Student;
        *temp = *this;
        return temp;
    }
private:
    int id;
};
​
int main()
{
    Person *s1 = new Student("Zhang San",1);
    s1->show();
    Person *s2 = s1->clone();
    s2->show();
    
    s2->setId(2);
    s1->show();
    s2->show();
​
    return 0;
}

Structural - adapter mode

1, Overview

1. Adapter mode: adding or deleting functions to classes can adapt to other incompatible classes.

2. Classification:
① Class adapter: implement multiple inheritance and provide an adapted interface

② Object adapter: single inheritance, wrapping the adapted object and providing the adapted interface

3. Differences:

The class adapter is adapted according to the whole class, and the object cannot be selected, that is, all USB is converted to typeC in this example; The object adapter adapts to specific incoming objects.

2, Code demonstration

1. Class adapter

//Class Adapter 
#include <iostream>
using namespace std;
​
class USB
{
public:
    virtual void isUSB()
{
        cout << "USB Interface" << endl;
    }
};
​
class TypeC
{
public:
    void isTypeC()
{
        cout << "TypeC Interface" << endl;
    }
};
​
class adapter : public USB,public TypeC //Multiple inheritance
{
public:
    void isUSB()
{
        isTypeC();  //Replace USB with TypeC
    }
};
​
int main()
{
    USB *usb = new adapter;
    usb->isUSB();
​
    return 0;
}

2. Object adapter

//object adapter 
#include <iostream>
using namespace std;
​
class USB
{
public:
    virtual void isUSB()
{
        cout << "USB Interface" << endl;
    }
};
​
class TypeC
{
public:
    void isTypeC()
{
        cout << "TypeC Interface" << endl;
    }
};
​
class adapter : public USB  //Single inheritance
{
public:
    void isUSB()
{
        p->isTypeC();//Calling member methods through objects
    }
private:
    TypeC *p = new TypeC;
};
​
int main()
{
    USB *usb = new adapter;
    usb->isUSB();
​
    return 0;
}

Structural - appearance mode

1, Overview

1. Appearance mode is the integration of interfaces. A unified interface is used to access a group of interfaces of subsystems. By simplifying the class interface, the complex process is encapsulated internally, and only simple interfaces are provided externally.

2. Composition: appearance class, subsystem class

3. Advantages: reduce interdependence between classes; The implementation of user hiding subsystem reduces the coupling between user and subsystem; The subsystem method is relatively closed and safe.

4. Disadvantages: for business changes, the appearance class has to be modified, which does not comply with the opening and closing principle

2, Code demonstration

//Intelligent Home Furnishing system
//Light, TV and sound are subsystems, and various modes are appearance
#include <iostream>
using namespace std;

class LightSystem
{
public:
    void on()
{
        cout << "turn on the light" << endl;
    }
    void off()
{
        cout << "Turn off the lights" << endl;
    }
};

class MovieSystem
{
public:
    void on()
{
        cout << "turn on TV" << endl;
    }
    void off()
{
        cout << "Turn off the TV" << endl;
    }
};

class SoundSystem  
{
public:
    void on()
{ 
        cout << "Turn on the stereo" << endl;
    }
    void off()
{
        cout << "Turn off the sound" << endl;
    }
};

class HomeSystem  //Home system: user oriented, simplest interface
{
public:
    void openKTV()
{
        cout << "get into KTV pattern:" << endl;
        l.off();
        m.off();
        s.on();
    }

    void openMV()
{
        cout << "Viewing mode" << endl;
        l.off();
        m.on();
        s.off();
    }

    void sleep()
{
        cout << "Sleep mode" << endl;
        l.off();
        m.off();
        s.off();
    }

private:
    LightSystem l;
    MovieSystem m;
    SoundSystem s;
};

int main()
{
    HomeSystem home;
    home.openKTV();

    return 0;
}

Structured - agent model

1, Overview

  1. Proxy mode creates a proxy object to access the original object. The proxy object acts as a mediator between the client and the target object

  2. Structure: Abstract role class, proxy role class, real role class

  3. Benefits: proxy mode protects the target object; The proxy object is separated from the target object, which reduces the system coupling and enhances the scalability.

  4. Disadvantages: directly adding agents in the target object and client reduces the operation efficiency; Agent mode will cause a large number of system classes and make the system more complex.

2, Code demonstration

//proxy pattern
#include <iostream>
#include <string>
​
using namespace std;
​
class Person
{
public:
    virtual void renthouse() = 0;
​
};
​
class Renter : public Person  //tenant
{
public:
    void renthouse()
{
        cout << "I want a house" << endl;
    }
};
​
class Intermediary : public Person  //agent
{
public:
    Intermediary(Person *p):m_person(p){}
    void renthouse()
{
        m_person->renthouse();
        cout << "Hello, I'm an agent. I'll help you find a house" << endl;
    }
private:
    Person *m_person;  //References to real objects
};
​
int main()
{
    Person *p = new Renter;  //Tenant p
    Person *intermediary = new Intermediary(p); //Tenant agent, bind tenant p
    intermediary->renthouse();  //Agent to find a house
​
    return 0;
}

Structural type - decoration mode (packaging mode)

1, Overview

  1. Decoration pattern extends the functions of classes in a transparent and dynamic way, which is an alternative to inheritance.

  2. Composition: original abstract class of decorated object, concrete decorated object class, abstract decoration class and concrete decoration class

  3. Benefits: the decoration pattern can be dynamically combined and extended freely, which is more flexible than inheritance

  4. Disadvantages: many small classes will be generated, making the program complex.

2, Code example

/*
Decoration mode: making milk tea
 Types of milk tea (specific objects to be decorated): coco: 6, tea Baidao: 5.5, a little bit: 5
 Seasoning (decoration): Ice: 0.5, pearl: 2, pudding: 2.5, taro balls: 3, sugar: 1
 Display: for example: Half sugar to ice taro round, a little milk tea, display the price
*/
#include <iostream>
#include <string>
​
using namespace std;
​
class MilkTea  //Milk tea base
{
public:
    virtual void show() = 0;
    virtual double getPrice() = 0;
};
​
class CoCo : public MilkTea  //CoCo milk tea
{
public:
    CoCo(){}
    CoCo(string name):m_name(name){}
​
    void show()
{
        cout << endl << m_name << "(6 element)" ;
    }
    double getPrice()
{
        return 6;
    }
​
private:
    string m_name;
};
​
class ChaBaiDao : public MilkTea //Tea Baidao milk tea
{
public:
    ChaBaiDao(){}
    ChaBaiDao(string name):m_name(name){}
​
    void show()
{
        cout << endl << m_name << "(5.5 element)" ;
    }
    double getPrice()
{
        return 5.5;
    }
private:
    string m_name;
};
​
class OnePoint : public MilkTea // A little milk tea
{
public:
    OnePoint(){}
    OnePoint(string name):m_name(name){}
​
    void show()
    {
        cout << endl << m_name << "(5 element)" ;
    }
     double getPrice()
{
        return 5;
    }
private:    
    string m_name;
};
​
class Decorator : public MilkTea //Decorated base class
{
public:
    void decorator(MilkTea *mt)
{
        this->mt = mt;
    }
​
    void show()
{
        if(mt != NULL)
        {
            mt->show();
        }
    }
protected:
    MilkTea *mt;       
};
​
class Ice : public Decorator
{
public:
    void show()
{   
        Decorator::show();
        cout << " + ice(0.5 element)" ;
    }
    double getPrice()
{
        return mt->getPrice() + 0.5;
    }
};
​
class Pearl: public Decorator
{
public:
    void show()
{
        Decorator::show();
        cout << " + Pearl(2 element)" ;
    }
    double getPrice()
{
        return mt->getPrice() + 2;
    }
};
​
class Sugar : public Decorator
{
public:
    void show()
{
        Decorator::show();
        cout << " + sugar(1 element)" ;
    }
    double getPrice()
{ 
        return mt->getPrice() + 1;
    }
};
​
​
class Pudding : public Decorator  
{
public:
    void show()
{    
        Decorator::show();
        cout << " + Pudding(2.5 element)" ;
    }
    double getPrice()
{
        return mt->getPrice() + 2.5;
    }
};
​
int main()
{
    MilkTea *mt = new CoCo("CoCo tea with milk");
    Ice *ic = new Ice;
    Sugar *sg = new Sugar;
    Pearl *pl = new Pearl;
    ic->decorator(mt);
    sg->decorator(ic);
    pl->decorator(sg);
    pl->show();
    cout << endl << "The total price is:" << pl->getPrice() << endl;
    //CoCo milk tea (6 yuan) + ice (0.5 yuan) + sugar (1 yuan) + Pearl (2 yuan)
    //The total price is: 9.5
    delete mt,ic,sg,pl;
​
    mt = new OnePoint("A little milk tea");
    ic = new Ice;
    sg = new Sugar;
    pl = new Pearl;
    Pudding *pd = new Pudding;
    ic->decorator(mt);
    sg->decorator(ic);
    pl->decorator(sg);
    pd->decorator(pl);
    pd->show();
    cout << endl << "The total price is:" << pd->getPrice() << "element" << endl;
    //A little milk tea (5 yuan) + ice (0.5 yuan) + sugar (1 yuan) + Pearl (2 yuan) + pudding (2.5 yuan)
    //The total price is 11 yuan
    delete mt,ic,sg,pl;
   
    return 0;
}

Structural - bridging mode

1, Overview

1. The bridge mode separates abstract objects from concrete objects and connects them through concrete classes so that they can change independently.

2. Composition: abstract part, realization of abstract part, concrete part, realization of concrete part.

3. Advantages: transparent details and good scalability

4. Disadvantages: it is difficult to understand and design the system.

2, Code demonstration

//Bridging mode
//Color: red, green, etc; Pencils: large, small, etc
#include <iostream>
#include <string>
using namespace std;
​
class Color  //Abstract color class
{
public:
    virtual void paint(const string &pentype,const string &name) = 0;
};
​
class Red : public Color
{
public:
    void paint(const string &pentype,const string &name)
{
        cout << pentype << "red" << name << endl;
    }
};
​
class Green : public Color
{
public:
    void paint(const string &pentype,const string &name)
{
        cout << pentype << "green" << name << endl;
    }
};
​
class Pen  //Abstract pen
{
public:
    virtual void draw(const string &name) = 0;
    void setColor(Color *c)
{
        this->c = c;
    }
protected:
    Color *c;
};
​
class smallPencil :public Pen
{
public:
    void draw(const string &name)
{
        string pentype = "Small pencil drawing";
        this->c->paint(pentype,name);
    }
};
​
class Pencil :public Pen
{
public:
    void draw(const string &name)
{
        string pentype = "Pencil drawing";
        this->c->paint(pentype,name);
    }
};
​
​
int main()
{
    Color *c = new Red;
    Pen *p = new Pencil;
//Define a pen object and a red object; Give the red object to the pen object and call the pen object to draw the sun
    p->setColor(c);
    p->draw("sun");
​
    delete c;
    delete p;
​
    return 0;
}

Structural - sharing mode

1, Overview

1. Meta sharing mode is an implementation of object pool, which can avoid repeatedly creating and destroying objects. You only need to create a shared element pool. When you need objects, you can get them from the pool. If they don't exist, you can create them again. Shared meta mode separates the shareable attribute States and turns them into shared meta objects. Non shareable attributes are processed separately.

2. Composition: Abstract shared meta class, specific shared meta class, shared meta factory class, and non shared class

3. Benefits: reduce the number of objects, reduce memory overhead and enhance system performance; If the external state is relatively independent and does not affect the internal state, the shared meta object can be shared in different environments.

4. Disadvantages: complicate the system logic

2, Code demonstration

#include <iostream>
#include <string>
#include <vector>
​
using namespace std;
​
enum pieceColor
{
    black,
    white
};
​
class Piece  //Abstract chess subclass
{
public:
    Piece(pieceColor color):m_color(color){}
​
    virtual void draw() = 0;  //Luozi
​
private:
    pieceColor m_color;
};
​
class BlackPiece : public Piece
{
public:
    BlackPiece(pieceColor color) : Piece(color){}
​
    void draw()
{
        cout << "A black chess piece fell" << endl;
    }
};
​
class WhitePiece : public Piece
{
public:
    WhitePiece(pieceColor color) : Piece(color){}
​
    void draw()
{
        cout << "Drop a white chess piece" << endl;
    }
};
​
class PiecePos  //Coordinate class
{
public:
    PiecePos(int a,int b):x(a),y(b){}
public:
    int x;  //Abscissa
    int y;  //Ordinate
};
​
class PieceBoard  //chessboard 
{
public:
    PieceBoard(string bname,string wname) 
    : m_blackName(bname),m_whiteName(wname)
    {
        m_black = NULL;
        m_white = NULL;
    }
​
    ~PieceBoard()
    {
        delete m_black;
        delete m_white;
    }
​
    void setPiece(pieceColor color,PiecePos pos)
{
        if(color == black)
        {
            if(m_black == NULL)  //Object for judging whether there are chessmen
            {
                m_black = new BlackPiece(color); //Create black
            }
            cout << m_blackName << "In position(" 
            << pos.x << "," << pos.y << ")" ;
            m_black->draw();
            m_pos.emplace_back(pos);
        }
        else
        {
            if(m_white == NULL)  //Object for judging whether there are chessmen
            {
                m_white = new WhitePiece(color); //Create black
            }
            cout << m_whiteName << "In position(" 
            << pos.x << "," << pos.y << ")" ;
            m_white->draw();
            m_pos.emplace_back(pos);
        }
    }
private:
    vector<PiecePos> m_pos;  //Falling position sequence
    Piece *m_black;  //sunspot
    Piece *m_white;  //White characters
    string m_blackName;  //Sunspot man
    string m_whiteName;  //A white man
};
​
int main()
{
    PieceBoard p("A","B");
    p.setPiece(black,PiecePos(8,8));
    p.setPiece(white,PiecePos(9,9));
    p.setPiece(black,PiecePos(9,8));
    p.setPiece(white,PiecePos(10,9));
    return 0;
}

Behavioral - template method model

1, Overview

1. The template method pattern defines an algorithm skeleton in a class, and the details are handed over to subclasses for processing to improve code reusability.

2. Structure: abstract class, concrete subclass.

2, Code demonstration

//Template method pattern
#include <iostream>
using namespace std;
​
class TestPaper
{
public:
    void doPaper()
{
        studentName();
        titleOne();  //Question composition: question + answer
        titleTwo();  
    }
    void titleOne()
{
        cout << "1 + 1 = ?" << endl;
        answerOne();
    }
​
    void titleTwo()
{
        cout << "1 X 1 = ?" << endl;
        answerTwo();
    }
​
    virtual void studentName() = 0;
    virtual void answerOne() = 0;
    virtual void answerTwo() = 0;
};
​
class NolStudent1 : public TestPaper
{
public:
    void studentName()
{
        cout << "Name: Zhang San" << endl;
    }
​
    void answerOne()
{
        cout << "Answer: 2" << endl;
    }
    void answerTwo()
{
        cout << "Answer: 1" << endl;
    }
};
class NolStudent2 : public TestPaper
{
public:
    void studentName()
{
        cout << "Name: Li Si" << endl;
    }
​
    void answerOne()
{
        cout << "Answer: 3" << endl;
    }
    void answerTwo()
{
        cout << "Answer: 1" << endl;
    }
};
int main()
{
    NolStudent1 p1;
    p1.doPaper();
    cout << endl;
​
    NolStudent2 p2;
    p2.doPaper();
    return 0;
}

Behavioral observer model

1, Overview

  1. Observer mode defines a one to many dependency. Multiple observers monitor the observed and stop the observer when the state of the observed changes. It realizes the synchronous communication between classes.

  2. Structure: Abstract observed - > concrete observed; Abstract Observer - > concrete observer

  3. Benefits: in line with the opening and closing principle, the boundaries between modules are clear, and the code reusability and maintainability are enhanced.

  4. Disadvantages: the code structure will become complex; Beware of system crash caused by cyclic dependency; Only the observed state can be received, and its state cannot be affected

2, Code demonstration

 //Observer mode: anchor, subscriber
#include <iostream>
#include <string>
#include <set>
using namespace std;
​
class ObserveBase  //Observer base class
{
public:
    virtual const string getName() const = 0;  //Display name
    virtual void update(const string &words) = 0;  //Update status
    virtual bool operator==(const ObserveBase *outer) const = 0;
};
​
class TargetBase //Observed base class
{
public:
    virtual void addOneFollower(ObserveBase *outer) = 0;  //Add subscriber
    virtual void rmOneFollower(ObserveBase *outer) = 0;  //Delete subscriber
    virtual void sendMessage(const string &words) = 0;  //Release news
};
​
class ObserveFan : public ObserveBase
{
public:
    ObserveFan(const string &name) : m_name(name){} 
​
    void update(const string &words)
{
        this->news = words;
        cout << "I am" << m_name << ", The anchor told me:" << news << endl;
    }
​
    const string getName() const
{
        return m_name;
    }
​
    bool operator==(const ObserveBase *outer) const
    {
        cout << "Reload!" << endl;
        return outer->getName() == this->m_name;
    }
private:
    string m_name;
    string news;
};
​
class TargetHost : public TargetBase //Observed, anchor
{
public:
    void addOneFollower(ObserveBase *outer)
{
        fans.insert(outer);
        cout << outer->getName() << "Subscribed" << endl;
    }
​
    void rmOneFollower(ObserveBase *outer)
{
        for(auto it = fans.begin(); it != fans.end(); it++)
        {
            if( **it == outer)
            {
                fans.erase(it);
                cout << outer->getName() << "Delete succeeded" << endl;
                break;                
            }
        }
    }
​
    void sendMessage(const string &words)
{
        this->words = words;
        cout << "Anchor release message:" << this->words << endl;
        for(auto it = fans.begin(); it != fans.end(); it++)
        {
            (*it)->update(this->words);
        }
    }
private:
    set<ObserveBase *>  fans;  //fans
    string words;  //Anchor status
};
​
int main()
{
    TargetHost *lbw = new TargetHost;
    
    ObserveBase *fan1 = new ObserveFan("Zhang San");
    ObserveBase *fan2 = new ObserveFan("Li Si");
​
    lbw->addOneFollower(fan1);
    lbw->addOneFollower(fan2);
​
    lbw->sendMessage("Live at eight tonight!");
    return 0;
}

Behavioral responsibility chain model

1, Overview

1. The responsibility chain mode is the processing of different levels of a request.

2. Structure: Abstract handler, concrete handler

3. Precautions:

① The responsibility chain itself does not create a responsibility chain

② It effectively reduces the coupling between the request sender and the request receiver, and is easy to expand and modify

③ The responsibility chain can be a line, a tree and a ring

④ Avoid circular dependencies and be able to handle all requests

2, Code demonstration

//Responsibility chain model, salary increase request
#include <iostream>
#include <string>
using namespace std;
​
class Request
{
public:
    int salary;
};
​
class Manager
{
public:
    Manager(string name):name(name){}
    void setRequest(Manager *tem){manager = tem;}
​
    virtual void solveRequest(Request *r) = 0;
protected:
    Manager *manager;
    string name;
};
​
class commonManager:public Manager //Department leader
{
public:
    commonManager(string name):Manager(name){}
    void solveRequest(Request *r)
{
        if(r->salary <= 500)
        {
            cout << name << "Passed the raise" << r->salary << "Your request!" << endl;
        }
        else
        {
            manager->solveRequest(r);
        }
    }
};
​
class Major:public Manager //Leader in charge
{
public:
    Major(string name):Manager(name){}
    void solveRequest(Request *r)
{
        if(r->salary <= 1000 && r->salary > 500)
        {
            cout << name << "Passed the raise" << r->salary 
            << "Your request!" << endl;
        }
        else
        {
            manager->solveRequest(r);
        }
    }
};
​
​
class generalManager:public Manager //Leader in charge
{
public:
    generalManager(string name):Manager(name){}
    void solveRequest(Request *r)
{
        if(r->salary > 1000)
        {
            cout << name << "Passed the raise" << r->salary << "Your request!" << endl;
        }
        else
        {
            manager->solveRequest(r);
        }
    }
};
​
int main()
{
    Manager *common = new commonManager("Manager Zhang");
    Manager *major = new Major("Supervisor Li");
    Manager *general = new generalManager("Zhao Zong");
​
    common->setRequest(major);
    major->setRequest(general);
​
    Request *r = new Request;
    r->salary = 800;
    common->solveRequest(r);
​
    return 0;
}

Behavioral intermediary model

1, Overview

1. The mediator pattern is to use a mediator object to interact with a series of encapsulated classes, so as to reduce the coupling between them and make them more loose.

2. Structure: Abstract intermediary - > concrete intermediary; Abstract colleague class - > concrete colleague class

3. Advantages: reduce the dependency between classes and reduce coupling.

4. Disadvantages: intermediaries will become very complex and bloated.

2, Code demonstration

//Intermediary model: Intermediary house purchase
#include <iostream>
#include <string>
using namespace std;
​
class HousePerson;
​
class Mediator
{
public:
    virtual void sendMessage(string msg,HousePerson *p) = 0;
};
​
class HousePerson
{
public:
    HousePerson(Mediator *m)
    {
        m_me = m;
    }
protected:
    Mediator *m_me;
};
​
class BuyHousePerson : public HousePerson
{
public:
    BuyHousePerson(Mediator *m):HousePerson(m){}
    void sendMessage(string msg)
{
        m_me->sendMessage(msg,this);
    }
​
    void notify(string msg)
{
        cout << "The buyer received a message:" << msg << endl;
    }
};
​
class SellHousePerson : public HousePerson
{
public:
    SellHousePerson(Mediator *m):HousePerson(m) {}
    void sendMessage(string msg)
{
        m_me->sendMessage(msg,this);
    }
​
    void notify(string msg)
{
        cout << "The seller received a message:" << msg << endl;
    }
​
};
​
class ConcreteMediator : public Mediator
{
public:
    void sendMessage(string msg,HousePerson *p)
{
        if(p == b)
        {
            s->notify(msg);
        }
        else
        {
            b->notify(msg);
        }
    }
public:
    BuyHousePerson *b;
    SellHousePerson *s;
};
​
int main()
{
    ConcreteMediator *c = new ConcreteMediator;
    BuyHousePerson *b = new BuyHousePerson(c);
    SellHousePerson *s = new SellHousePerson(c);
​
    c->b = b;
    c->s = s;
​
    b->sendMessage("300 Buy or not?");
    s->sendMessage("Don't bargain, take $4 million!");
​
    return 0;
}

Behavioral - memo mode

1, Overview

1. Memo mode captures the internal state of an object without destroying the encapsulation, and saves the state outside the object, so that the changed state can be restored.

2. Structure: Backup object, attribute object, memo object and management object

3. Advantages:
① Improved the memo scene and gave the program regret medicine
② The backup data is managed by additional memo objects, and the memo is managed by the manager, which conforms to the principle of single responsibility

4. Disadvantages:
Saving all or part of the state will cause a large consumption of memory resources

2, Code demonstration

//Load Game 
#include <iostream>
#include <vector>
using namespace std;

class Memnto
{
public:
    Memnto(int vitality,int attack, int defense)
    {
        m_vitality = vitality;
        m_attack = attack;
        m_defense = defense;
    }
    
    Memnto& operator=(const Memnto &m)
    {
        m_vitality = m.m_vitality;
        m_attack = m.m_attack;
        m_defense = m.m_defense;
        return *this;
    }
public:
    int m_vitality; //Blood volume
    int m_attack; //aggressivity
    int m_defense; //Defensive power
};

class GameRole
{
public:
    GameRole()
    {
        m_vitality = 100;
        m_attack= 10;
        m_defense = 10;
    }
    
    Memnto save() //Save current information
    {
        Memnto m(m_vitality,m_attack,m_defense);
        return m;
    }

    void load(Memnto m)
    {
        m_vitality = m.m_vitality;
        m_attack = m.m_attack;
        m_defense = m.m_defense;
    }

    void show()
    {
        cout << "Current status:" << endl;
        cout << "Blood volume:" << m_vitality << endl;
        cout << "aggressivity" << m_attack << endl;
        cout << "Defensive power" << m_defense << endl << endl;
    }

    void attack()
    {
        m_vitality -= 10;
        m_defense -= 3;
        m_attack -= 2;
    }

private:
    int m_vitality;  //Blood volume
    int m_attack; //aggressivity
    int m_defense; //Defensive power
};

class Caretaker
{
public:
    void save(Memnto m)
    {
        v.emplace_back(m);
    }
    Memnto load(int state)
    {
        return v[state];
    }
private:
    vector<Memnto> v;
};

int main()
{
    Caretaker ct;  //Create progress Library
    GameRole gr; //Create role
    gr.show();//Initial value of role data

    ct.save(gr.save());  //Save status value
    gr.attack();
    cout << "After battle:" << endl;
    gr.show();  //Status after attack

    cout << "File reading..." << endl;
    gr.load(ct.load(0));
    gr.show();
    
    return 0;
}

Behavioral - command mode

1, Overview

1. The command mode encapsulates the request into an object so that other objects can be parameterized with different requests. It can also be combined with the memo mode to support revocation.

2. Structure: Abstract command interface - > concrete command interface, executor, caller

3. Advantages:
① The client and server are decoupled;
② You can dynamically add and delete commands, which is more flexible;
③ Only one calling method is needed to call different function commands.

4. Disadvantages:
A perfect command system needs to create many independent command objects

5. The command mode is suitable for use with other modes, such as responsibility chain mode and template method mode

2, Code demonstration

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

class Cooker  //Chef, executor
{
public:
    void cookeRice()
    {
        cout << "The cook cooked a bowl of rice!" << endl;
    }
    void cookeChicken()
    {
        cout << "The cook made a bowl of chicken soup!" << endl;
    }
};

class Consumer  //request
{
public:
    virtual void commond() = 0;
};

class Rice : public Consumer  //rice
{
public:
    Rice(Cooker *ck):c(ck){}

    void commond()
    {
        c->cookeRice();
    }

private:
    Cooker *c; //Command Chef
};

class Chicken : public Consumer  //Chicken soup
{
public:
    Chicken(Cooker *ck):c(ck){}

    void commond()
    {
        c->cookeChicken();
    }

private:
    Cooker *c; //Command Chef
};


class Waiter  //Waiter, caller
{
public:
    void addCmd(Consumer *pc) //Add command
    {
        l.emplace_back(pc);
    }

    void delCmd(Consumer *pc)  //Undo order
    {
        auto it = find(l.begin(),l.end(),pc);
        l.erase(it);
    }
    void sendCmd()
    {
        auto it_s = l.begin();
        auto it_e = l.end();
        while(it_s != it_e)
        {
            (*it_s)->commond();
            it_s++;
        }
    }
private:
    list<Consumer *> l;

};

int main()
{
    Cooker *ck = new Cooker;
    Consumer *cm = new Rice(ck);  
    Consumer *cm1 = new Chicken(ck);

    Waiter *w = new Waiter;   //waiter
    w->addCmd(cm);
    w->addCmd(cm1);
    w->sendCmd();
    w->delCmd(cm1);
    cout << endl;
    w->sendCmd();

    return 0;
}

Behavioral - state mode

1, Overview

1. State mode allows an object to change its behavior when its state changes.

2. Role: abstract state class, concrete state class and state maintenance class

3. Advantages:
① Clear structure and improved maintainability
② Reflect the principle of opening and closing and the principle of single responsibility

4. Disadvantages: if there are many states, you need to create a new state class

2, Code demonstration

#include <iostream>
#include <string>
using namespace std;

class state;
class primarystate;
class Highstate;

class Account //account number
{
public:
    Account(string name);
    string getname() const { return name; }

    void setstate(state *s)
    {
        this->st = s;
    }

    void loadfile(int point);

private:
    string name; //user name
    state *st;   //Status: forum level and forum points
};

class state //Abstract class of state
{
public:
    virtual void showstate(int point) = 0;
    virtual void setstate(int point) = 0;
    virtual void downfile(int point)
    {
        cout << a->getname() << "Download File,deduction" << point << endl;
        this->m_point -= point;
        setstate(point);
        showstate(point);
    }

    void setpoint(int point)
    {
        this->m_point = point;
    }

    int getpoint()
    {
        return m_point;
    }

    Account *getacc()
    {
        return a;
    }

    int getlevel()
    {
        return level;
    }

protected:
    Account *a;  //Whose state is it
    int level;   //Grade
    int m_point; //integral
};

class primarystate : public state //Novice account
{
public:
    primarystate(state *s)
    {
        this->a = s->getacc();

        this->m_point = s->getpoint();
        this->level = 1;
    }

    void showstate(int point)
    {
        cout << "Current points deducted" << point << "branch" << endl;
        this->m_point -= point;
    }

    void setstate(int point);

    primarystate(Account *a)
    {
        this->a = a;
        this->m_point = 0;
        this->level = 1;
    }
};

class Highstate : public state //Master account
{
public:
    Highstate(state *s)
    {
        this->a = s->getacc();

        this->m_point = s->getpoint();
        this->level = 2;
    }

    void showstate(int point)
    {
        cout << "Current points deducted" << point * 0.8 << "branch" << endl;
        this->m_point -= point * 0, 8;
    }

    void setstate(int point);
  
};

int main()
{
    Account a("Zhang San");
    a.loadfile(100);
    a.setState();
    return 0;
}

Behavioral - policy pattern (sorting algorithm)

1, Overview

1. The policy pattern abstracts a parent class (algorithm interface), which implements and encapsulates the algorithms. These algorithms are the same kind of algorithms.

2. Structure: Abstract policy class, specific policy class, environment role.

3. Benefits: it can dynamically change behavior and modify strategy, which is more flexible.

4. Disadvantages: each strategy is a class, and there are too many classes; Customers need to know all the strategies before they can make a choice.

2, Code demonstration

//1. Strategy mode: six sorts (bubble, select, insert, fast, merge, Hill)
#include <iostream>
#include <vector>
using namespace std;

void print(vector<int> result)
{
    for(auto i : result)
    {
        cout << i << " " ;
    }
    cout << endl;
}

class Sort
{
public:
    virtual vector<int> mySort(vector<int> a) = 0;
};

class BubbleSort : public Sort  //Bubble sorting
{
public:
    vector<int> mySort(vector<int> a)
    {
        int step = 1;
        cout << "Bubble sort:" << endl;
        int len = a.size();
        for(int i = 0; i < len; i++)
        {
            for(int j = 0; j < len - i - 1; j++)
            {
                if(a[j] > a[j + 1])
                {
                    auto temp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = temp;
                    cout << "The first" << step++ << "Step:";
                    print(a);
                }
            }
        }
        return a;
    }   
};

class SelectSort : public Sort  //Select sort
{
public:
     vector<int> mySort(vector<int> a)
     {
         int step = 1;
         cout << "Select sort:" << endl;
         int len = a.size();
         int i;
         for(i = 0; i < len; i++)
         {
             int temp = a[i];
             int min = i;
             for(int j = i +1; j < len; j++)
             {
                 if(a[j] < temp)
                 {
                     temp = a[j];
                     min = j;
                     cout << "The first" << step++ << "Step:";
                     print(a);
                 }
             }
             temp = a[i];
             a[i] = a[min];
             a[min] = temp;
         }
        return a;
     }        
};

class InsertSort : public Sort  //Insert sort
{
public:
    vector<int> mySort(vector<int> a)
    {
        int step = 1;
        cout << "Insert sort:" << endl;
        int len = a.size();
        for (int i = 0; i < len; i++)
        {
            int j;
            int temp = a[i];
            for(j = i; j > 0; j--)
            {
                if(a[j-1] > temp)
                {
                    a[j] = a[j -1];
                    a[j - 1] = temp;
                    cout << "The first" << step++ << "Step:";
                    print(a);
                }
                else
                {
                    break;
                }
            }
        }
        return a;
    }
};

class Quicksort : public Sort 
{
    int step = 0;
    vector<int> mySort(vector<int> a)
    {
        if(step == 0){cout << "Quick sort:" << endl;step++;}
    
        if(a.size()<=2)
        {
            if(a[1]<a[0]&&a.size()==2)
            {
                int temp=a[1];
                a[1]=a[0];
                a[0]=temp;
                cout << "The first" << step++ << "Step:";
                print(a);
            }
            return a;
        }
        int i=1;
        int j=a.size()-1;   
        while(i!=j)
        {
            while(a[j]>=a[0]&&j>i)
            {
                j--;
            }
            while(a[i]<=a[0]&&j>i)
            {
                i++;
            }
            if(j>i)
            {
                int temp=a[i];
                a[i]=a[j];
                a[j]=temp;
                cout << "The first" << step++ << "Step:";
                print(a);
            }
        }
        if(a[i]<a[0])
        {
            int temp=a[i];
            a[i]=a[0];
            a[0]=temp;
            cout << "The first" << step++ << "Step:";
            print(a);
        }
        if(a.size()>=3)
        {
            vector<int> temp;
            vector<int> a2(a.begin(),a.begin()+i);
            vector<int> a3;
            temp=mySort(a2);
            if(a.begin()+i+1!=a.end())
            {
                a3.assign(a.begin()+i+1,a.end());
                a3=mySort(a3);
            }
            temp.emplace_back(a[i]);
            temp.insert(temp.end(),a3.begin(),a3.end());
            a=temp;
            cout << "The first" << step++ << "Step:";
            print(a);
        }
        return a;
    }
};

class ShellSort:public Sort
{
    vector<int> mySort(vector<int> a)
    {
        cout << "Hill sort:"<< endl;
        int step = 1;
        int len=a.size();
        int gap=len/2;
        while(gap>=1)
        {
            for(int i=0;i+gap<=len-1;i++)
            {
                if(a[i]>a[i+gap])
                {
                    int temp=a[i];
                    a[i]=a[i+gap];
                    a[i+gap]=temp;
                    cout << "The first" << step++ << "Step:";
                    print(a);
                }
            }
            gap=gap/2;
        }
        return a;
    }
};

class MergeSort : public Sort
{
    int step = 0;
    vector<int> mySort(vector<int> a)
    {
        
        if(step == 0){cout << "Merge sort:" << endl;step++;}
        cout << "The first" << step++ << "Step (disassembly):";
        print(a);
        int len=a.size();
        if(len<=2)
        {
            if(a[0]>a[1]&&len==2)
            {
                int temp=a[0];
                a[0]=a[1];
                a[1]=temp;
                cout << "The first" << step++ << "Step (closing):";
                print(a);  
            }
           
            return a;
        }
        if(len>2)
        {
            vector<int> a1(a.begin(),a.begin()+(len+1)/2);
            vector<int> a2(a.begin()+(len+1)/2,a.end());
            a1=mySort(a1);
            a2=mySort(a2);
            print(a1);
            print(a2);
            int i=0,j=0;
            while(i<a1.size()&&j<a2.size())
            {
                if(a1[i]>=a2[j])
                {
                    a1.insert(a1.begin()+i,a2[j]);
                    a2.erase(a2.begin()+j,a2.begin()+j+1);
                    i++;   
                }
                else
                {
                    i++;
                }
            }
            a=a1;
            a.insert(a.end(),a2.begin(),a2.end());
            cout << "The first" << step++ << "Step: (closed)";
            print(a);
            return a;
        }
        return a;
    }
};

class Handler  //Policy processing class
{
public:
    vector<int> mySort(vector<int> a)
    {
        return s->mySort(a);
    }

    void setSort(Sort *s)
    {
        this->s = s;
    }
private:
    Sort *s;
};

int main()
{
    vector<int> a = {122,32,453,534,57,57,24,97,979,23,2,77,90};
    cout << "Incoming sequence:" << endl;
    print(a);
    Handler h;

    h.setSort(new BubbleSort());
    h.mySort(a);

    h.setSort(new SelectSort());
    h.mySort(a);
    
    h.setSort(new InsertSort());
    h.mySort(a);

    h.setSort(new ShellSort());
    h.mySort(a);

    h.setSort(new MergeSort());
    h.mySort(a);

    h.setSort(new Quicksort());
    h.mySort(a);

    return 0;
}

Topics: C++ Design Pattern Algorithm