Design Patterns in software development and MQL5 (Part 4): Behavioral Patterns 2

Mohamed Abdelmaaboud | 22 December, 2023

Introduction

In this article, we will complete the behavioral design patterns to complete the topic of the design patterns in software and how we can use them in the MQL5. We have identified in the previous articles all creational patterns through the article of Design Patterns in Software Development and MQL5 (Part I): Creational Patterns and we have identified all structural patterns through the article of Design Patterns in Software Development and MQL5 (Part 2): Structural Patterns. Then, we identified in the previous article of Design Patterns in Software Development and MQL5 (Part 3): Behavioral Patterns 1 some of the behavioral patterns and they were the same as the following: Chain of responsibility, Command, Interpreter, Iterator, and Mediator. We also identified what are design patterns, which are patterns that can be used to define and manage the method of communication between objects.

In this article, we will complete what remains in the behavioral patterns through the following topics:

We identified before that behavioral patterns are the patterns of behavior that are concerned with the assignment and determination of responsibilities between objects. They also identify how objects can communicate or interact with each other. They characterize a complex flow of control that's hard to follow at run time. They allow you to focus only on the way objects are connected, shifting your focus away from the control flow.

If you have read my previous articles in this series, you will be familiar with our approach to presenting each pattern through the following points:

It is important to mention that if you understand the OOP (object Oriented Programing) topic this will help to well understand design patterns topic and if you want to read or learn some of this topic you can also read my previous Understanding MQL5 Object-Oriented Programming (OOP) article. I hope that you find my articles are useful in your journey to improve your programming skills by identifying one of the most important topics which is design patterns to write clean, extendable, reusable, and well-tested code.

Disclaimer: All information provided 'as is' only for educational purposes and is not prepared for trading purposes or advice. The information does not guarantee any kind of result. If you choose to use these materials on any of your trading accounts, you will do that at your own risk and you will be the only responsible.

Memento

In this section, we are going to identify the Memento pattern as a behavioral design pattern. The Memento pattern can be used to externalize the state of an object to provide rollback functionality, and it is also known as a token.

What does the pattern do?

We can use the Memento pattern when we need to store a snapshot of the state of the object to be restored at a later time, and when a direct interface to get the state would expose the details of the execution and break the encapsulation of the object. So this pattern will capture and externalize the object state to be restored later, the following is a diagram of the structure of this pattern that shows how it can work:

Memento

As we can see in the previous graph we have the following as participants:

There are some pitfalls in the use of this type of pattern and they are the same as those listed below:

What does design pattern solve?

By using this pattern, the following problems can be solved:

How can we use it in MQL5?

In this part, we are going to try to use the Memento pattern in MQL5 and it can be done by going through the following steps:

Declare the Memento class by using the class keyword

class Memento
  {
protected:
   string            m_state;
public:
   string            GetState(void);
   void              SetState(string);
                     Memento(string);
  };
Memento::Memento(string state):
   m_state(state)
  {
  }
string Memento::GetState(void)
  {
   return m_state;
  }
void Memento::SetState(string state)
  {
   m_state=state;
  }

Declare the Originator class

class Originator
  {
protected:
   string            m_state;
public:
   void              SetMemento(Memento& memento);
   Memento*          CreateMemento(void);
   string            State(void);
   void              State(string);
  };
void Originator::SetMemento(Memento& memento)
  {
   m_state=memento.GetState();
  }
Memento* Originator::CreateMemento(void)
  {
   return new Memento(m_state);
  }
void Originator::State(string state)
  {
   m_state=state;
  }
string Originator::State(void)
  {
   return m_state;
  }

Declare the Caretaker class

class Caretaker
  {
public:
   Memento*          memento;
                    ~Caretaker(void);
  };
Caretaker::~Caretaker(void)
  {
   if(CheckPointer(memento)==1)
     {
      delete memento;
     }
  }

So the following is the complete code in one block for the use of the Memento pattern in MQL5:

//+------------------------------------------------------------------+
//|                                                      Memento.mqh |
//+------------------------------------------------------------------+
class Memento
  {
protected:
   string            m_state;
public:
   string            GetState(void);
   void              SetState(string);
                     Memento(string);
  };
Memento::Memento(string state):
   m_state(state)
  {
  }
string Memento::GetState(void)
  {
   return m_state;
  }
void Memento::SetState(string state)
  {
   m_state=state;
  }
class Originator
  {
protected:
   string            m_state;
public:
   void              SetMemento(Memento& memento);
   Memento*          CreateMemento(void);
   string            State(void);
   void              State(string);
  };
void Originator::SetMemento(Memento& memento)
  {
   m_state=memento.GetState();
  }
Memento* Originator::CreateMemento(void)
  {
   return new Memento(m_state);
  }
void Originator::State(string state)
  {
   m_state=state;
  }
string Originator::State(void)
  {
   return m_state;
  }
class Caretaker
  {
public:
   Memento*          memento;
                    ~Caretaker(void);
  };
Caretaker::~Caretaker(void)
  {
   if(CheckPointer(memento)==1)
     {
      delete memento;
     }
  }

Observer

In this part, we will identify another behavioral pattern which is the Observer pattern. It defines a one-to-many dependency between objects so that when one object changes its state, all its dependencies will be notified and will be automatically updated. It is also known as Dependents and Publish-Subscribe.

What does the pattern do?

We can use the Observer pattern when we need to encapsulate aspects of abstraction in separate objects to be able to vary and reuse them independently, when there is a need to change other objects when changing one and we do not know the number of objects needed to be changed, and when we need the object to send a notification to other objects without coupling between these objects.

Based on the foregoing, this pattern can be represented in the following diagram, to have a better understanding of how this pattern works:

Observer

As we can see in the previous graph we can say that we have participants in this pattern the same as the following:

There are pitfalls when using the observer pattern and they are the same as the following:

What does design pattern solve?

According to what we understand in the way that the Observer pattern works we can say that it will be able to solve or there are the following benefits when using it:

How can we use it in MQL5?

Now, it is time to see a method to use the Observer pattern in MQL5 and it will be the same as the following:

Using the interface keyword to declare the Observer area to determine the classes, and functions that we need

interface Observer
  {
   void Update(string state);
  };

Using the class keyword to declare the Subject

class Subject
  {
public:
                     Subject(void);
                    ~Subject(void);
   void              Attach(Observer* observer);
   void              Detach(Observer& observer);
   void              Notify(void);
   void              State(string state);
   string            State(void) {return m_state;}

protected:
   string            m_state;
   Observer*         m_observers[];

   int               Find(Observer& observer);
  };
Subject::Subject(void):
   m_state(NULL)
  {
  }
Subject::~Subject(void)
  {
   int itotal=ArraySize(m_observers);
   for(int i=0; i<itotal; i++)
     {
      Observer* item=m_observers[i];
      if(CheckPointer(item)==1)
        {
         delete item;
        }
     }
  }
void Subject::State(string state)
  {
   m_state=state;
  }
void Subject::Notify(void)
  {
   int itotal=ArraySize(m_observers);
   for(int i=0; i<itotal; i++)
     {
      m_observers[i].Update(m_state);
     }
  }
void Subject::Attach(Observer *observer)
  {
   int size=ArraySize(m_observers);
   ArrayResize(m_observers,size+1);
   m_observers[size]=observer;
  }
void Subject::Detach(Observer &observer)
  {
   int find=Find(observer);
   if(find==-1)
      return;
   Observer* item=m_observers[find];
   if(CheckPointer(item)==1)
      delete item;
   ArrayRemove(m_observers,find,1);
  }
int Subject::Find(Observer &observer)
  {
   int itotal=ArraySize(m_observers);
   for(int i=0; i<itotal; i++)
     {
      Observer* item=m_observers[i];
      if(item==&observer)
         return i;
     }
   return -1;
  }

Using the class keyword to declare the ConcreteSubject

class ConcreteSubject:public Subject
  {
  public:
   void              State(string state);
   string            State(void) {return m_state;}
  };
void ConcreteSubject::State(string state)
  {
   m_state=state;
  } 

Using the class keyword to declare the ConcreteObserver

class ConcreteObserver:public Observer
  {
public:
   void              Update(string state);
                     ConcreteObserver(ConcreteSubject& subject);
protected:
   string            m_observer_state;
   ConcreteSubject*  m_subject;
  };
ConcreteObserver::ConcreteObserver(ConcreteSubject& subject):
   m_subject(&subject)
  {
  }
void ConcreteObserver::Update(string state)
  {
   m_observer_state=state;
  }

So, the following is the full code for using the Observer behavioral design pattern in MQL5:

//+------------------------------------------------------------------+
//|                                                     Observer.mqh |
//+------------------------------------------------------------------+
interface Observer
  {
   void Update(string state);
  };
class Subject
  {
public:
                     Subject(void);
                    ~Subject(void);
   void              Attach(Observer* observer);
   void              Detach(Observer& observer);
   void              Notify(void);
   void              State(string state);
   string            State(void) {return m_state;}

protected:
   string            m_state;
   Observer*         m_observers[];

   int               Find(Observer& observer);
  };
Subject::Subject(void):
   m_state(NULL)
  {
  }
Subject::~Subject(void)
  {
   int itotal=ArraySize(m_observers);
   for(int i=0; i<itotal; i++)
     {
      Observer* item=m_observers[i];
      if(CheckPointer(item)==1)
        {
         delete item;
        }
     }
  }
void Subject::State(string state)
  {
   m_state=state;
  }
void Subject::Notify(void)
  {
   int itotal=ArraySize(m_observers);
   for(int i=0; i<itotal; i++)
     {
      m_observers[i].Update(m_state);
     }
  }
void Subject::Attach(Observer *observer)
  {
   int size=ArraySize(m_observers);
   ArrayResize(m_observers,size+1);
   m_observers[size]=observer;
  }
void Subject::Detach(Observer &observer)
  {
   int find=Find(observer);
   if(find==-1)
      return;
   Observer* item=m_observers[find];
   if(CheckPointer(item)==1)
      delete item;
   ArrayRemove(m_observers,find,1);
  }
int Subject::Find(Observer &observer)
  {
   int itotal=ArraySize(m_observers);
   for(int i=0; i<itotal; i++)
     {
      Observer* item=m_observers[i];
      if(item==&observer)
         return i;
     }
   return -1;
  }
class ConcreteSubject:public Subject
  {
  public:
   void              State(string state);
   string            State(void) {return m_state;}
  };
void ConcreteSubject::State(string state)
  {
   m_state=state;
  }  
class ConcreteObserver:public Observer
  {
public:
   void              Update(string state);
                     ConcreteObserver(ConcreteSubject& subject);
protected:
   string            m_observer_state;
   ConcreteSubject*  m_subject;
  };
ConcreteObserver::ConcreteObserver(ConcreteSubject& subject):
   m_subject(&subject)
  {
  }
void ConcreteObserver::Update(string state)
  {
   m_observer_state=state;
  }

State

In this part, we will identify the State pattern which allows the object to change its behavior in case of changing in its internal state and the object will appear to change its class. It is also know as Objects for States. We can use it when the behavior of the object depends on its state by changing the behavior of the object based on the state at run-time. It can be used also when we have operations with large conditional statements and all of that depends on the state of the object.

What does the pattern do?

If we need to understand how the State behavioral pattern works, we can see through the following graph:

State

As we can see in the previous graph we have the following participants:

Context: it identifies the interface of the interest to the client. It also maintains the subclass of the instance of the ConcreteState that identifies the current state.

State: it identifies the interface to encapsulate the behavior of a specific state of the context.

ConcreteState subclasses: the subclass executes the behavior of the state of the context and this is for every subclass.

According to what we said about the State pattern, it can be used when we have an object that behaves differently depending on its current state. But there is a pitfalls when using this pattern that is we will have more classes which means more code.

What does design pattern solve?

Although there are the same pitfalls that we have presented when using the State pattern, there are advantages that can be considered as problems that can be solved when using it. These advantages are the same as the following:

How can we use it in MQL5?

The following steps are for a method about how we can use the State pattern in MQL5:

Declare the Context class

class Context;

Declare the State interface

interface State
  {
   void Handle(Context& context);
  };

Declare the m_state object

State*            m_state;

Declare the Context class

class Context
  {
public:
                     Context(State& state);
                    ~Context(void);
   State*            State(void) {return m_state;}
   void              State(State& state);
   void              Request(void);
  };
Context::~Context(void)
  {
   if(CheckPointer(m_state)==1)
      delete m_state;
  }
void Context::State(State& state)
  {
   delete m_state;
   m_state=&state;
  }
void Context::Request(void)
  {
   m_state.Handle(this);
  }

Declare the ConcreteStateA class

class ConcreteStateA:public State
  {
public:
   void              Handle(Context& context);
  };
void ConcreteStateA::Handle(Context& context)
  {
   context.State(new ConcreteStateB);
  }

Declare the ConcreteStateB class

class ConcreteStateB:public State
  {
public:
   void              Handle(Context& context);
  };
void ConcreteStateB::Handle(Context& context)
  {
   context.State(new ConcreteStateA);
  }

So, the following is the one block of code to use the State design pattern in MQL5:

//+------------------------------------------------------------------+
//|                                                        State.mqh |
//+------------------------------------------------------------------+
class Context;
interface State
  {
   void Handle(Context& context);
  };
State*            m_state;
class Context
  {
public:
                     Context(State& state);
                    ~Context(void);
   State*            State(void) {return m_state;}
   void              State(State& state);
   void              Request(void);
  };
Context::~Context(void)
  {
   if(CheckPointer(m_state)==1)
      delete m_state;
  }
void Context::State(State& state)
  {
   delete m_state;
   m_state=&state;
  }
void Context::Request(void)
  {
   m_state.Handle(this);
  }
  
class ConcreteStateA:public State
  {
public:
   void              Handle(Context& context);
  };
void ConcreteStateA::Handle(Context& context)
  {
   context.State(new ConcreteStateB);
  }

class ConcreteStateB:public State
  {
public:
   void              Handle(Context& context);
  };
void ConcreteStateB::Handle(Context& context)
  {
   context.State(new ConcreteStateA);
  }

Strategy

Here is another behavioral pattern which is the Strategy pattern. It identifies a group of algorithms, encapsulates them, and makes them interchangeable. This allows the algorithm to vary independently of the clients that use it. So, we can use it when we need to enable the algorithm to be selected at run-time, and when we need to eliminate conditional statements. It is also known as the Policy.

So, we can say that we can use the Strategy pattern when:

What does the pattern do?

If we want to understand how this Strategy pattern works, we can do that through the following graph:

Strategy

As we can see in the previous graph the Strategy pattern has the following as participants:

Although benefits can be obtained when using this strategy pattern, there are pitfalls and they are the same as the following:

What does design pattern solve?

The following are the consequences or benefits of using the Strategy pattern:

How can we use it in MQL5?

Now we will identify a way to use the Strategy pattern, which will be the same as the following steps:

Declare the Strategy interface to declare classes, and functions within:

interface Strategy
  {
   void AlgorithmInterface(void);
  };

Declare the Context class

class Context
  {
public:
                     Context(Strategy& strategy);
                    ~Context(void);

   void              ContextInterface(void);
protected:
   Strategy*         m_strategy;
  };
Context::Context(Strategy& strategy)
  {
   m_strategy=&strategy;
  }
Context::~Context(void)
  {
   if(CheckPointer(m_strategy)==1)
      delete m_strategy;
  }
void Context::ContextInterface(void)
  {
   m_strategy.AlgorithmInterface();
  }

Declare the ConcreteStrategyA class

class ConcreteStrategyA : public Strategy
  {
public:
   void              AlgorithmInterface(void);
  };
void ConcreteStrategyA::AlgorithmInterface(void)
  {
  }

Declare the ConcreteStrategyB class

class ConcreteStrategyB : public Strategy
  {
public:
   void              AlgorithmInterface(void);
  };
void ConcreteStrategyB::AlgorithmInterface(void)
  {
  }

Declare the ConcreteStrategyC class

class ConcreteStrategyC : public Strategy
  {
public:
   void              AlgorithmInterface(void);
  };
void ConcreteStrategyC::AlgorithmInterface(void)
  {
  }  

So, the following code is the full code in one block to use the Strategy pattern in MQL5:

//+------------------------------------------------------------------+
//|                                                     Strategy.mqh |
//+------------------------------------------------------------------+
interface Strategy
  {
   void AlgorithmInterface(void);
  };
class Context
  {
public:
                     Context(Strategy& strategy);
                    ~Context(void);

   void              ContextInterface(void);
protected:
   Strategy*         m_strategy;
  };
Context::Context(Strategy& strategy)
  {
   m_strategy=&strategy;
  }
Context::~Context(void)
  {
   if(CheckPointer(m_strategy)==1)
      delete m_strategy;
  }
void Context::ContextInterface(void)
  {
   m_strategy.AlgorithmInterface();
  }
class ConcreteStrategyA : public Strategy
  {
public:
   void              AlgorithmInterface(void);
  };
void ConcreteStrategyA::AlgorithmInterface(void)
  {
  }
class ConcreteStrategyB : public Strategy
  {
public:
   void              AlgorithmInterface(void);
  };
void ConcreteStrategyB::AlgorithmInterface(void)
  {
  }
class ConcreteStrategyC : public Strategy
  {
public:
   void              AlgorithmInterface(void);
  };
void ConcreteStrategyC::AlgorithmInterface(void)
  {
  }  

Template Method

The Template Method is another behavioral design pattern that can be useful in software development. It can be used to recognize the basic components of an algorithm within an operation and to delegate certain steps to subclasses. This allows subclasses to redefine these steps without changing the overall structure of the algorithm.

So, we can say that we can use it when:

What does the pattern do?

If we need to understand how this pattern works, we can do this with the help of the following chart, which is a representation of the pattern:

 Template Method

As we can see in the previous graph we have the following participants:

What does design pattern solve?

There are benefits and issues that can be solved when using the template method behavioral pattern and these are the same as the following:

Despite these benefits, there is a pitfall when using this pattern that is all classes must follow the algorithm without exceptions.

How can we use it in MQL5?

If we need to use the Template Method pattern in MQL5 we can do that through the following steps as a method to do that:

Declare the AbstractClass by using the class keyword

class AbstractClass
  {
public:
   virtual void      PrimitiveOperation1(void)=0;
   virtual void      PrimitiveOperation2(void)=0;
   virtual void      TemplateMethod(void);
  };
void AbstractClass::TemplateMethod(void)
  {
   PrimitiveOperation1();
   PrimitiveOperation2();
  }

Declare the ConcreteClass by using the class keyword

  class ConcreteClass : public AbstractClass
  {
public:
   void              PrimitiveOperation1(void);
   void              PrimitiveOperation2(void);
  };
void ConcreteClass::PrimitiveOperation1(void)
  {
  }
void ConcreteClass::PrimitiveOperation2(void)
  {
  }

So, the following block of code is the full code to use the Template Method pattern in MQL5:

//+------------------------------------------------------------------+
//|                                              Template_Method.mqh |
//+------------------------------------------------------------------+
class AbstractClass
  {
public:
   virtual void      PrimitiveOperation1(void)=0;
   virtual void      PrimitiveOperation2(void)=0;
   virtual void      TemplateMethod(void);
  };
void AbstractClass::TemplateMethod(void)
  {
   PrimitiveOperation1();
   PrimitiveOperation2();
  }
  class ConcreteClass : public AbstractClass
  {
public:
   void              PrimitiveOperation1(void);
   void              PrimitiveOperation2(void);
  };
void ConcreteClass::PrimitiveOperation1(void)
  {
  }
void ConcreteClass::PrimitiveOperation2(void)
  {
  }

Visitor

Here is the last type of behavior pattern, the Visitor pattern. It makes it possible to identify a new operation without having an impact on the classes of elements on which the operation is performed. We can use this pattern when we have an object structure containing many classes of objects and interfaces that are different and we need to execute operations on these objects, we have many different and unrelated operations that need to be executed on objects within the object structure, and there is a desire to avoid the "pollution" of their classes with these operations, and we have the classes that define the structure of the object rarely change, but you do need to define new operations on the structure.

What does the pattern do?

In the following graph, we can see how the pattern of the visitors is working through its structure:

Visitor

As we can see in the previous graph we have the following participants:

What does design pattern solve?

According to what we mentioned about the visitor pattern we can find that it solves or has the following benefits are some of them when applying or using it:

How can we use it in MQL5?

If we need to use the Visitor pattern in MQL5 the following are steps of a method to do that:

Declare the Visitor interface

interface Visitor;

Declare the Element class

class Element
  {
protected:
   Visitor*          m_visitor;
public:
                    ~Element(void);
   virtual void      Accept(Visitor* visitor)=0;
protected:
   void              Switch(Visitor* visitor);
  };
Element::~Element(void)
  {
   if(CheckPointer(m_visitor)==1)
      delete m_visitor;
  }
void Element::Switch(Visitor *visitor)
  {
   if(CheckPointer(m_visitor)==1)
      delete m_visitor;
   m_visitor=visitor;
  }

Declare the ConcreteElementA class

class ConcreteElementA : public Element
  {
public:
   void              Accept(Visitor*);
   void              OperationA(void);
  };
void ConcreteElementA::OperationA(void)
  {
  }
void ConcreteElementA::Accept(Visitor *visitor)
  {
   Switch(visitor);
   visitor.VisitElementA(&this);
  }

Declare the ConcreteElementB class

class ConcreteElementB : public Element
  {
public:
   void              Accept(Visitor* visitor);
   void              OperationB(void);
  };
void ConcreteElementB::OperationB(void)
  {
  }
void ConcreteElementB::Accept(Visitor *visitor)
  {
   Switch(visitor);
   visitor.VisitElementB(&this);
  }

Using the interface keyword to declare the Visitor to define VisitElementA and  VisitElementB within

interface Visitor
{
   void VisitElementA(ConcreteElementA*);
   void VisitElementB(ConcreteElementB*);
};

Declare the ConcreteVisitor1 class

class ConcreteVisitor1 : public Visitor
  {
public:
   void              VisitElementA(ConcreteElementA* visitor);
   void              VisitElementB(ConcreteElementB* visitor);
  };
void ConcreteVisitor1::VisitElementA(ConcreteElementA* visitor)
  {
   visitor.OperationA();
  }
void ConcreteVisitor1::VisitElementB(ConcreteElementB* visitor)
  {
   visitor.OperationB();
  }

Declare the ConcreteVisitor2 class

class ConcreteVisitor2 : public Visitor
  {
public:
   void              VisitElementA(ConcreteElementA*);
   void              VisitElementB(ConcreteElementB*);
  };
void ConcreteVisitor2::VisitElementA(ConcreteElementA* visitor)
  {
   visitor.OperationA();
  }
void ConcreteVisitor2::VisitElementB(ConcreteElementB* visitor)
  {
   visitor.OperationB();
  }

So, the following block of code is the full code to see the Visitor pattern in MQL5:

//+------------------------------------------------------------------+
//|                                                      Visitor.mqh |
//+------------------------------------------------------------------+
interface Visitor;
class Element
  {
protected:
   Visitor*          m_visitor;
public:
                    ~Element(void);
   virtual void      Accept(Visitor* visitor)=0;
protected:
   void              Switch(Visitor* visitor);
  };
Element::~Element(void)
  {
   if(CheckPointer(m_visitor)==1)
      delete m_visitor;
  }
void Element::Switch(Visitor *visitor)
  {
   if(CheckPointer(m_visitor)==1)
      delete m_visitor;
   m_visitor=visitor;
  }
class ConcreteElementA : public Element
  {
public:
   void              Accept(Visitor*);
   void              OperationA(void);
  };
void ConcreteElementA::OperationA(void)
  {
  }
void ConcreteElementA::Accept(Visitor *visitor)
  {
   Switch(visitor);
   visitor.VisitElementA(&this);
  }
class ConcreteElementB : public Element
  {
public:
   void              Accept(Visitor* visitor);
   void              OperationB(void);
  };
void ConcreteElementB::OperationB(void)
  {
  }
void ConcreteElementB::Accept(Visitor *visitor)
  {
   Switch(visitor);
   visitor.VisitElementB(&this);
  }
interface Visitor
  {
   void VisitElementA(ConcreteElementA*);
   void VisitElementB(ConcreteElementB*);
  };
class ConcreteVisitor1 : public Visitor
  {
public:
   void              VisitElementA(ConcreteElementA* visitor);
   void              VisitElementB(ConcreteElementB* visitor);
  };
void ConcreteVisitor1::VisitElementA(ConcreteElementA* visitor)
  {
   visitor.OperationA();
  }
void ConcreteVisitor1::VisitElementB(ConcreteElementB* visitor)
  {
   visitor.OperationB();
  }
class ConcreteVisitor2 : public Visitor
  {
public:
   void              VisitElementA(ConcreteElementA*);
   void              VisitElementB(ConcreteElementB*);
  };
void ConcreteVisitor2::VisitElementA(ConcreteElementA* visitor)
  {
   visitor.OperationA();
  }
void ConcreteVisitor2::VisitElementB(ConcreteElementB* visitor)
  {
   visitor.OperationB();
  }

Conclusion

After reading this article and others in this series, you will be able to identify all the types of design patterns that are patterns (creative, structural, and behavioral). We identified each one by understanding how it works when we can use it, what issues or problems can be solved after using each pattern, and how we can use these patterns in the MQL5 to write clean code, which means we get maintainable, reusable, and well-tested code.

We have identified patterns that are the same as the following ones:

You can read the other articles in this series by clicking on the links below:

I cannot stress enough the importance of understanding the topic of design patterns because it can be very useful in creating any software as we have mentioned, so I recommend reading more on this topic and the following some references on the same topic:

I hope that you found this article and series useful for your journey to improve your programming skills in general and MQL5 in particular. If you need to read more articles about programming by the MQL5 and how you can create trading systems based on the most popular technical indicators like moving averages, RSI, Bollinger Bands, and MACD for examples you can check my publications page and I hope that you find them useful also to improve your trading and programming background.