C++ design patterns (23 types) summary and code implementation

Table of contents

Seven principles of design patterns:

Opening and closing principle:

Single responsibility principle:

Richter substitution principle:

Dependency inversion principle:

Interface isolation principle:

Dimit Principle (Least Known Principle):

Synthetic reuse principles:

Three major modes and their characteristics:

Creational mode:

Structural pattern:

Behavioral pattern:

--------------------I am a dividing line, next is the creative mode------------------ -----

Interpreter mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Builder mode:

shortcoming:

advantage:

Compared with factory mode:

Applicable scene:

Implementation code:

Simple factory pattern:

advantage:

shortcoming:

Factory method:

Implementation code:

Prototype mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Singleton mode:

advantage:

shortcoming:

step:

detail:

Implementation code:

--------------------I am the dividing line, next is the structural pattern------------------ -----

Adapter mode:

advantage:

shortcoming:

Applicable scene:

Precautions:

Implementation code:

Bridge mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Combined entity mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Decorator pattern:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Appearance mode (facade mode):

advantage:

shortcoming:

Applicable scene:

Implementation code:

Flyweight mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Agent mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

--------------------------I am a dividing line, what follows is the behavior pattern-------------- ------

Chain of responsibility model:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Command mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Interpreter mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Iterator pattern:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Mediator model:

advantage:

shortcoming:

Applicable scene:

Code:

Memo mode:

advantage:

shortcoming:

Applicable scene:

Implementation code:

Observer mode:

advantage:

shortcoming:

Applicable scene:

Code:

Status mode:

advantage:

shortcoming:

Applicable scene:  

Code:

Strategy mode:

advantage:

shortcoming:

Applicable scene:

Precautions:

Code:

Template mode:

advantage:

shortcoming:

Applicable scene:

Code:

Visitor mode:

advantage:

shortcoming:

Applicable scene:

Code:

reference:


Seven principles of design patterns:

Opening and closing principle:

        Open for extension, closed for modification. When the program needs to be expanded, the original code cannot be modified, but the original code can be expanded to achieve the hot-swappable effect.

Single responsibility principle:

        There should not be more than one reason for class changes, which means that each class should implement a single responsibility. If not, the class should be split.


Richter substitution principle:

        It is one of the basic principles of object-oriented design. Anywhere a base class can appear, a subclass can certainly appear. It is the cornerstone of inheritance and reuse. Only when the derived class can replace the base class and the function of the software unit is not affected, the base class can be truly reused, and the derived class can also add new behaviors on the basis of accumulation. It is a supplement to the "open-close principle". The key step to realize the opening and closing principle is abstraction, and the inheritance relationship between base classes and subclasses is the specific implementation of abstraction, so the Liskov substitution principle is a specification for the specific steps to achieve abstraction.


Dependency inversion principle:

        This is the basis of the opening and closing principle. The specific content is: interface-oriented programming relies on abstraction rather than concreteness. When a concrete class is used when writing code, it does not interact with the concrete class, but with the upper-level interface of the concrete class.


Interface isolation principle:

        There are no methods in each interface that are not used by subclasses but must be implemented. If not, the interface must be split. Using multiple isolated interfaces is better than using a single interface.


Dimit Principle (Least Known Principle):

        The less a class knows about the classes it depends on, the better. No matter how complex the dependent class is, the logic should be encapsulated inside the method and provided to the outside through the public method. In this way, when the dependent class changes, the class can be minimally affected.


Synthetic reuse principles:

        ​ ​ ​ Try to use composition/aggregation instead of inheritance.

Three major modes and their characteristics:

Creational mode:

        Abstracts the instantiation process. They help a system become independent of how its objects are created, composed, and represented.

(Abstract factory pattern, builder pattern, factory pattern, prototype pattern, singleton pattern)

Structural pattern:

        Involves how to combine classes and objects to obtain larger structures. The creational pattern focuses on the instantiation of a class or object; the structural pattern focuses on the combination of multiple classes or objects into more complex objects in order to construct objects more flexibly.

(Adapter mode, bridge mode, combined entity mode, decorator mode, appearance mode, flyweight mode, proxy mode)

Behavioral pattern:

        It involves the distribution of responsibilities between algorithms and objects. Describes not only the patterns of objects and classes, but also the communication patterns between them. Use the inheritance mechanism to dispatch behavior between classes.

(Chain of responsibility pattern, command pattern, interpreter pattern, iterator pattern, mediator pattern, memento pattern, observer pattern, state pattern, strategy pattern, template pattern, visitor pattern)

--------------------I am a dividing line, next is the creative mode------------------ -----

Interpreter mode:

It refers to given a language (expression), defining a representation of its grammar, and defining an interpreter, using the interpreter to interpret sentences (expressions) in the language

advantage:

    1. Has good scalability. Grammar translation is implemented through classes, and extending classes can expand its interpretation capabilities.

    2. The implementation difficulty is low. Each expression node class in the syntax tree has a certain similarity, so it is relatively easy to implement.

shortcoming:

    1. Low execution efficiency. There are usually a large number of loops and recursive statements in the interpreter. When the sentences being interpreted are complex, the performance of the program is greatly affected.

    2. Class expansion problem. When there are more rules, the number of classes also expands.

Applicable scene:

    When a specific type of problem occurs frequently enough, for example, simple syntax explanation, compiler, calculation of operational expressions, regular expressions, log processing: when using scripting language or programming language to process logs,

    A large number of reports will be generated, and the logs need to be parsed and reports generated.

    The log formats of each service are different, but the elements in the data are the same. In this case, the main solution to solve the above problems through programs is to use the interpreter mode. The interpreter mode is rarely used in daily projects.

Compare with the adapter pattern: The two patterns are similar, but the adapter pattern does not require prior knowledge of the adapter's rules. The interpreter mode requires the rules to be written in advance and the interpretation to be performed according to the rules.

Implementation code:

#include<iostream>
#include<list>
#include<vector>
#include<algorithm>

class Context{
public:
    Context(int num){
        m_num=num;
    }

    void setNum(int num){
        m_num=num;
    }
    int getNum(){
        return m_num;
    }
    void setRes(int res){
        m_res=res;
    }
    int getRes(){
        return m_res;
    }

private:
    int m_num,m_res;

};

class Expression{
public:
    virtual void interpreter(Context* context) =0;
};

class PlusExpression :public Expression{
public:
    virtual void interpreter(Context* context){
        int num=context->getNum();
        num++;
        context->setNum(num);
        context->setRes(num);
    }
};

class MinusExpression :public Expression{
public:
    virtual void interpreter(Context* context){
        int num =context->getNum();
        num--;
        context->setNum(num);
        context->setRes(num);
    }
};


int
main(){

    Context* pcxt =new Context(10);
    Expression* e1=new PlusExpression();

    e1->interpreter(pcxt);
    std::cout<<"PlusExpression:"<<pcxt->getRes()<<std::endl;

    Expression* e2 =new MinusExpression();
    e2->interpreter(pcxt);
    std::cout<<"MinusExpression:"<<pcxt->getRes()<<std::endl;
    delete e1,e2,pcxt;

    return 0;
}

Builder mode:

Separating the construction process of a complex object from its representation allows the same construction process to create different representations.

 Users only need to specify the type to be built to get the product instance corresponding to that type, and do not care about the details of the construction process.

That is, how to gradually build an object containing multiple components. The same building process can create different products.

shortcoming:

        ​ ​ 1. Increased the number of classes: generating redundant Builder objects

        ​ ​ 2. Difficulty in internal modification: If the product changes internally, the builder must also modify it accordingly

advantage:

        ​ ​ 1. Good encapsulation: separation of creation and use

        ​ ​ 2. Good scalability: the construction classes are independent of each other and decoupled to a certain extent

Compared with factory mode:

    The focus is different: the builder pattern focuses more on the method calling process; the factory pattern focuses on creating products and does not care about the order in which methods are used.

    ​ Different strengths in creating objects: Different strengths in creating objects. The builder pattern can create complex products, which are composed of various complex components. The factory pattern creates the same instance objects.

Applicable scene:

        Complex structure: The object has a very complex internal structure with many attributes. Separate creation and use: I want to separate the creation and use of complex objects.

        The builder pattern is suitable when creating an object requires many steps. When creating an object only requires a simple method, the factory pattern is suitable.

Implementation code:

#include<iostream>
#include<memory.h>
#include<string>

class PersonBuilder {
public:
    virtual void buildHead() {}
    virtual void buildBody() {}
    virtual void buildArm() {}
    virtual void buildLeg() {}
    PersonBuilder(){}
    PersonBuilder(std::string g, std::string p) {
        this->g = g;
        this->p = p;
    }
    virtual ~PersonBuilder() {};

    std::string g, p;
};

class PersonThinBuilder : public PersonBuilder {
public:
    PersonThinBuilder(std::string g, std::string p){
        this->g = g;
        this->p = p;
    }
    void buildHead() {
        std::cout << "瘦子 画头" << std::endl;
    }
    void buildBody() {
        std::cout << "瘦子 画身体" << std::endl;
    }
    void buildArm() {
        std::cout << "瘦子 画胳膊" << std::endl;
    }
    void buildLeg() {
        std::cout << "瘦子 画大腿" << std::endl;
    }

};

class PersonFatBuilder : public PersonBuilder {
public:
    PersonFatBuilder() {}
    PersonFatBuilder(std::string g, std::string p) {
        this->g = g;
        this->p = p;
    }
    
    void buildHead() {
        std::cout << "胖子 画头" << std::endl;
    }
    void buildBody() {
        std::cout << "胖子 画身体" << std::endl;
    }
    void buildArm() {
        std::cout << "胖子 画胳膊" << std::endl;
    }
    void buildLeg() {
        std::cout << "胖子 画大腿" << std::endl;
    }
};

class PersonDirector {
public:
    PersonDirector(PersonBuilder* pb) {
        this->pb = pb;
    }
    void createPerson() {
        pb->buildHead();
        pb->buildBody();
        pb->buildArm();
        pb->buildLeg();
    }
private:
    PersonBuilder* pb;
};


int
main() {

    PersonThinBuilder* pt = new PersonThinBuilder("画瘦子", "红笔");
    PersonDirector* pd = new PersonDirector(pt);
    pd->createPerson();

    return 0;
}

Simple factory pattern:

        Any product can be produced through parameter control

advantage:

        Simple and crude, intuitive and easy to understand. Use a factory to produce any product under the same hierarchical structure

shortcoming:

        ​ ​ 1. Everything is produced together. Too many products will lead to a huge amount of code.

        ​ ​ 2. The opening and closing principle (open for expansion, closed for modification) is not very good. If you want to add new products, you must modify the factory method.

Factory method:

        Define an interface for creating objects, but let the subclass decide what kind of object to create, and use multiple factories to produce specified fixed products.

Implementation code:

#include <iostream>
#include <string>
#include <memory>
class Fruit {
 public:
    Fruit(){}
    virtual void show() = 0;
};
class Apple : public Fruit {
 public:
    Apple() {}
    virtual void show() {
    std::cout << "我是⼀个苹果" << std::endl;
 }
};
class Banana : public Fruit {
 public:
    Banana() {}
    virtual void show() {
        std::cout << "我是⼀个⾹蕉" << std::endl;
    }
};
class FruitFactory {
 public:
    static std::shared_ptr<Fruit> create(const std::string &name) {
    if (name == "苹果") {
        return std::make_shared<Apple>();
    }else if(name == "⾹蕉") {
        return std::make_shared<Banana>();
    }
    return std::shared_ptr<Fruit>();
 }
};
int main()
{
    std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");
    fruit->show();
    fruit = FruitFactory::create("⾹蕉");
    fruit->show();
    return 0;
}

Prototype mode:

        The prototype mode also provides the function of self-replication, which means that new objects can be created through existing objects.

advantage:

    1. Objects can be cloned without being coupled to the concrete classes to which they belong.

    2. You can clone the pre-generated prototype to avoid repeatedly running the initialization code

    3. It is easier to generate complex objects

    4. Different configurations of complex objects can be handled in ways other than inheritance.

shortcoming:

    1. You need to configure a clone method for each class

    2. The clone method is located inside the class. When modifying an existing class, the code needs to be modified, which violates the opening and closing principle.

    3. When performing deep copying, more complex code needs to be written, and when there are multiple nestings between objects, in order to achieve deep cloning, the classes corresponding to each layer of objects must support deep cloning, which is complex to implement.

Applicable scene:

    1. When objects are the same or similar, and only differ in a few individual attributes

    2. The cost of creating objects is high, such as long initialization time, too much CPU usage, or too many network resources. Resources need to be optimized.

    3. Creating an object requires frequent data preparation or access permissions, etc., which requires improved performance or improved security.

    4. This type of object is widely used in the system, and each caller needs to reassign its attributes.

Implementation code:

#include<iostream>
#include<memory.h>
#include<string>

class Prototype{
public:
    virtual ~Prototype(){}
    virtual Prototype* Clone()const=0;
protected:
    Prototype(){}
};

class ConcretePrototype:public Prototype{
public:
    ConcretePrototype(){}
    ConcretePrototype(const ConcretePrototype& cp){
        std::cout<<"ConcretePrototype copy ..."<<std::endl;
    }
    ~ConcretePrototype();
    Prototype* Clone()const{
        return new ConcretePrototype(*this);
    }
};

int 
main(){

    Prototype* p=new ConcretePrototype();
    Prototype* p1=p->Clone();

    return 0;
}

Singleton mode:

        A class only creates a unique object, that is, it can be created once and used multiple times.

advantage:

    1. Provides controlled access to a unique instance

    2. Since there is only one object in the system, system resources can be saved. For some objects that need to be frequently created and destroyed, the singleton mode can undoubtedly improve the performance of the system.

    3.Allow variable number of instances

shortcoming:

    1. Since there is no abstraction layer in the singleton pattern, it is very difficult to extend the singleton class.

    2. The accusation of singleton class is too heavy, which violates the "single responsibility principle" to a certain extent.

    3. Abusing singletons will bring about some negative problems. For example, in order to save resources, the database connection pool object is designed as a singleton class, which may lead to too many programs sharing the connection pool object.

    And the connection pool overflow occurs; if the instantiated object is not used for a long time, the system will consider it as garbage and be recycled, which will lead to the loss of object state.

step:

    1. Constructor privatization

    2. Add a static private pointer variable of the current class

    3. Provide a static external interface, allowing users to obtain singleton mode

detail:

    1. Hungry mode: instantiate itself when the class is loaded. Its advantage is that it does not need to consider multi-thread access issues and can ensure the uniqueness of the instance.

        Since it is created from the beginning, it is better than the lazy style in terms of calling speed and response time. But whether the system needs to use the instance object at runtime

        The object will be created when the class is loaded. In terms of resource utilization efficiency, the hungry style is not as good as the lazy style. Moreover, since a singleton object needs to be created when the system is loaded, the loading time is relatively long.

    2. Lazy mode: Created when used for the first time, there is no need to occupy system resources all the time, and lazy loading is achieved, but the problem of simultaneous access by multiple threads must be dealt with, especially when

        As a resource controller, the singleton class must involve resource initialization during instantiation, and resource initialization is likely to take a lot of time, which means that multiple threads may occur for the first time at the same time.

        The probability of referencing classes like this becomes greater and needs to be controlled through a double check mechanism, which may affect system performance.

Implementation code:

#include<iostream>
#include<memory.h>
#include<string>
//饿汉式
class SingletonHungry{
public:
    static SingletonHungry* getInstance(){
        return pSingleton;
    }
private:
    SingletonHungry(){}
    static SingletonHungry* pSingleton;
};
SingletonHungry* SingletonHungry::pSingleton=new SingletonHungry;//在加载的时候就创建

class SingletonLazy{
public:
    static SingletonLazy* getInstance(){
        if(pSingleton==nullptr){
            pSingleton=new SingletonLazy;
        }
        return pSingleton;
    }
private:
    SingletonLazy(){}
    static SingletonLazy* pSingleton;
};
SingletonLazy* SingletonLazy::pSingleton=nullptr;



int 
main(){

    SingletonHungry* test1=SingletonHungry::getInstance();
    SingletonHungry* test2=SingletonHungry::getInstance();
    if(test1==test2) std::cout<<"饿汉单例模式"<<std::endl;
    else std::cout<<"Not 饿汉单例模式"<<std::endl;

    SingletonLazy* test3=SingletonLazy::getInstance();
    SingletonLazy* test4=SingletonLazy::getInstance();
    if(test3==test4) std::cout<<"懒汉单例模式"<<std::endl;
    else std::cout<<"Not 懒汉单例模式"<<std::endl;



    return 0;
}

--------------------I am the dividing line, next is the structural pattern------------------ -----

Adapter mode:

        It combines the functions of two independent interfaces, converts the interface of one class into another interface expected by the customer, so that originally incompatible classes or functions can operate together, and is a bridge between two incompatible interfaces.

advantage:

    1. Can effectively solve interface compatibility issues

    2. Strong flexibility

shortcoming:

    1. Excessive use of adapters will make the system very messy and difficult to grasp as a whole.

        ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

        Therefore, if it is not necessary, you can reconstruct the system directly without using the adapter.

Applicable scene:

    1. During the software iteration process, interface incompatibility occurs.

    2. Want to create a class that can be reused to work with some classes that are not much related to each other, including some classes that may be introduced in the future. These source classes do not necessarily have consistent interfaces.

    3. Insert a class into another class through interface conversion.

    (For example, between tigers and birds, now there is a flying tiger. Without the need to add entities, add an adapter to contain a tiger object and implement the flying interface.)

Precautions:

        ​ ​ ​ Adapters are not added during program design, but to solve problems in projects currently in service.

Implementation code:

#include<iostream>
#include<memory.h>
#include<string>

class Target{
public:
    Target(){}
    virtual ~Target(){}
    virtual void Request(){
        std::cout<<"Target::Request"<<std::endl;
    }
};

class Adaptee{
public:
    Adaptee(){}
    ~Adaptee(){}
    void SpecificRequest(){
        std::cout<<"Adaptee::SpecificRequest"<<std::endl;
    }
};

class Adapter:public Target{
public:
    Adapter(Adaptee* ade){
        _ade=ade;
    }
    ~Adapter(){}
    void Request(){
        _ade->SpecificRequest();
    }
private:
    Adaptee* _ade;
};



int
main(){

    Adaptee* ade=new Adaptee;
    Target* adt =new Adapter(ade);
    adt->Request();
    return 0;
}

Bridge mode:

        ​ ​ Separate abstraction and implementation so that the two can change independently. The bridge mode converts inheritance relationships into combination relationships. Achieved separation of abstraction and implementation

advantage:

    1. Extract reality and implement abstraction so that the specific implementation of the object depends on the abstraction, which satisfies the dependency inversion principle

    2. Extract the changes that can be shared to reduce duplicate information in the code

    3. The specific implementation of the object can be more flexible and can meet the requirements of multiple factors

    4. Improved the scalability of the system. If a certain dimension needs to be expanded, it only needs to add an implementation class interface or a specific implementation class, and it will not affect the other dimension, complying with the opening and closing principle.

shortcoming:

    1. It will increase the difficulty of understanding and designing the system, because the relationship is established at the abstract layer, and it is necessary to design and program at the abstract layer from the beginning.

    2. It is required to correctly identify two or more independently changing dimensions in the system. How to accurately identify the two dimensions in the system is the difficulty in applying the bridge mode.

Applicable scene:

    1. The system needs to add more flexibility between abstraction and concretization to avoid establishing a static inheritance relationship between the two levels. Through the bridge mode, they can establish an association relationship in the abstraction layer.

    2. A class has two or more independently changing dimensions, and these two or more dimensions need to be expanded independently

    3. For those systems that do not want to use inheritance or where the number of system classes increases sharply due to multi-level inheritance.

//Take Windows drawing software as an example. Pen is a base class that exposes the drow interface to the outside world, and the lines drawn are thick, medium, and thin. Colors are divided into red, yellow and blue. If implemented through inheritance. Then 3*3 = 9 subclasses are needed for permutation and combination. Instead use bridge mode. Only 6 are needed. Obviously, the more thicknesses and colors, the greater the advantages.

Implementation code:

#include<iostream>
#include<string>
class AbstractionImp {
public:
    virtual ~AbstractionImp() {}
    virtual void Operation() = 0;
protected:
    AbstractionImp() {}
};

class Abstraction {
public:
    virtual ~Abstraction() {}
    virtual void Operation() = 0;
protected:
    Abstraction() {}
};

class RefinedAbstaction :public Abstraction {
public:
    RefinedAbstaction(AbstractionImp* imp) {
        _imp = imp;
    }
    ~RefinedAbstaction() {}
    void Operation() {
        _imp->Operation();
    }
private:
    AbstractionImp* _imp;
};

class ConcreteAbstractionImpA : public AbstractionImp {
public:
    ConcreteAbstractionImpA() {}
    ~ConcreteAbstractionImpA() {}
    virtual void Operation() {
        std::cout << "ConcreteAbstractionImpA...." << std::endl;
    }

};

class ConcreteAbstractionImpB : public AbstractionImp {
public:
    ConcreteAbstractionImpB() {}
    ~ConcreteAbstractionImpB() {}
    virtual void Operation() {
        std::cout << "ConcreteAbstractionImpB...." << std::endl;
    }
};

int
main() {

    AbstractionImp* imp = new ConcreteAbstractionImpA();
    Abstraction* abs = new RefinedAbstaction(imp);
    abs->Operation();

    return 0;
}

Combined entity mode:

        It is a structural design pattern that can be used to combine objects into a tree structure and use them like independent objects.

advantage:

    1. Allows the client to process single objects and combined objects in a unified manner

    2. Allows you to add new types of components more easily

shortcoming:

    1. May make your design too generic. Sometimes only leaf components need to define certain operations, but due to commonality, you have to define these operations in all components

Applicable scene:

    For example: In graphical user interfaces, many graphical user interfaces use the combination mode to organize and manage components such as windows and panels. File systems, which typically use composition patterns to represent file and folder hierarchies,

        XML/HTML documents can also use combination mode to represent their tree structure

Implementation code:

#include<iostream>
#include<algorithm>
#include<memory.h>
#include<string>
#include<list>

class Component{
public:
    Component(){}
    virtual ~Component(){}
    virtual void Operation()=0;
    virtual void Add(Component* pChild){};
    virtual void Remove(Component* pChild){};
    virtual Component* GetChild(int nIndex){ 
        return nullptr;
    };

};

class Leaf: public Component{
public:
    Leaf(){}
    virtual ~Leaf(){}
    virtual void Operation(){
        std::cout<<"Operation by leaf\n";
    }
};

class Composite:public Component{
public:
    Composite(){}
    virtual ~Composite(){
        std::list<Component*>::iterator iter1,iter2,temp;
        for(iter1 =m_ListOfComponent.begin(),iter2=m_ListOfComponent.end();iter1!=iter2;){
            temp=iter1;
            ++iter1;
            delete *temp;
        }
    }
    virtual void Operation(){
        std::cout<<"Operation by Composite\n";
        std::list<Component*>::iterator iter1,iter2;
        for( iter1=m_ListOfComponent.begin(), iter2=m_ListOfComponent.end(); iter1!=iter2; ++iter1){
            (*iter1)->Operation();
        }
    }
    virtual void Add(Component* pChild){
        m_ListOfComponent.push_back(pChild);
    }
    virtual void Remove(Component* pChild){
        std::list<Component*>::iterator iter;
        iter=find(m_ListOfComponent.begin(),m_ListOfComponent.end(),pChild);
        if(m_ListOfComponent.end()!=iter){
            m_ListOfComponent.erase(iter);
        }
    }
    virtual Component* GetChild(int nIndex){
        if(nIndex <=0 ||nIndex > m_ListOfComponent.size()) return nullptr;
        std::list<Component*>::iterator iter1,iter2;
        int i;
        for(i=1,iter1=m_ListOfComponent.begin(),iter2=m_ListOfComponent.end() ; iter1!=iter2 ; ++iter1, ++i){
            if(i == nIndex) break;
        }
        return *iter1;
    }
private:
    std::list<Component*> m_ListOfComponent;
};


int 
main(){

    Leaf* pLeaf1=new Leaf();
    Leaf* pLeaf2=new Leaf();
    Composite* pComposite=new Composite;
    pComposite->Add(pLeaf1);
    pComposite->Add(pLeaf2);
    pComposite->Operation();
    pComposite->GetChild(2)->Operation();
    
    return 0;
}

Decorator pattern:

        Without changing the original class file or using inheritance, dynamically expand an object to enhance or add objects.

advantage:

    1. Good flexibility. Compared with inheritance, the decorator mode is more flexible in expanding object functions.

    2. Good scalability, different decoration combinations can create a variety of objects, and avoid class explosion

    3. Meet the opening and closing principles and reuse principles that meet the requirements of the design pattern

    4. Good transparency. The client targets abstract operations and is invisible to the specific implementation content.

shortcoming:

    1. High complexity. The design of decorators is often highly complex and requires high levels of developer skills.

Applicable scene:

    For example, graphical user interface: Many graphical user interfaces use the decorator pattern to dynamically add new behaviors and styles. Data compression, logging, etc.

Implementation code:

#include<iostream>
#include<memory>
#include<string>

class AbstractHero{
public:
    virtual void showStatus()=0;
    int mHp,mMp,mAt,mDf;
};

class HeroA:public AbstractHero{
public:
    HeroA(){
        mHp=0,mMp=0,mAt=0,mDf=0;
    }
    virtual void showStatus(){
        std::cout<<"血量:"<<mHp<<std::endl;
        std::cout<<"魔法:"<<mMp<<std::endl;
        std::cout<<"攻击:"<<mAt<<std::endl;
        std::cout<<"防御:"<<mDf<<std::endl;
    }
};

//英雄穿上某个装饰物,那么也还是一个英雄
class AbstractEquipment: public AbstractHero{
public:
    AbstractEquipment(AbstractHero* hero){
        pHero=hero;
    }
    virtual void showStatus(){}
    AbstractHero* pHero;
};

//给英雄穿上狂徒铠甲
class KuangtuEquipment:public AbstractEquipment{
public:
    KuangtuEquipment(AbstractHero* hero):AbstractEquipment(hero){}
    //增加新的功能
    void AddKuangtu(){
        std::cout<<"新英雄穿上狂徒之后"<<std::endl;
        this->mHp=this->pHero->mHp;
        this->mMp=this->pHero->mMp;
        this->mAt=this->pHero->mAt;
        this->mDf=this->pHero->mDf+30;
    }
    virtual void showStatus(){
        AddKuangtu();
        std::cout<<"血量:"<<mHp<<std::endl;
        std::cout<<"魔法:"<<mMp<<std::endl;
        std::cout<<"攻击:"<<mAt<<std::endl;
        std::cout<<"防御:"<<mDf<<std::endl;
    }
};


int main(){

    AbstractHero* hero=new HeroA;
    hero->showStatus();
    std::cout<<"------------------------"<<std::endl;
    //给英雄穿上狂徒
    hero=new KuangtuEquipment(hero);
    hero->showStatus();

    return 0;
}

Appearance mode (facade mode):

        refers to the communication between the outside and the subsystem must be carried out through a unified appearance object.

        Provide a consistent interface for a set of interfaces in the subsystem and define a high-level interface that makes the subsystem easier to use

advantage:

    1. Simplify the interaction between the client and the subsystem, making it easier for the client to use the subsystem

    2. It can reduce the coupling between the client and the subsystem, so that the client does not need to interact directly with the subsystem

    3. The implementation of the subsystem can be changed without affecting the client

shortcoming:

    1. The client's access to the subsystem may be restricted because the client can only access the subsystem through the appearance class.

    2. If the design is not careful, it may become a huge and complex class that is difficult to maintain.

Applicable scene:

    1. In software development, the appearance pattern is often used to encapsulate the underlying library or framework and provide a simple interface for the upper layer. This can reduce the coupling between upper-layer applications and underlying libraries, making upper-layer applications easier to develop and maintain.

    2. In operating systems, appearance mode is also often widely used. For example, the operating system provides a simple interface for applications so that applications can easily access hardware resources without directly interacting with hardware drivers.

    3. In Web development, Web frameworks usually provide developers with a simple interface, allowing developers to easily develop Web applications without caring about the underlying http protocol and server configuration.

Implementation code:

#include<iostream>
#include<string>
#include<memory>

class Television{
public:
    void on(){
        std::cout<<"电视机打开"<<std::endl;
    }
    void off(){
        std::cout<<"电视机关闭"<<std::endl;
    }
};

class Light{
public:
    void on(){
        std::cout<<"灯打开"<<std::endl;
    }
    void off(){
        std::cout<<"灯关闭"<<std::endl;
    }
};

class DVDplayer{
public:
    void on(){
        std::cout<<"DVD打开"<<std::endl;
    }
    void off(){
        std::cout<<"DVD关闭"<<std::endl;
    }
};

//外观模式
class KTVMode{
public:
    KTVMode(){
        pTV=new Television;
        pLight=new Light;
        pDvD=new DVDplayer;
    }
    void onKTV(){
        pTV->on();
        pLight->on();
        pDvD->on();
    }
    ~KTVMode(){
        pTV->off();
        pLight->off();
        pDvD->off();
        delete pTV,pLight,pDvD;
    }
private:
    Television* pTV;
    Light* pLight;
    DVDplayer* pDvD;
};


int
main(){

    KTVMode* ktv1=new KTVMode;
    ktv1->onKTV();
    delete ktv1;

    return 0;
}

Flyweight mode:

        It means lightweight, and Chinese translates into Xiangyuan. Enable identical or similar reuse through shared technology. Use sharing technology to effectively support the reuse of a large number of fine-grained objects

advantage:

    1. It can reduce memory usage and improve program performance. It reduces the number of objects by sharing them, thus reducing the memory footprint

    2. It can improve the execution speed of the program. Since there are fewer objects, there are fewer objects that the program needs to process at runtime, so the execution speed of the program will be faster.

shortcoming:

    1. Flyweight mode will increase the complexity of the program. You need to separate the object's state into internal state and external state, and consider how to share internal state when implementing flyweight objects.

    2. It may increase the maintenance cost of the program. Since the flyweight mode separates the creation and use of objects, you need to manage the external state in the client code and pass the external state to it when using the flyweight mode object. This may increase the maintenance cost of the program

Applicable scene:

    1. An application uses a large number of identical or similar objects, causing a large storage overhead.

    2. Most of the state of the object can be externalized, and these external states can be passed into the object

    3. If you delete the external state of an object, you can replace many groups of objects with relatively few shared objects.

    4. The application does not rely on object identification. Since flyweight objects can be shared, identity tests will return true for conceptually obvious other objects.

    5. Using the flyweight mode requires maintaining a flyweight pool to store flyweight objects, which consumes resources. Therefore, it is worthwhile to use the flyweight mode only when the flyweight object is reused multiple times.

Implementation code:

#include<iostream>
#include<list>
#include<algorithm>
class ConcreateFlyweight;
class Flyweight{
public:
    virtual ~Flyweight(){}
    std::string GetIntrinsicState(){
        return m_State;
    }
    virtual void Operation(std::string& ExtrinsicState)=0;
protected:
    Flyweight(const std::string& state):m_State(state){}
private:
    std::string m_State;
};

class ConcreateFlyweight:public Flyweight{
public:
    ConcreateFlyweight(const std::string& state):Flyweight(state){}
    virtual ~ConcreateFlyweight(){}
    virtual void Operation(std::string& ExtrinsicState){}
};

class FlyweightFactory{
public:
    FlyweightFactory(){}
    ~FlyweightFactory(){
        for(std::list<Flyweight*>::iterator iter1=m_ListFlyweight.begin(), temp; iter1!=m_ListFlyweight.end(); ){
            temp=iter1;
            ++iter1;
            delete *temp;
        }
        m_ListFlyweight.clear();
    }
    Flyweight* GetFlyweight(const std::string& key){
        for(std::list<Flyweight*>::iterator iter1=m_ListFlyweight.begin(); iter1!=m_ListFlyweight.end(); ++iter1){
            if((*iter1)->GetIntrinsicState() == key){
                std::cout<<"The Flyweight:"<< key<<" already exits " <<std::endl;
                return (*iter1);
            }
        }
        std::cout<<"Creating a new Flyweight:"<<key <<std::endl;
        Flyweight* flyweight =new ConcreateFlyweight(key);
        m_ListFlyweight.push_back(flyweight);
    }
private:
    std::list<Flyweight*> m_ListFlyweight;
};


int
main(){

    FlyweightFactory flyweightfactory;
    flyweightfactory.GetFlyweight("hello");
    flyweightfactory.GetFlyweight("world");
    flyweightfactory.GetFlyweight("hello");

    return 0;
}

Agent mode:

        Provide a proxy to control access to the original object without changing the software

advantage:

    1. The accusation is clear, the real target focuses on its own business logic, and does not need to consider other non-responsibility content, leaving it to the agent to complete

    2. High scalability, changes to the real object do not affect the agent

    3. Decoupling, separating the client from the real object, reducing system coupling

    4. Improve performance, virtual agents can reduce the consumption of system resources

    5. High security and stability, the agent can better control access and improve system security

shortcoming:

    1. Increase system complexity. Agents have many responsibilities

    2. The request speed is low. Adding proxies to the client and real objects will reduce the operating efficiency of the entire system process to a certain extent.

Applicable scene:

    When you need to control access to the original object, provide additional functions, reduce client code complexity, etc., you can use the proxy mode

Implementation code:

#include<iostream>
#include<string>
#include<memory>

class AbstractCommonInterface{
public:
    virtual void run() =0;
};

class MySystem:public AbstractCommonInterface{
public:
    virtual void run(){
        std::cout<<"系统启动"<<std::endl;
    }
};

class MySystemProxy:public AbstractCommonInterface{
public:
    MySystemProxy(std::string username,std::string password){
        mUserName=username;
        mPassWord=password;
        pMySystem=new MySystem;
    }

    bool Check(){
        if(mUserName=="admin" && mPassWord=="admin"){
            return true;
        }
        return false;
    }

    virtual void run(){
        if(Check()==true){
            std::cout<<"启动成功"<<std::endl;
            pMySystem->run();
        }else{
            std::cout<<"用户名或密码错误\n";
        }
    }
    ~MySystemProxy(){
        if(pMySystem !=nullptr){
            delete pMySystem;
        }
    }

private:
    std::string mUserName,mPassWord;
    MySystem* pMySystem;
};


int
main(){

    MySystemProxy* proxy =new MySystemProxy("admin","admin");
    proxy->run();
    return 0;
}

--------------------------I am a dividing line, what follows is the behavior pattern-------------- ------

Chain of responsibility model:

        ​​​Creates a chain of receiver objects for the request. This pattern gives the type of request and decouples the sender and receiver of the request.

advantage:

    1. Reduce the degree of coupling, which decouples the sender and receiver of the request

    2. Enhanced the flexibility of assigning responsibilities to objects: by changing the members within the chain or moving their order, it allows dynamic addition or deletion of responsibilities.

    3. It is very convenient to add new request processing classes

shortcoming:

    1. There is no guarantee that the request will be processed because the request does not have a clear recipient.

    2. System performance will be affected to a certain extent, and it will be inconvenient when debugging code; it may cause loop calls

    3. It may be difficult to observe runtime characteristics, which may lead to errors

Applicable scene:

    For example, in the exception handling mechanism in Java, when a method throws an exception, if the method does not handle the exception internally, the exception will be passed to the upper method that calls the method for processing until it is handled or thrown to the program. outermost layer

    Event bubbling mechanism in JavaScript

    Filter chain in Servlet development

Implementation code:

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>

class PurchaseRequest{
public:
    int getType()const{
        return type;
    }
    float getPrice()const{
        return price;
    }
    int getId()const{
        return id;
    }
    PurchaseRequest(const int type,const float price,const int id):type(type),price(price),id(id){

    }
private:
    int type,id;
    float price;
};

class Approver{
public:
    void setApprover(Approver* const approver){
        this->approver=approver;
    }
    explicit Approver(const std::string& name):name(name){}
    virtual void processRequest(PurchaseRequest* purchaseRequest)=0;
protected:
    Approver* approver;
    std::string name;
};

class DepartmentApprover:public Approver{
public:
    explicit DepartmentApprover(const std::string& name):Approver(name){}
    void processRequest(PurchaseRequest* purchaseRequest)override{
        if(purchaseRequest->getPrice()<=5000){
            std::cout<<"请求编号id= "<<purchaseRequest->getId()<<"被"<<this->name<<"处理"<<std::endl;
        }else{
            approver->processRequest(purchaseRequest);
        }
    }
};

class CollegeApprover:public Approver{
public:
    explicit CollegeApprover(const std::string& name):Approver(name){}
    void processRequest(PurchaseRequest* purchaseRequest)override{
        if(purchaseRequest->getPrice()>5000 && purchaseRequest->getPrice()<=10000){
            std::cout<<"请求编号id= "<<purchaseRequest->getId()<<"被"<<this->name<<"处理"<<std::endl;
        }else{
            approver->processRequest(purchaseRequest);
        }
    }
};

class ViceSchoolMasterApprover:public Approver{
public:
    explicit ViceSchoolMasterApprover(const std::string& name):Approver(name){}
    void processRequest(PurchaseRequest* purchaseRequest)override{
        if(purchaseRequest->getPrice()>10000 && purchaseRequest->getPrice()<=30000){
            std::cout<<"请求编号id= "<<purchaseRequest->getId()<<"被"<<this->name<<"处理"<<std::endl;
        }else{
            approver->processRequest(purchaseRequest);
        }
    }
};


int
main(){

    PurchaseRequest* purchaseRequest =new PurchaseRequest(1,10005,1);
    DepartmentApprover* department =new DepartmentApprover("庄主任");
    CollegeApprover* college=new CollegeApprover("李院长");
    ViceSchoolMasterApprover* viceSchoolMaster =new ViceSchoolMasterApprover("王副校长");

    department->setApprover(college);
    college->setApprover(viceSchoolMaster);

    department->processRequest(purchaseRequest);//部长审批不了转交给学院,学院审批不来转交给副校长,副校长能审批批下来了

    return 0;
}

Command mode:

        Encapsulate a request as an object to separate the responsibility for making the request and the responsibility for executing the request.

advantage:

    1. Good encapsulation, each command is encapsulated. For the client, if it needs any function, it can call the corresponding command without knowing how the command is executed.

shortcoming:

    1. It may cause some systems to have too many specific command classes. Because a specific command class needs to be designed for each command, some systems may require a large number of specific command classes, which will affect the use of command mode

Applicable scene:

    The system needs to decouple the requester and the request receiver so that the caller and receiver do not interact directly

    The system needs to specify requests at different times, queue requests and execute requests

    The system needs to support the undo and restore operations of commands

    (Command mode can be used wherever commands are considered)

Implementation code:

#include<iostream>
#include<vector>
#include<List>
#include<memory>
#include<queue>
#include<windows.h>

class HandleClinetProtocal{
public:
    void AddMoney(){
        std::cout<<"给玩家增加金币"<<std::endl;
    }
    void AddDiamond(){
        std::cout<<"给玩家增加钻石"<<std::endl;
    }
};

class AbstractCommand{
public:
    virtual void handle()=0;
};

class AddMoneyCommand:public AbstractCommand{
public:
    AddMoneyCommand(HandleClinetProtocal* protocal){
        this->pProtocol=protocal;
    }
    virtual void handle(){
        this->pProtocol->AddMoney();
    }
    HandleClinetProtocal* pProtocol;
};

class AddDiamondCommand:public AbstractCommand{
public:
    AddDiamondCommand(HandleClinetProtocal* protocal){
        this->pProtocol=protocal;
    }
    virtual void handle(){
        this->pProtocol->AddMoney();
    }
    HandleClinetProtocal* pProtocol;
};

//服务器程序(命令调用类)
class Server{
public:
    void addRequest(AbstractCommand* command){
        mCommands.push(command);
    }

    //启动处理程序
    void startHandle(){
        while(!mCommands.empty()){
            Sleep(1000);
            AbstractCommand* command =mCommands.front();
            command->handle();
            mCommands.pop();
        }
    }
    std::queue<AbstractCommand*> mCommands;
};


int
main(){

    HandleClinetProtocal* protocal=new HandleClinetProtocal;

    //客户端增加金币的请求
    AbstractCommand* addmoney =new AddMoneyCommand(protocal);

    //客户端增加钻石的请求
    AbstractCommand* adddiamond =new AddDiamondCommand(protocal);

    //将客户端的请求加入到请求队列中
    Server* server =new Server;
    server->addRequest(addmoney);
    server->addRequest(adddiamond);

    //服务器开始处理请求
    server->startHandle();

    return 0;
}

Interpreter mode:

        refers to given a language (expression), defining a representation of its grammar, and defining an interpreter, using the interpreter to interpret sentences (expressions) in the language

advantage:

    1. Has good scalability. Grammar translation is implemented through classes, and extending classes can expand its interpretation capabilities.

    2. The implementation difficulty is low. Each expression node class in the syntax tree has a certain similarity, so it is relatively easy to implement.

shortcoming:

    1. Low execution efficiency. There are usually a large number of loops and recursive statements in the interpreter. When the sentences being interpreted are complex, the performance of the program is greatly affected.

    2. Class expansion problem. When there are more rules, the number of classes also expands.

Applicable scene:

    When a specific type of problem occurs frequently enough, for example, simple syntax explanation, compiler, calculation of operational expressions, regular expressions, log processing: when using scripting language or programming language to process logs,

    A large number of reports will be generated, and the logs need to be parsed and reports generated.

    The log formats of each service are different, but the elements in the data are the same. In this case, the main solution to solve the above problems through programs is to use the interpreter mode. The interpreter mode is rarely used in daily projects.

Compare with the adapter pattern: The two patterns are similar, but the adapter pattern does not require prior knowledge of the adapter's rules. The interpreter mode requires writing the rules in advance and executing the interpretation according to the rules.

Implementation code:

#include<iostream>
#include<list>
#include<vector>
#include<algorithm>

class Context{
public:
    Context(int num){
        m_num=num;
    }

    void setNum(int num){
        m_num=num;
    }
    int getNum(){
        return m_num;
    }
    void setRes(int res){
        m_res=res;
    }
    int getRes(){
        return m_res;
    }

private:
    int m_num,m_res;

};

class Expression{
public:
    virtual void interpreter(Context* context) =0;
};

class PlusExpression :public Expression{
public:
    virtual void interpreter(Context* context){
        int num=context->getNum();
        num++;
        context->setNum(num);
        context->setRes(num);
    }
};

class MinusExpression :public Expression{
public:
    virtual void interpreter(Context* context){
        int num =context->getNum();
        num--;
        context->setNum(num);
        context->setRes(num);
    }
};


int
main(){

    Context* pcxt =new Context(10);
    Expression* e1=new PlusExpression();

    e1->interpreter(pcxt);
    std::cout<<"PlusExpression:"<<pcxt->getRes()<<std::endl;

    Expression* e2 =new MinusExpression();
    e2->interpreter(pcxt);
    std::cout<<"MinusExpression:"<<pcxt->getRes()<<std::endl;
    delete e1,e2,pcxt;

    return 0;
}

Iterator pattern:

        Provides a unified interface for traversing collection elements, using a consistent method to traverse collection elements without knowing the underlying representation of the collection object. And when you need to traverse no matter what these objects are, you should choose to use the iterator pattern.

advantage:

    1. It supports traversing an aggregate object in different ways without exposing its internal representation

    2. Iterator simplifies aggregate classes

    3. In the iterator pattern, due to the introduction of abstract classes, it is very convenient to add new aggregate classes and iterators without modifying the original code.

shortcoming:

    1. Due to the addition of an abstraction layer, the complexity of the system will be increased.

    2. For simple traversals (such as arrays), it is more cumbersome to use iterators to traverse

Applicable scene:

    Often used to traverse various containers, such as linked lists, arrays, trees, etc. In STL containers, the iterator pattern is widely used.

    Containers (vector, list, set) in STL all provide iterators, which can be used to traverse and access all elements in the container

Implementation code:

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<list>

class Iterator{//迭代抽象类,用于定义得到开始对象、得到下一个对象、判断是否到结尾、当前对象等抽象方法,统一接口
public:
    Iterator(){}
    virtual ~Iterator(){}
    virtual std::string First() =0;
    virtual std::string Next() =0;
    virtual std::string CurrentItem() =0;
    virtual bool IsDone() =0;
};

class Aggregate{//聚集抽象类
public:
    virtual int Count() =0;
    virtual void Push(const std::string& strValue) =0;
    virtual std::string Pop(const int nIndex) =0;
    virtual Iterator* CreateIterator() =0;
};

class ConcreteIterator:public Iterator{
public:
    ConcreteIterator(Aggregate* pAggregate):m_nCurrent(0),Iterator(){
        m_Aggregate =pAggregate;
    }
    std::string First(){
        return m_Aggregate->Pop(0);
    }
    std::string Next(){
        std::string strRet;
        m_nCurrent++;
        if(m_nCurrent < m_Aggregate->Count()){
            strRet =m_Aggregate->Pop(m_nCurrent);
        }
        return strRet;
    }
    std::string CurrentItem(){
        return m_Aggregate->Pop(m_nCurrent);
    }
    bool IsDone(){
        return ((m_nCurrent >= m_Aggregate->Count()) ? true :false);
    }
private:
    Aggregate* m_Aggregate;
    int m_nCurrent;
};

class ConcreteAggregate:public Aggregate{
public:
    ConcreteAggregate():m_pIterator(nullptr){
        m_vecItems.clear();
    }
    ~ConcreteAggregate(){
        if(m_pIterator){
            delete m_pIterator;
            m_pIterator=nullptr;
        }
    }
    Iterator* CreateIterator(){
        if(!m_pIterator){
            m_pIterator=new ConcreteIterator(this);
        }
        return m_pIterator;
    }
    int Count(){
        return m_vecItems.size();
    }
    void Push(const std::string& strValue){
        m_vecItems.push_back(strValue);
    }
    std::string Pop(const int nIndex){
        std::string strRet;
        if(nIndex < Count()){
            strRet =m_vecItems[nIndex];
        }
        return strRet;
    }

private:
    std::vector<std::string> m_vecItems;
    Iterator* m_pIterator;
};


int 
main(){

    ConcreteAggregate* pName=new ConcreteAggregate();
    if(pName){
        pName->Push("hello");
        pName->Push("word");
        pName->Push("cxue");
    }
    Iterator* iter =nullptr;
    iter= pName->CreateIterator();
    if(iter){
        std::string strItem =iter->First();
        while(!iter->IsDone()){
            std::cout<< iter->CurrentItem()<<" is ok "<<std::endl;
            iter->Next();
        }
    }
    return 0;
}

Mediator model:

        It is a behavioral software design pattern, also known as the arbiter pattern. As the name suggests, the role of this pattern is to act as an intermediary to help other classes communicate well.

advantage:

    1. Decoupling. The existence of intermediaries relieves the strong coupling relationship between co-worker objects. They can change independently without affecting the whole, which facilitates reuse.

    2. Good scalability. If the interaction behavior changes, you only need to extend the mediation

    3. Centralized interaction for easy management

shortcoming:

    1. The responsibilities of the intermediary are very important and complex.

Applicable scene:

    When multiple classes are coupled to each other to form a network structure, it is necessary to separate the network structure into a star structure

Code:

#include<iostream>
#include<string>
#include<memory>
#include<vector>
class Mediator{
public:
    virtual void send(std::string message,Colleague* colleague) =0;
    virtual void add(Colleague* colleague)=0;
};

class Colleague{
public:
    Mediator* getMediator(){
        return mediator;
    }
    void setMediator(Mediator* const mediator){
        this->mediator=mediator;
    }
    Colleague(Mediator* mediator){
        this->mediator =mediator;
        this->mediator->add(this);
    }
    virtual void Notify(std::string message)=0;
private:
    Mediator* mediator;
};

class ConcreteColleague1:public Colleague{
public:
    ConcreteColleague1(Mediator* mediator):Colleague(mediator){}
    void send(std::string message){
        getMediator()->send(message,this);
    }
    void Notify(std::string message){
        std::cout<<"同事1收到信息:"+message<<std::endl;
    }
};

class ConcreteColleague2:public Colleague{
public:
    ConcreteColleague2(Mediator* mediator):Colleague(mediator){}
    void send(std::string message){
        getMediator()->send(message,this);
    }
    void Notify(std::string message){
        std::cout<<"同事2收到信息:"+message<<std::endl;
    }
};

class ConcreteMediator:public Mediator{
public:
    void add(Colleague* colleague){
        colleaguesList.push_back(colleague);
    }
    void send(std::string message,Colleague* colleague){
        for(auto value:colleaguesList){
            if(value!=colleague){
                value->Notify(message);
            }
        }
    }
private:
    std::vector<Colleague*> colleaguesList;
};


int
main(){

    Mediator* mediator =new ConcreteMediator();
    ConcreteColleague1* colleague1 =new ConcreteColleague1(mediator);
    ConcreteColleague2* colleague2 =new ConcreteColleague2(mediator);
    colleague1->send("早上好啊");
    colleague2->send("早安!");


    return 0;
}

Memo mode:

        Capture the internal state of an object and save this state outside the object without destroying encapsulation. This way you can restore the object to its original state later.

advantage:

    1. When the status of the initiator role changes, it may be a wrong change. We can use the memo mode to restore this wrong change.

    2. The status of the backup is saved outside the initiator role. In this way, the initiator role does not need to manage the status of each backup.

shortcoming:

    1. If the backup object contains a large amount of information or the creation and recovery operations are very frequent, it may cause a lot of performance overhead.

Applicable scene:

    It is necessary to save the state of an object at a certain moment so that it can be restored to the current state when needed later.

    If you let other objects directly save the state of the current object, it will expose the implementation details of the object and destroy the encapsulation of the object.

Implementation code:

#include<iostream>
#include<list>
#include<memory>
#include<string>
#include<vector>

class Memento{
public:
    explicit Memento(const std::string& state):state(state){}
    std::string getState()const{
        return state;
    }
private:
    std::string state;
};

class Originator{
public:
    std::string getState()const{
        return state;
    }
    void setState(const std::string& state){
        this->state=state;
    }
    Memento SaveStateMemento(){
        return Memento(state);
    }
    void getStateFromMemento(Memento memento){
        state=memento.getState();
    }

private:
    std::string state;
};

class Caretaker{
public:
    void add(Memento memento){
        mementoList.push_back(memento);
    }
    Memento get(int index){
        return mementoList[index];
    }

private:
    std::vector<Memento> mementoList;

};

int
main(){

    Originator originator;
    Caretaker caretaker;
    originator.setState("状态1,攻击力为100");

    //保存当前状态
    caretaker.add(originator.SaveStateMemento());

    //受到debuff,攻击力下降
    originator.setState("状态2,攻击力为80");

    //保存状态
    caretaker.add(originator.SaveStateMemento());

    std::cout<<"当前状态:"<<originator.getState()<<std::endl;
    std::cout<<"debuff状态结束,回到状态1"<<std::endl;
    originator.getStateFromMemento(caretaker.get(0));
    std::cout<<"恢复到状态1后的状态"<<originator.getState();

    return 0;
}

Observer mode:

        Define one-to-many dependency relationships between objects. When the state of an object changes, all objects that depend on it are notified and automatically updated.

advantage:

    1. Loose coupling: The observer pattern provides a loosely coupled design, so that when the state of an object changes, it does not need to know how other objects use this information. Yes the system is easier to expand and maintain

    2. Dynamic association: The observer pattern allows observers to be dynamically added or removed at runtime without modifying the code of the subject or other observers.

    3. Abstract decoupling: Since subjects and observers communicate only through abstract interfaces, the coupling between them is abstract rather than concrete.

shortcoming:

    1. It may lead to unexpected updates. If an observer performs some operations after receiving the notification, and these operations lead to changes in the topic status, it may lead to unexpected updates.

    2. It may cause performance problems. If there are a large number of observers that need to be updated, then notifying all observers may cause performance problems.

    3. May increase complexity. If not implemented correctly, the observer pattern may increase the complexity of the system.

Applicable scene:

    When the state of an object changes, other objects need to be notified. When an object's state changes, some operations need to be performed. When a loosely coupled design needs to be implemented between multiple objects

Code:

#include<iostream>
#include<string>
#include<memory>
#include<list>

class AbstractHero{//抽象的英雄,抽象的观察者
public:
    virtual void Update() =0;
};

class HeroA :public AbstractHero{//具体的英雄,具体的观察者
public:
    HeroA(){
        std::cout<<"英雄A正在撸BOSS"<<std::endl;
    }
    virtual void Update(){
        std::cout<<"英雄A停止撸,待机状态"<<std::endl;
    }
};

class HeroB :public AbstractHero{
public:
    HeroB(){
        std::cout<<"英雄B正在撸BOSS"<<std::endl;
    }
    virtual void Update(){
        std::cout<<"英雄B停止撸,待机状态"<<std::endl;
    }
};

class AbstractBoss{//定义抽象的观察目标
public:
    virtual void addHero(AbstractHero* hero) =0;//添加观察者
    virtual void deleteHero(AbstractHero* hero)=0;//删除观察者
    virtual void notifv()=0;
};

class BOSSA:public AbstractBoss{
public:
    virtual void addHero(AbstractHero* hero){
        pHeroList.push_back(hero);
    }
    virtual void deleteHero(AbstractHero* hero){
        pHeroList.remove(hero);
    }
    virtual void notifv(){
        for(std::list<AbstractHero*>::iterator it=pHeroList.begin();it!=pHeroList.end();it++){
            (*it)->Update();
        }
    }
    std::list<AbstractHero*> pHeroList;
};


int
main(){

    //创建观察者
    AbstractHero* heroA=new HeroA;
    AbstractHero* heroB=new HeroB;

    //创建观察目标
    AbstractBoss* bossA=new BOSSA;
    bossA->addHero(heroA);
    bossA->addHero(heroB);

    std::cout<<"heroA 阵亡"<<std::endl;
    bossA->deleteHero(heroA);

    std::cout<<"Boss死了,通知其他英雄停止攻击"<<std::endl;
    bossA->notifv();
    delete heroA,heroB,bossA;

    return 0;
}

Status mode:

        ​​​Allows an object to change its behavior when its internals change, so that the object appears to modify its class. It can be seen as an extension of the strategy. Both are based on compositional mechanisms

        They all change their behavior in different situations by delegating part of the work to "helper" objects. The policy makes these objects completely independent of each other, and they are unaware of the existence of the objects.

        But the state model does not limit the dependence between specific states, and allows them to change their states in different scenarios.

advantage:

    1. It encapsulates the conversion rules and also enumerates possible states. Before enumerating the states, you need to confirm the state type.

    2. Comply with the opening and closing principle. New states can be introduced without modifying existing state classes and contexts

    3. Simplify context code by eliminating bloated state machine conditional statements

shortcoming:

    1. If the state machine has only a few states or rarely changes, it will increase the complexity of the system.

    2. The support for the "opening and closing principle" is not very good. For the state mode that can switch states, adding a new state class requires modifying the source code responsible for the conversion, otherwise it will not be possible to switch to the new state.

Applicable scene:  

    1. The behavior of an object depends on its state, and it must change its behavior according to the state at run time

    2. The code contains a large number of conditional statements related to the state of the object

Code:

#include<iostream>
#include<string>
#include<memory>
class War;
class State{
public:
    virtual void Prophase(){}
    virtual void Metaphase(){}
    virtual void Anaphase(){}
    virtual void End(){}
    virtual void CurrentState(War* war){}
};

class War{
private:
    State* m_state;//目前状态
    int m_days;//战争持续时间
public:
    War(State* state):m_state(state),m_days(0){}
    ~War(){ delete m_state; }
    int GetDays(){ return m_days; }
    void SetDays(int days){ m_days = days; }
    void SetState(State* state){ delete m_state; m_state = state; }
    void GetState(){ m_state->CurrentState(this); }
};

//战争结束状态
class EndState: public State{
public:
    void End(War* war){
        std::cout<<"战争结束"<<std::endl;
    }
    void CurrentState(War* war){
        End(war);
    }
};

//后期
class AnaphaseState: public State{
public:
    void Anaphase(War* war){//后期具体的行为
        if(war->GetDays() < 30){
            std::cout<<"第"<<war->GetDays()<<"天:战争后期,双方拼死一搏"<<std::endl;
        }else{
            war->SetState(new EndState());
            war->GetState();
        }
    }
    void CurrentState(War* war){ Anaphase(war); }
};


int
main(){

    War* war=new War(new AnaphaseState());
    for(int i=1;i<50;i+=5){
        war->SetDays(i);
        war->GetState();
    }
    return 0;
}

Strategy mode:

        When the algorithm is encapsulated, there will be multiple algorithms when dealing with the problem. It is similar to the Template mode.

advantage:

    1. Provides perfect support for the "opening and closing principle". Users can choose algorithms or behaviors without modifying the original code, and can also flexibly add new algorithms or behaviors.

    2. It can avoid the use of multiple conditional judgments and reduce the difficulty of program maintenance.

    3. Strong scalability

    4. Provides an algorithm reuse mechanism, and different environment classes can easily reuse these strategy classes.

shortcoming:

    1. It will lead to an increase in strategy classes (class explosion), making it difficult to maintain in the future.

    2. All policy classes are exposed to the outside world, the client’s permissions are too large, and the security is weak

Applicable scene:

    1. If there are many classes in a system, and the only difference between them is their behavior, the strategy pattern can be used to dynamically allow an object to choose among many behaviors.

    2. A system needs to dynamically choose one of several algorithms

    3. If an object has many behaviors, without appropriate patterns, these behaviors have to be implemented using multiple conditional selection statements.

Precautions:

    If a system has more than four strategies, you need to consider using a hybrid mode to solve the problem of strategy explosion

Code:

#include<iostream>
#include<string>
#include<memory>

class WeaponStrategy{
public:
    virtual void UseWeapon()=0;
};

class Knife :public WeaponStrategy{
public:
    virtual void UseWeapon(){
        std::cout<<"使用匕首"<<std::endl;
    }
};

class AK47 :public WeaponStrategy{
public:
    virtual void UseWeapon(){
        std::cout<<"使用AK47"<<std::endl;
    }
};

class Character{
public:
    WeaponStrategy* pWeapon;
    void setWeapon(WeaponStrategy* pWeapon){
        this->pWeapon=pWeapon;
    }
    void ThrowWeapon(){
        this->pWeapon->UseWeapon();
    }
};

int
main(){

    Character* character=new Character;
    WeaponStrategy* knife=new Knife;
    WeaponStrategy* ak47=new AK47;
    character->setWeapon(ak47);
    character->ThrowWeapon();
    character->setWeapon(knife);
    character->ThrowWeapon();
    delete character,knife,ak47;
    return 0;
}

Template mode:

        Define the skeleton of an algorithm in one operation, deferring some steps to subclasses. Template methods allow subclasses to redefine specific steps of an algorithm without changing the structure of the algorithm.

advantage:

    1. Encapsulate the constant part and extend the variable part

    2. Extract public code to facilitate maintenance

    3. Behavior is controlled by the parent class and implemented by the subclass (to achieve code reuse)

shortcoming:

    1. Each different implementation requires a subclass to implement, resulting in an increase in the number of classes and making the system larger

Applicable scene:

        For a certain business logic, there are different detailed implementations in different objects, but the logical framework is the same

Code:

#include<iostream>
#include<algorithm>
#include<string>

class DrinkTemplate{//做饮料模板
public:
    virtual void BoildWater()=0;
    virtual void Brew()=0;
    virtual void PourInCup()=0;
    virtual void AddSomething()=0;
    void Make(){
        BoildWater();
        Brew();
        PourInCup();
        AddSomething();
    }
};

class Coffee :public DrinkTemplate{
    virtual void BoildWater(){
        std::cout<<"煮山泉水"<<std::endl;
    }
    virtual void Brew(){
        std::cout<<"冲泡咖啡"<<std::endl;
    }
    virtual void PourInCup(){
        std::cout<<"咖啡加入杯中"<<std::endl;
    }
    virtual void AddSomething(){
        std::cout<<"加糖加牛奶"<<std::endl;
    }
};

class Tea :public DrinkTemplate{
    virtual void BoildWater(){
        std::cout<<"煮自来水"<<std::endl;
    }
    virtual void Brew(){
        std::cout<<"冲泡铁观音"<<std::endl;
    }
    virtual void PourInCup(){
        std::cout<<"茶水加入杯中"<<std::endl;
    }
    virtual void AddSomething(){
        std::cout<<"加下雨天氛围"<<std::endl;
    }
};

int
main(){

    Tea* tea = new Tea;
    tea->Make();
    Coffee* coffee=new Coffee;
    coffee->Make();

    return 0;
}

Visitor mode:

        It encapsulates some operations that act on each element of a certain data structure. It can define new operations that act on these elements without changing the data structure.

advantage:

    1. Good scalability. To extend operations on elements, just add visitors

    2. Meet the single responsibility principle. Related operations are encapsulated into a visitor, making the visitor responsible for a single

    3. Decoupling, decoupling the data structure itself and the operations that act on it

shortcoming:

    1. It is not easy to add categories. Every time an element class is added, the visitor interface and implementation must change.

    2. Violates the principle of dependency inversion. Visitors provide specific elements rather than abstract elements.

    3. Destroy the encapsulation. Visitors can get details of the visited element

Applicable scene:

        It is suitable for systems where the data structure is relatively stable and the algorithm is easy to change.

Code:

#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<List>
class Man;
class Woman;

class Action{
public:
    virtual void getManResult(Man* man) =0;
    virtual void getWomanResult(Woman* woman) =0;
};

class Success:public Action{
public:
    void getManResult(Man* man) override{
        std::cout<<"男人的给的评价该歌手是很成功"<<std::endl;
    }
    void getWomanResult(Woman* woman) override{
        std::cout<<"女人给的评价是该歌手很成功"<<std::endl;
    }
};

class Fail:public Action{
public:
    void getManResult(Man* man) override{
        std::cout<<"男人的给的评价该歌手是很失败"<<std::endl;
    }
    void getWomanResult(Woman* woman) override{
        std::cout<<"女人给的评价是该歌手很失败"<<std::endl;
    }
};

class Person{
public:
    virtual void accept(Action* action) =0;
};

class Man:public Person{
public:
    void accept(Action* action)override{
        action->getManResult(this);
    }
};

class Woman:public Person{
public:
    void accept(Action* action)override{
        action->getWomanResult(this);
    }
};

class ObjectStructure{
public:
    void attach(Person* p){
        persons.push_back(p);
    }
    void detach(Person* p){
        persons.remove(p);
        delete p;
    }
    //显示测评情况
    void display(Action* action){
        for(auto value:persons){
            value->accept(action);
        }
    }

private:
    std::list<Person*> persons;
};


int
main(){

    ObjectStructure* objectStructure =new ObjectStructure;
    objectStructure->attach(new Man);
    objectStructure->attach(new Woman);


    return 0;
}

reference:

C++ design patterns (23 types in total)_momohola's blog-CSDN blog

Several common design patterns in C++_c++ design patterns_lTimej's blog-CSDN blog

11 commonly used design patterns in C++_c++ design model_CBoy_JW's blog-CSDN blog

Big talk design pattern

effective c++

"C++ Design Patterns"_c++ Design Patterns_Yiqou Ersanli's Blog-CSDN Blog

txinyu's blog_-CSDN blog

Guess you like

Origin blog.csdn.net/songbijian/article/details/132656594