English Русский Español 日本語
preview
Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 2): Strukturelle Muster

Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 2): Strukturelle Muster

MetaTrader 5Handel | 23 Februar 2024, 13:00
125 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Einführung

Willkommen zu einem neuen Artikel, in dem wir über ein sehr wichtiges Thema der Softwareentwicklung sprechen, nämlich Entwurfsmuster, indem wir andere Arten von ihnen fortführen. Wir haben in einem früheren Artikel über die kreativen Entwurfsmuster gesprochen. Wenn Sie mehr über diesen Typ erfahren möchten, lesen Sie den Artikel „Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil I): Erzeugungsmuster“. Wenn Sie neu im Thema Entwurfsmuster sind, empfehle ich Ihnen, diesen Artikel zu lesen, um mehr über das Thema Entwurfsmuster im Allgemeinen zu erfahren und zu lernen, wie nützlich sie in der Softwareentwicklung sind.

Entwurfsmuster sind unentbehrlich, wenn Sie Ihre Softwareentwicklungsfähigkeiten auf die nächste Stufe heben wollen. Diese Muster geben Ihnen die fertige Blaupause, um spezifische Probleme zu lösen, anstatt das Rad neu zu erfinden, sondern sie zu benutzen, um sehr praktische und testbare Lösungen zu erhalten.

In diesem Artikel werden wir fortfahren, indem wir die strukturellen Entwurfsmuster vorstellen und lernen, wie sie in der Welt der Softwareentwicklung sehr nützlich sein können, um größere Strukturen zu bilden, indem wir verwenden, was wir als Klassen haben. Der interessanteste Teil des Artikels besteht darin zu erfahren, wie wir diese Muster in der Programmiersprache MQL5 nutzen können, um von ihnen zu profitieren und effektive Software für den Handel mit dem MetaTrader 5-Terminal zu entwickeln.

Wir werden die Entwurfsmuster des Typs Structural anhand der folgenden Themen behandeln:

Ich hoffe, dass Sie diesen Artikel nützlich finden, um Ihre Entwicklungs- und Programmierfähigkeiten durch das Lernen eines sehr interessanten Themas zu entwickeln. Es ist auch gut zu erwähnen, dass Ihr Wissen über das Thema der objektorientierten Programmierung (OOP) Ihnen sehr helfen wird, das Thema Entwurfsmuster zu verstehen. Wenn Sie über dieses Thema lesen wollen, können Sie das tun, indem Sie meinen vorherigen Artikel „Verstehen der MQL5 Objektorientierte Programmierung (OOP)“ zu diesem Thema lesen und ich hoffe, dass er in diesem Zusammenhang nützlich sein wird.

Haftungsausschluss: Alle Informationen werden in der vorliegenden Form nur zu Informationszwecken bereitgestellt und sind nicht für Handelszwecke oder als Ratschläge gedacht. Die Informationen garantieren keinen Erfolg. Wenn Sie sich dafür entscheiden, diese Materialien auf einem Ihrer Handelskonten zu verwenden, tun Sie dies auf eigenes Risiko und Sie sind allein verantwortlich.


Strukturelle Muster

In diesem Teil werden wir herausfinden, was strukturelle Entwurfsmuster und ihre Typen und Strukturen sind. Strukturmuster befassen sich mit der Art und Weise, wie Klassen und Objekte strukturiert werden, um Komponenten für den Aufbau größerer Strukturen zu sein. Diese Muster setzen Schnittstellen und Implementierungen unter Verwendung des Vererbungskonzepts zusammen. Dieses Vererbungskonzept bedeutet, dass wir eine Klasse haben, die die Eigenschaften ihrer Elternklassen hat oder kombiniert. Die Notwendigkeit dieses Musters wird noch deutlicher, wenn wir dafür sorgen müssen, dass entwickelte Klassen unabhängig voneinander zusammenarbeiten.

Es gibt viele Arten von Strukturmustern, wie die folgenden:

  • Adapter: Er hilft dabei, eine andere Schnittstelle zu erhalten, die Clients erwarten, indem er die Schnittstelle einer Klasse umwandelt.
  • Bridge: Die Abstraktion und ihre Implementierung können unabhängig voneinander variieren, indem sie entkoppelt werden.
  • Composite: Zur Darstellung von Teil-Ganzes-Hierarchien ist es hilfreich, Objekte in Baumstrukturen zusammenzusetzen. Darüber hinaus ermöglicht Composite eine einheitliche Behandlung von Einzeldingen und den Zusammenstellungen von Objekten durch die Clients.
  • Decorator: Er kann verwendet werden, um aktuellen Objekten auf dynamische Weise weitere Verantwortlichkeiten zuzuweisen, und er kann als flexible Alternative zur Unterklassifizierung von Anbietern verwendet werden, um die Funktionalität zu erweitern.
  • Facade: Sie kann verwendet werden, wenn wir eine einheitliche Schnittstelle zu einer Reihe von Schnittstellen in einem Teilsystem benötigen, und sie hilft, das Teilsystem einfach zu verwenden, indem sie eine Schnittstelle auf höherer Ebene definiert.
  • Flyweight: Es hilft bei der effektiven Unterstützung einer großen Anzahl von feinkörnigen Objekten durch gemeinsame Nutzung.
  • Proxy: Er kann verwendet werden, wenn der Zugriff auf ein Objekt kontrolliert werden muss, indem eine Alternative oder ein Platzhalter für das Objekt gefunden wird. 

Wir werden diese Muster durch den folgenden Ansatz oder durch die Beantwortung der folgenden Fragen abdecken:

  • Was bewirkt das Muster?
  • Was löst das Entwurfsmuster?
  • Wie können wir sie in MQL5 verwenden?


Adapter

In diesem Teil beginnen wir mit der Identifizierung der Typen von strukturellen Entwurfsmustern, indem wir den ersten Typ, den Adapter, identifizieren. Das Schlüsselwort zum Verständnis dieses Musters ist Anpassungsfähigkeit. Ganz einfach, wenn wir eine Schnittstelle haben, die unter bestimmten Umständen verwendet werden kann, und dann einige Aktualisierungen mit diesen Umständen passieren, ist es wichtig, Aktualisierungen in der Schnittstelle zu machen, die den Code anpassen und effektiv mit diesen neuen Umständen arbeiten lassen. Das ist es, was dieses Muster tun kann, weil es die Schnittstelle der Klasse, die wir haben, in eine andere umwandelt, die von den Clients genauso genutzt werden kann, wie sie es erwarten. Dieses Adapter-Muster ermöglicht es Klassen, in einigen Fällen von inkompatiblen Schnittstellen zusammenzuarbeiten. Dieses Muster ist auch als Wrapper bekannt, weil es scheint, einen Wrapper für die Schnittstelle zu geben, um sich anzupassen und als eine andere zu arbeiten.

Was bewirkt das Muster?

Wie bereits erwähnt, kann dieses Muster verwendet werden, wenn die entworfene Schnittstelle nicht mit den Anwendungsanforderungen der domänenspezifischen Schnittstelle übereinstimmt, um die Schnittstelle dieser Klasse in eine andere umzuwandeln und es den Klassen zu ermöglichen, zusammenzuarbeiten.

Die folgenden Diagramme stellen die Struktur des Adapterentwurfsmusters dar:

Adapter1

Adapter1

Wie wir in den vorherigen Diagrammen sehen können, gibt es aufgrund der Unterstützung der Mehrfachvererbung in der Programmiersprache den Klassenadapter und den Objektadapter. Wir haben das Ziel, das die domänenspezifische der neuen Schnittstelle identifiziert, die der Client verwendet, den Client, der mit Objekten teilnimmt, die sich an die Zielschnittstelle anpassen, der Adaptee identifiziert die bestehende Schnittstelle (die alte), die wir brauchen, um sie anpassungsfähig zu machen, und der Adapter, der die Schnittstelle „Adaptee“ an die Zielschnittstelle anpassbar macht.

Was löst das Entwurfsmuster?

  • Verwendung der vorhandenen Klasse mit einer Schnittstelle, die nicht mit der von uns benötigten Schnittstelle übereinstimmt.
  • Erstellung wiederverwendbarer Klassen, die mit nicht verwandten Klassen zusammenarbeiten können, unabhängig davon, ob diese Klassen kompatible oder inkompatible Schnittstellen haben.
  • Anpassung der Schnittstelle der übergeordneten Klasse, wenn viele bestehende Unterklassen verwendet werden müssen.

Wie können wir sie in MQL5 verwenden?

In diesem Teil werden wir lernen, wie wir dieses Muster (AdapterClass und ObjectClass) in der MQL5-Programmiersprache verwenden können, so wie folgt:

Mit Hilfe der Namespace-Funktion zur Deklaration des Bereichs (AdapterClass) werden wir unsere Funktionen, Variablen und Klassen innerhalb von:

namespace AdapterClass

Die Verwendung der Schnittstellenfunktion zur Deklaration (Target) ermöglicht die Festlegung spezifischer Funktionen, die später von der Klasse implementiert werden können, oder die Definition der domänenspezifischen Funktionen, die die Clients verwenden:

interface Target
  {
   void Request();
  };

Verwendung der Klassenfunktion, um Adaptee zu definieren, das die bestehende Schnittstelle definiert, die mit einem öffentlichen Mitglied (SpecificRequest()) anpassbar sein muss.

class Adaptee
  {
public:
   void              SpecificRequest();
  };

 Drucken einer Meldung, wenn die Anfrage vom Adaptee ausgeführt wird:

void Adaptee::SpecificRequest(void)
  {
   Print("A specific request is executing by the Adaptee");
  }

Deklaration der Adapterklasse, die die Schnittstelle des Annehmenden an die Schnittstellen des Ziels anpasst, die von Ziel und Angenommenem erben, als Mehrfachvererbung:

class Adapter;
class AdapterAsTarget:public Target
  {
public:
   Adapter*          asAdaptee;
   void              Request();
  };
void AdapterAsTarget::Request()
  {
   printf("The Adapter requested Operation");
   asAdaptee.SpecificRequest();
  }
class Adapter:public Adaptee
  {
public:
   AdapterAsTarget*  asTarget;
                     Adapter();
                    ~Adapter();
  };
void Adapter::Adapter(void)
  {
   asTarget=new AdapterAsTarget;
   asTarget.asAdaptee=&this;
  }
void Adapter::~Adapter(void)
  {
   delete asTarget;
  }

Deklaration der Klasse Client:

class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output()
  {
   return __FUNCTION__;
  }

Ausführen des Clients:

void Client::Run()
  {
   Adapter adapter;
   Target* target=adapter.asTarget;
   target.Request();
  }

Im Folgenden finden Sie den vollständigen Code der Adapterklasse in MQL5 in einem einzigen Codeblock:

namespace AdapterClass
{
interface Target
  {
   void Request();
  };
class Adaptee
  {
public:
   void              SpecificRequest();
  };
void Adaptee::SpecificRequest(void)
  {
   Print("A specific request is executing by the Adaptee");
  }
class Adapter;
class AdapterAsTarget:public Target
  {
public:
   Adapter*          asAdaptee;
   void              Request();
  };
void AdapterAsTarget::Request()
  {
   printf("The Adapter requested Operation");
   asAdaptee.SpecificRequest();
  }
class Adapter:public Adaptee
  {
public:
   AdapterAsTarget*  asTarget;
                     Adapter();
                    ~Adapter();
  };
void Adapter::Adapter(void)
  {
   asTarget=new AdapterAsTarget;
   asTarget.asAdaptee=&this;
  }
void Adapter::~Adapter(void)
  {
   delete asTarget;
  }
class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output()
  {
   return __FUNCTION__;
  }
void Client::Run()
  {
   Adapter adapter;
   Target* target=adapter.asTarget;
   target.Request();
  }
}

Im Folgenden wird beschrieben, wie wir den Objektadapter in MQL5 verwenden können:

Verwendung des Namensraumes zur Erstellung eines Deklarationsbereiches für Funktionen, Variablen und Klassen des AdapterObjects:

namespace AdapterObject

Verwendung der Schnittstelle, um das Ziel zu definieren, das von den Clients verwendet wird:

interface Target
  {
   void Request();
  };

Erstellen der Klasse Adaptee, um die vorhandene Schnittstelle zu definieren, die anpassbar sein muss:

class Adaptee
  {
public:
   void              SpecificRequest();
  };
void Adaptee::SpecificRequest(void)
  {
   Print("The specific Request");
  }
class Adapter:public Target
  {
public:
   void              Request();
protected:
   Adaptee           adaptee;
  };
void Adapter::Request(void)
  {
   Print("The request of Operation requested");
   adaptee.SpecificRequest();
  }

Deklaration des Clienten:

class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output()
  {
   return __FUNCTION__;
  }

Ausführen des Clients, wenn Clients Operationen auf der Instanz des Adapters aufrufen:

void Client::Run()
  {
   Target* target=new Adapter;
   target.Request();
   delete target;
  }

Im Folgenden finden Sie den vollständigen Code in einem Block:

namespace AdapterObject
{
interface Target
  {
   void Request();
  };
class Adaptee
  {
public:
   void              SpecificRequest();
  };
void Adaptee::SpecificRequest(void)
  {
   Print("The specific Request");
  }
class Adapter:public Target
  {
public:
   void              Request();
protected:
   Adaptee           adaptee;
  };
void Adapter::Request(void)
  {
   Print("The request of Operation requested");
   adaptee.SpecificRequest();
  }
class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output()
  {
   return __FUNCTION__;
  }
void Client::Run()
  {
   Target* target=new Adapter;
   target.Request();
   delete target;
  }
}


Bridge

In diesem Teil werden wir das Entwurfsmuster Bridge als eines der Strukturmuster identifizieren. Der Hauptgedanke bei der Verwendung dieses Musters besteht darin, die Abstraktion von ihren Implementierungen zu entkoppeln, um zukünftige Konflikte zu vermeiden, die bei Aktualisierungen oder Änderungen in einer der Implementierungen auftreten können. Es wird auch als Handle oder Body bezeichnet.

Was bewirkt das Muster?

Wie bereits erwähnt, kann das Bridge-Muster verwendet werden, wenn eine Abstraktion viele mögliche Implementierungen hat. Anstatt die übliche Methode der Vererbung zu verwenden, die die Implementierung immer mit der Abstraktion verknüpft, können wir dieses Muster verwenden, um die Abstraktion von ihren Implementierungen zu entkoppeln, um Probleme im Falle von Änderungen oder Aktualisierungen zu vermeiden. Dies kann sehr nützlich sein, um einen sauberen Code zu erstellen, der wiederverwendbar, erweiterbar und leicht zu testen ist.

Nachfolgend ist ein Diagramm für das Entwurfsmuster Bridge dargestellt:

Bridge

Wie aus dem vorstehenden Diagramm für die Struktur des Bridge-Musters hervorgeht, gibt es folgende Teilnehmer:

  • Abstraktion: Sie definiert eine Schnittstelle der Abstraktion und enthält einen Verweis auf ein Objekt vom Typ Implementierer.
  • RefinedAbstraction: Diese erweitert die Abstraktionsschnittstelle.
  • Implementator: der die Implementierungsklassen identifiziert Schnittstelle.
  • ConcreteImplementor: der die Schnittstelle des Implementators implementiert und die konkrete Implementierung dieser Schnittstelle angibt.

Was löst das Entwurfsmuster?

Dieses Bridge-Muster kann verwendet werden, wenn wir die folgenden Beispiele benötigen:

  • Vermeidung der ständigen Verknüpfung zwischen der Abstraktion und ihrer Implementierung, da dieses Muster dazu beiträgt, sie zu entkoppeln.
  • Kombination verschiedener Abstraktionen und Implementierungen und unabhängige Erweiterung jeder einzelnen ohne Konflikte.
  • Vermeidung von Auswirkungen auf die Clients bei Änderungen in der Implementierung der Abstraktion.
  • Die Implementierung der Abstraktion wird in C++ vollständig vor den Clients verborgen.

Wie können wir sie in MQL5 verwenden?

In diesem Teil werden wir herausfinden, wie man dieses Muster in der Programmiersprache MQL5 verwenden kann, um von seinen nützlichen Vorteilen zu profitieren und effektive Software zu erstellen. Im Folgenden wird beschrieben, wie wir die Struktur des Bridge-Musters in MQL5 codieren können:

Erstellung des Deklarationsbereichs zur Definition von Variablen, Funktionen und Klassen des Musters:

namespace Bridge

Erstellung der Implementor-Schnittstelle unter Verwendung des Schlüsselworts interface, um die Implementierung bestimmter Funktionen durch die Klasse zu ermöglichen:

interface Implementor
  {
   void OperationImp();
  };

Erstellung der Abstraktionsklasse mit öffentlichen und geschützten Mitgliedern als Teilnehmer und Beibehaltung eines Verweises auf das Objekt des Implementierers:

class Abstraction
  {
public:
   virtual void      Operation();
                     Abstraction(Implementor*);
                     Abstraction();
                    ~Abstraction();
protected:
   Implementor*      implementor;
  };
void Abstraction::Abstraction(void) {}
void Abstraction::Abstraction(Implementor*i):implementor(i) {}
void Abstraction::~Abstraction()
  {
   delete implementor;
  }
void Abstraction::Operation()
  {
   implementor.OperationImp();
  }

Erstellen der Klasse RefinedAbstraction als Teilnehmer:

class RefinedAbstraction:public Abstraction
  {
public:
                     RefinedAbstraction(Implementor*);
   void              Operation();
  };
void RefinedAbstraction::RefinedAbstraction(Implementor*i):Abstraction(i) {}
void RefinedAbstraction::Operation()
  {
   Abstraction::Operation();
  }

Erstellen von Klassen von ConcreteImplementorA und -B:

class ConcreteImplementorA:public Implementor
  {
public:
   void              OperationImp();
  };
void ConcreteImplementorA::OperationImp(void)
  {
   Print("The implementor A");
  }
class ConcreteImplementorB:public Implementor
  {
public:
   void              OperationImp();
  };
void ConcreteImplementorB::OperationImp(void)
  {
   Print("The implementor B");
  }

Erstellen der Klasse Client:

class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }

Ausführen des Clients:

void Client::Run(void)
  {
   Abstraction* abstraction;
   abstraction=new RefinedAbstraction(new ConcreteImplementorA);
   abstraction.Operation();
   delete abstraction;
   abstraction=new RefinedAbstraction(new ConcreteImplementorB);
   abstraction.Operation();
   delete abstraction;
  }

Der folgende Code ist also der vollständige Code der Bridge-Musterstruktur:

namespace Bridge
{
interface Implementor
  {
   void OperationImp();
  };
class Abstraction
  {
public:
   virtual void      Operation();
                     Abstraction(Implementor*);
                     Abstraction();
                    ~Abstraction();
protected:
   Implementor*      implementor;
  };
void Abstraction::Abstraction(void) {}
void Abstraction::Abstraction(Implementor*i):implementor(i) {}
void Abstraction::~Abstraction()
  {
   delete implementor;
  }
void Abstraction::Operation()
  {
   implementor.OperationImp();
  }
class RefinedAbstraction:public Abstraction
  {
public:
                     RefinedAbstraction(Implementor*);
   void              Operation();
  };
void RefinedAbstraction::RefinedAbstraction(Implementor*i):Abstraction(i) {}
void RefinedAbstraction::Operation()
  {
   Abstraction::Operation();
  }
class ConcreteImplementorA:public Implementor
  {
public:
   void              OperationImp();
  };
void ConcreteImplementorA::OperationImp(void)
  {
   Print("The implementor A");
  }
class ConcreteImplementorB:public Implementor
  {
public:
   void              OperationImp();
  };
void ConcreteImplementorB::OperationImp(void)
  {
   Print("The implementor B");
  }
class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   Abstraction* abstraction;
   abstraction=new RefinedAbstraction(new ConcreteImplementorA);
   abstraction.Operation();
   delete abstraction;
   abstraction=new RefinedAbstraction(new ConcreteImplementorB);
   abstraction.Operation();
   delete abstraction;
  }
}


Composite

In diesem Teil werden wir ein weiteres strukturelles Muster identifizieren, nämlich das Composite-Muster. Dieses Muster hilft dabei, Objekte in einem Baum als Struktur zusammenzusetzen, und es ermöglicht eine einheitliche Behandlung von Clients für einzelne Objekte und Kompositionen.

Was bewirkt das Muster?

Wie wir bereits erwähnt haben, hängt dieses Composite-Muster davon ab, dass wir Objekte in Baumstrukturen zusammensetzen müssen, mit dem Baum als Hauptschlüssel in diesem Muster. Wenn wir also eine Komponente haben, können wir anhand der Baumstruktur feststellen, dass es zwei Dinge unter dieser Komponente gibt: Leaf (Blatt), das nur Operationen hat, und Composite, das weitere Operationen wie Hinzufügen, Entfernen und Aufrufen von Child hat.

Das folgende Diagramm zeigt, wie das Entwurfsmuster Composite aussieht:

Composite

Wie aus dem vorherigen Diagramm hervorgeht, gibt es folgende Teilnehmer:

  • Component: Es wird die Schnittstelle der Objekte deklariert und das Standardverhalten der Schnittstelle für Klassen implementiert, um auf die dafür deklarierten Komponenten der Schnittstelle zuzugreifen und sie zu verwalten.
  • Leaf: Es stellt Objekte des Leaf in einer Komposition dar und dieses Leaf hat keine Kinder (childs), identifiziert das Verhalten von Objekten, die als primitiv in der Komposition betrachtet werden können.
  • Composite: Es identifiziert das Verhalten von Komponenten mit Kinder, speichert diese Kinder von Komponenten und implementiert Operationen der Kinder in der Schnittstelle von Komponenten.
  • Client: Über die Komponentenschnittstelle manipuliert der Client Objekte.

Was löst das Entwurfsmuster?

Dieses Composite-Muster kann bei Bedarf verwendet werden:

  • Teil-Ganzes-Hierarchien der Objektdarstellung.
  • Alle Objekte im Verbund werden vom Client einheitlich behandelt.

Wie können wir sie in MQL5 verwenden?

In diesem Teil werden wir zeigen, wie wir das Composite-Muster in MQL5 kodieren können, und zwar so wie im Folgenden beschrieben:

Erstellen wir den Composite-Raum oder -Bereich, um alle Funktionen, Variablen und Klassen mit dem Schlüsselwort namespace zu deklarieren:

namespace Composite

Erstellen der Klasse Component mit öffentlichen und geschützten Mitgliedern und Zugriff auf die übergeordnete Komponente:

class Component
  {
public:
   virtual void      Operation(void)=0;
   virtual void      Add(Component*)=0;
   virtual void      Remove(Component*)=0;
   virtual Component*   GetChild(int)=0;
                     Component(void);
                     Component(string);
protected:
   string            name;
  };
Component::Component(void) {}
Component::Component(string a_name):name(a_name) {}

Definieren eines Nutzerfehlers beim Hinzufügen und Entfernen von Blättern und Erstellen der Klasse Leaf:

#define ERR_INVALID_OPERATION_EXCEPTION   1
class Leaf:public Component
  {
public:
   void              Operation(void);
   void              Add(Component*);
   void              Remove(Component*);
   Component*        GetChild(int);
                     Leaf(string);
  };
void Leaf::Leaf(string a_name):Component(a_name) {}
void Leaf::Operation(void)
  {
   Print(name);
  }
void Leaf::Add(Component*)
  {
   SetUserError(ERR_INVALID_OPERATION_EXCEPTION);
  }
void Leaf::Remove(Component*)
  {
   SetUserError(ERR_INVALID_OPERATION_EXCEPTION);
  }
Component* Leaf::GetChild(int)
  {
   SetUserError(ERR_INVALID_OPERATION_EXCEPTION);
   return NULL;
  }

Erstellen der Composite-Klasse als Teilnehmer, dann Operation, Hinzufügen und Entfernen von Komponenten und GetChild(int):

class Composite:public Component
  {
public:
   void              Operation(void);
   void              Add(Component*);
   void              Remove(Component*);
   Component*        GetChild(int);
                     Composite(string);
                    ~Composite(void);
protected:
   Component*        nodes[];
  };
Composite::Composite(string a_name):Component(a_name) {}
Composite::~Composite(void)
  {
   int total=ArraySize(nodes);
   for(int i=0; i<total; i++)
     {
      Component* i_node=nodes[i];
      if(CheckPointer(i_node)==1)
        {
         delete i_node;
        }
     }
  }
void Composite::Operation(void)
  {
   Print(name);
   int total=ArraySize(nodes);
   for(int i=0; i<total; i++)
     {
      nodes[i].Operation();
     }
  }
void Composite::Add(Component *src)
  {
   int size=ArraySize(nodes);
   ArrayResize(nodes,size+1);
   nodes[size]=src;
  }
void Composite::Remove(Component *src)
  {
   int find=-1;
   int total=ArraySize(nodes);
   for(int i=0; i<total; i++)
     {
      if(nodes[i]==src)
        {
         find=i;
         break;
        }
     }
   if(find>-1)
     {
      ArrayRemove(nodes,find,1);
     }
  }
Component* Composite::GetChild(int i)
  {
   return nodes[i];
  }

Erstellen der Klasse Client als Teilnehmer:

class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void) {return __FUNCTION__;}

Ausführen des Clients:

void Client::Run(void)
  {
   Component* root=new Composite("root");
   Component* branch1=new Composite("The branch 1");
   Component* branch2=new Composite("The branch 2");
   Component* leaf1=new Leaf("The leaf 1");
   Component* leaf2=new Leaf("The leaf 2");
   root.Add(branch1);
   root.Add(branch2);
   branch1.Add(leaf1);
   branch1.Add(leaf2);
   branch2.Add(leaf2);
   branch2.Add(new Leaf("The leaf 3"));
   Print("The tree");
   root.Operation();
   root.Remove(branch1); 
   Print("Removing one branch");
   root.Operation();
   delete root;
   delete branch1;
  }


Decorator

Das Decorator-Muster ist ein weiteres strukturelles Entwurfsmuster, das zur Bildung größerer Strukturen für erstellte oder vorhandene Objekte verwendet werden kann. Dieses Muster kann verwendet werden, um dem Objekt in einer dynamischen Methode zur Laufzeit zusätzliche Funktionen, Verhaltensweisen oder Zuständigkeiten hinzuzufügen, da es eine flexible Alternative zur Unterklassifizierung bietet. Es ist auch als Wrapper bekannt.

Was bewirkt das Muster?

Wie wir bereits gesagt haben, hilft uns dieses Muster, Verantwortlichkeiten zu jedem einzelnen Objekt hinzuzufügen, ohne dies in der gesamten Klasse als Wrapper zu tun, anstatt den Weg der Unterklassifizierung zu nutzen.

Das folgende Diagramm zeigt die Struktur des Decorator-Entwurfsmusters:

Decorator

Wie aus dem vorstehenden Schaubild hervorgeht, haben wir folgende Teilnehmer:

  • Component: Das identifiziert die Schnittstelle der Objekte und zeigt an, dass sie auf dynamische Weise zusätzliche Rollen haben.
  • ConcreteComponent: identifiziert das Objekt, dem zusätzliche Verantwortlichkeiten zugewiesen werden können.
  • Decorator: Er hilft dabei, einen Verweis auf das Objekt der Komponente zu erhalten und die Schnittstelle zu identifizieren, die zur Schnittstelle der Komponente passt
  • ConcreteDecorator: Er ist für das Hinzufügen von Zuständigkeiten zu der Komponente verantwortlich.

Was löst das Entwurfsmuster?

Dieses Decorator-Entwurfsmuster kann verwendet werden, wenn wir es brauchen:

  • Dynamisches und transparentes Hinzufügen zusätzlicher Zuständigkeiten zu einzelnen Objekten, ohne dass dies Auswirkungen auf andere Objekte hat.
  • Entzug der Verantwortung von Objekten.
  • Die Suche nach der Unterklassifizierungsmethode ist im Falle einer Erweiterung unpraktisch.

Wie können wir sie in MQL5 verwenden?

Wenn wir dieses Decorator-Muster in MQL5 kodieren müssen, um es in der erstellten Software zu verwenden, sind die folgenden Schritte dafür notwendig:

Erstellen des Bereichs der Deklaration unseres Decorators, um alles zu deklarieren:

namespace Decorator

Erstellen der Klasse Component mit einem öffentlichen Mitglied, um die Schnittstelle der Objekte zu definieren:

class Component
  {
public:
   virtual void      Operation(void)=0;
  };

Erstellen der Decorator-Klasse als Teilnehmer:

class Decorator:public Component
  {
public:
   Component*        component;
   void              Operation(void);
  };
void Decorator::Operation(void)
  {
   if(CheckPointer(component)>0)
     {
      component.Operation();
     }
  }

Erstellen der Klasse ConcreteComponent als Teilnehmer:

class ConcreteComponent:public Component
  {
public:
   void              Operation(void);
  };
void ConcreteComponent::Operation(void)
  {
   Print("The concrete operation");
  }

Erstellen von ConcreteDecoratorA und -B:

class ConcreteDecoratorA:public Decorator
  {
protected:
   string            added_state;
public:
                     ConcreteDecoratorA(void);
   void              Operation(void);
  };
ConcreteDecoratorA::ConcreteDecoratorA(void):
   added_state("The added state()")
  {
  }
void ConcreteDecoratorA::Operation(void)
  {
   Decorator::Operation();
   Print(added_state);
  }
class ConcreteDecoratorB:public Decorator
  {
public:
   void              AddedBehavior(void);
   void              Operation(void);
  };
void ConcreteDecoratorB::AddedBehavior(void)
  {
   Print("The added behavior()");
  }
void ConcreteDecoratorB::Operation(void)
  {
   Decorator::Operation();
   AddedBehavior();
  }

Erstellen der Klasse Client:

class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }

Ausführen des Clients:

void Client::Run(void)
  {
   Component* component=new ConcreteComponent();
   Decorator* decorator_a=new ConcreteDecoratorA();
   Decorator* decorator_b=new ConcreteDecoratorB();
   decorator_a.component=component;
   decorator_b.component=decorator_a;
   decorator_b.Operation();
   delete component;
   delete decorator_a;
   delete decorator_b;
  }

Wenn wir also den gesamten Code in einem Codeblock sehen wollen, können wir das wie folgt sehen:

namespace Decorator
{
class Component
  {
public:
   virtual void      Operation(void)=0;
  };
class Decorator:public Component
  {
public:
   Component*        component;
   void              Operation(void);
  };
void Decorator::Operation(void)
  {
   if(CheckPointer(component)>0)
     {
      component.Operation();
     }
  }
class ConcreteComponent:public Component
  {
public:
   void              Operation(void);
  };
void ConcreteComponent::Operation(void)
  {
   Print("The concrete operation");
  }
class ConcreteDecoratorA:public Decorator
  {
protected:
   string            added_state;
public:
                     ConcreteDecoratorA(void);
   void              Operation(void);
  };
ConcreteDecoratorA::ConcreteDecoratorA(void):
   added_state("The added state()")
  {
  }
void ConcreteDecoratorA::Operation(void)
  {
   Decorator::Operation();
   Print(added_state);
  }
class ConcreteDecoratorB:public Decorator
  {
public:
   void              AddedBehavior(void);
   void              Operation(void);
  };
void ConcreteDecoratorB::AddedBehavior(void)
  {
   Print("The added behavior()");
  }
void ConcreteDecoratorB::Operation(void)
  {
   Decorator::Operation();
   AddedBehavior();
  }
class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   Component* component=new ConcreteComponent();
   Decorator* decorator_a=new ConcreteDecoratorA();
   Decorator* decorator_b=new ConcreteDecoratorB();
   decorator_a.component=component;
   decorator_b.component=decorator_a;
   decorator_b.Operation();
   delete component;
   delete decorator_a;
   delete decorator_b;
  }
}


Facade

Facade (Fassade) ist ein weiteres Strukturmuster, das in der Softwareentwicklung verwendet werden kann, um andere größere Strukturen zu schaffen. Sie kennzeichnet eine Schnittstelle mit einer höheren Ebene, um die Nutzung von Teilsystemen reibungsloser und einfacher zu gestalten.

Was bewirkt das Muster?

Wie bereits erwähnt, ist Facade eine Möglichkeit, den Client von der Komplexität des Subsystems zu entkoppeln, da sie eine einheitliche Schnittstelle für eine Reihe von Subsystemschnittstellen bietet. Der Client interagiert also mit dieser einheitlichen Schnittstelle, um das Gewünschte zu erhalten, aber diese Schnittstelle interagiert mit dem Teilsystem, um das Gewünschte zurückzugeben.

Wenn wir die Struktur des Facade Entwurfsmuster sehen wollen, können wir sie wie in der folgenden Grafik sehen:

Facade

Wie in der vorangegangenen Grafik zu sehen ist, gibt es die folgenden Teilnehmer an diesem Muster:

  • Facade: Sie weiß, welches Subsystem Anfragen stellen kann, und delegiert Anfragen des Clients an die geeigneten Objekte des Subsystems.
  • Subsystem-Klassen: Sie führen Funktionen des Subsystems aus. Wenn sie eine Anfrage von Facade erhalten, bearbeiten sie diese, sie haben keine Verweise auf Facade.

Was löst das Entwurfsmuster?

Dieses Muster Facade kann verwendet werden, wenn wir es brauchen:

  • Zur Vereinfachung der Komplexität des Teilsystems durch Bereitstellung einer einfachen Schnittstelle.
  • Zur Entkopplung des Subsystems von Clients und anderen Subsystemen, um die bestehenden Abhängigkeiten zwischen Clients und Implementierungen von Klassen der Abstraktion in die Unabhängigkeit und Portabilität des Subsystems zu ändern.
  • Zur Festlegung von Einstiegspunkten zu jeder Teilsystemebene durch deren Überlagerung.

Wie können wir sie in MQL5 verwenden?

In diesem Teil werden wir den Code für die Verwendung des Musters Facade in MQL5 bereitstellen und die folgenden Schritte ausführen:

Schaffung des Raums der Fassade, um zu deklarieren, was wir brauchen, indem wir den Namespace verwenden:

namespace Facade

Deklaration der Klassen SubSystemA, SubSystemB und SubSystemC:

class SubSystemA
  {
public:
   void              Operation(void);
  };
void SubSystemA::Operation(void)
  {
   Print("The operation of the subsystem A");
  }
class SubSystemB
  {
public:
   void              Operation(void);
  };
void SubSystemB::Operation(void)
  {
   Print("The operation of the subsystem B");
  }
class SubSystemC
  {
public:
   void              Operation(void);
  };
void SubSystemC::Operation(void)
  {
   Print("The operation of the subsystem C");
  }

Deklaration der Klasse Facade:

class Facade
  {
public:
   void              Operation_A_B(void);
   void              Operation_B_C(void);
protected:
   SubSystemA        subsystem_a;
   SubSystemB        subsystem_b;
   SubSystemC        subsystem_c;
  };
void Facade::Operation_A_B(void)
  {
   Print("The facade of the operation of A & B");
   Print("The request of the facade of the subsystem A operation");
   subsystem_a.Operation();
   Print("The request of the facade of the subsystem B operation");
   subsystem_b.Operation();
  }
void Facade::Operation_B_C(void)
  {
   Print("The facade of the operation of B & C");
   Print("The request of the facade of the subsystem B operation");
   subsystem_b.Operation();
   Print("The request of the facade of the subsystem C operation");
   subsystem_c.Operation();
  }

Deklaration des Clienten:

class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }

Ausführen des Clients:

void Client::Run(void)
  {
   Facade facade;
   Print("The request of client of the facade operation A & B");
   facade.Operation_A_B();
   Print("The request of client of the facade operation B & C");
   facade.Operation_B_C();
  }

Im Folgenden wird der gesamte Code in einem Block zusammengefasst:

namespace Facade
{
class SubSystemA
  {
public:
   void              Operation(void);
  };
void SubSystemA::Operation(void)
  {
   Print("The operation of the subsystem A");
  }
class SubSystemB
  {
public:
   void              Operation(void);
  };
void SubSystemB::Operation(void)
  {
   Print("The operation of the subsystem B");
  }
class SubSystemC
  {
public:
   void              Operation(void);
  };
void SubSystemC::Operation(void)
  {
   Print("The operation of the subsystem C");
  }
class Facade
  {
public:
   void              Operation_A_B(void);
   void              Operation_B_C(void);
protected:
   SubSystemA        subsystem_a;
   SubSystemB        subsystem_b;
   SubSystemC        subsystem_c;
  };
void Facade::Operation_A_B(void)
  {
   Print("The facade of the operation of A & B");
   Print("The request of the facade of the subsystem A operation");
   subsystem_a.Operation();
   Print("The request of the facade of the subsystem B operation");
   subsystem_b.Operation();
  }
void Facade::Operation_B_C(void)
  {
   Print("The facade of the operation of B & C");
   Print("The request of the facade of the subsystem B operation");
   subsystem_b.Operation();
   Print("The request of the facade of the subsystem C operation");
   subsystem_c.Operation();
  }
class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   Facade facade;
   Print("The request of client of the facade operation A & B");
   facade.Operation_A_B();
   Print("The request of client of the facade operation B & C");
   facade.Operation_B_C();
  }
}


Flyweight

Das Strukturmuster Flyweight ist ein weiteres Muster, das nützlich sein kann, wenn es eine große Anzahl von feinkörnigen Objekten gibt, da es in diesem Fall die gemeinsame Nutzung verwendet, um dies zu unterstützen.

Was bewirkt das Muster?

Wie bereits erwähnt, kann dieses Muster durch die Verwendung von Sharing als Unterstützung auch in Bezug auf den Speicherplatz hilfreich sein, weshalb es auch Flyweight genannt wird.

Nachfolgend finden Sie eine grafische Darstellung der Struktur des Flyweights-Entwurfsmuster:

Flyweight1

Flyweight2

Wie aus dem vorherigen Diagramm hervorgeht, gibt es folgende Teilnehmer:

  • Flyweight.
  • ConcreteFlyweight.
  • UnsharedConcreteFlyweight.
  • FlyweightFactory.
  • Client.

Was löst das Entwurfsmuster?

Dieses Muster kann verwendet werden, wenn:

  • Eine große Anzahl von Objekten wird in einer Anwendung verwendet.
  • Wir den großen Speicheraufwand senken müssen.
  • Wenn der größte Teil des Objektzustands extrinsisch gemacht werden kann.
  • Wenn wir den extrinsischen Zustand entfernen, können viele Gruppen von Objekten relativ durch weniger gemeinsame Objekte ersetzt werden.
  • Die Identität des Objekts ist für die Anwendung im Hinblick auf die Abhängigkeit nicht so wichtig.

Wie können wir sie in MQL5 verwenden?

Wenn Sie dieses Muster in MQL5 kodieren möchten, werden wir den Code wie folgt erstellen:

Wir erstellen unseren Flyweight-Raum, indem wir das Namespace-Schlüsselwort verwenden, um alles zu deklarieren:

namespace Flyweight

Verwendung des Schlüsselworts interface zur Deklaration des Flyweight:

interface Flyweight;

Erstellen der Klasse Pair mit geschützten und öffentlichen Mitgliedern als Teilnehmer:

class Pair
  {
protected:
   string            key;
   Flyweight*        value;
public:
                     Pair(void);
                     Pair(string,Flyweight*);
                    ~Pair(void);
   Flyweight*        Value(void);
   string            Key(void);
  };
Pair::Pair(void){}
Pair::Pair(string a_key,Flyweight *a_value):
   key(a_key),
   value(a_value){}
Pair::~Pair(void)
  {
   delete value;
  }
string Pair::Key(void)
  {
   return key;
  }
Flyweight* Pair::Value(void)
  {
   return value;
  }

Erstellen der Klasse Reference und Definieren ihres Konstruktors und Dekonstruktors:

class Reference
  {
protected:
   Pair*             pairs[];
public:
                     Reference(void);
                    ~Reference(void);
   void              Add(string,Flyweight*);
   bool              Has(string);
   Flyweight*        operator[](string);
protected:
   int               Find(string);
  };
Reference::Reference(void){}
Reference::~Reference(void)
  {
   int total=ArraySize(pairs);
   for(int i=0; i<total; i++)
     {
      Pair* ipair=pairs[i];
      if(CheckPointer(ipair))
        {
         delete ipair;
        }
     }
  }
int Reference::Find(string key)
  {
   int total=ArraySize(pairs);
   for(int i=0; i<total; i++)
     {
      Pair* ipair=pairs[i];
      if(ipair.Key()==key)
        {
         return i;
        }
     }
   return -1;
  }
bool Reference::Has(string key)
  {
   return (Find(key)>-1)?true:false;
  }
void Reference::Add(string key,Flyweight *value)
  {
   int size=ArraySize(pairs);
   ArrayResize(pairs,size+1);
   pairs[size]=new Pair(key,value);
  }
Flyweight* Reference::operator[](string key)
  {
   int find=Find(key);
   return (find>-1)?pairs[find].Value():NULL;
  }

Deklaration der Flyweight-Schnittstelle, um auf den extrinsischen Zustand einzuwirken:

interface Flyweight
  {
   void Operation(int extrinsic_state);
  };

Deklaration der Klasse ConcreteFlyweight:

class ConcreteFlyweight:public Flyweight
  {
public:
   void              Operation(int extrinsic_state);
protected:
   int               intrinsic_state;
  };
void ConcreteFlyweight::Operation(int extrinsic_state)
  {
   intrinsic_state=extrinsic_state;
   printf("The intrinsic state - %d",intrinsic_state);
  }

Deklaration der Klasse UnsharedConcreteFlyweight:

class UnsharedConcreteFlyweight:public Flyweight
  {
protected:
   int               all_state;
public:
   void              Operation(int extrinsic_state);
  };
void UnsharedConcreteFlyweight::Operation(int extrinsic_state)
  {
   all_state=extrinsic_state;
   Print("all state - %d",all_state);
  }

Deklaration der Klasse FlyweightFactory:

class FlyweightFactory
  {
protected:
   Reference        pool;
public:
                     FlyweightFactory(void);
   Flyweight*        Flyweight(string key);
  };
FlyweightFactory::FlyweightFactory(void)
  {
   pool.Add("1",new ConcreteFlyweight);
   pool.Add("2",new ConcreteFlyweight);
   pool.Add("3",new ConcreteFlyweight);
  }
Flyweight* FlyweightFactory::Flyweight(string key)
  {
   if(!pool.Has(key))
     {
      pool.Add(key,new ConcreteFlyweight());
     }
   return pool[key];
  }

Deklaration der Klasse Client:

class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }

Ausführen des Clients:

void Client::Run(void)
  {
   int extrinsic_state=7;
   Flyweight* flyweight;
   FlyweightFactory factory;
   flyweight=factory.Flyweight("1");
   flyweight.Operation(extrinsic_state);
   flyweight=factory.Flyweight("10");
   flyweight.Operation(extrinsic_state);
   flyweight=new UnsharedConcreteFlyweight();
   flyweight.Operation(extrinsic_state);
   delete flyweight;
  }

Im Folgenden finden Sie den vollständigen Code in einem Block, um das Flyweight-Muster in MQL5 zu codieren:

namespace Flyweight
{
interface Flyweight;
class Pair
  {
protected:
   string            key;
   Flyweight*        value;
public:
                     Pair(void);
                     Pair(string,Flyweight*);
                    ~Pair(void);
   Flyweight*        Value(void);
   string            Key(void);
  };
Pair::Pair(void){}
Pair::Pair(string a_key,Flyweight *a_value):
   key(a_key),
   value(a_value){}
Pair::~Pair(void)
  {
   delete value;
  }
string Pair::Key(void)
  {
   return key;
  }
Flyweight* Pair::Value(void)
  {
   return value;
  }
class Reference
  {
protected:
   Pair*             pairs[];
public:
                     Reference(void);
                    ~Reference(void);
   void              Add(string,Flyweight*);
   bool              Has(string);
   Flyweight*        operator[](string);
protected:
   int               Find(string);
  };
Reference::Reference(void){}
Reference::~Reference(void)
  {
   int total=ArraySize(pairs);
   for(int i=0; i<total; i++)
     {
      Pair* ipair=pairs[i];
      if(CheckPointer(ipair))
        {
         delete ipair;
        }
     }
  }
int Reference::Find(string key)
  {
   int total=ArraySize(pairs);
   for(int i=0; i<total; i++)
     {
      Pair* ipair=pairs[i];
      if(ipair.Key()==key)
        {
         return i;
        }
     }
   return -1;
  }
bool Reference::Has(string key)
  {
   return (Find(key)>-1)?true:false;
  }
void Reference::Add(string key,Flyweight *value)
  {
   int size=ArraySize(pairs);
   ArrayResize(pairs,size+1);
   pairs[size]=new Pair(key,value);
  }
Flyweight* Reference::operator[](string key)
  {
   int find=Find(key);
   return (find>-1)?pairs[find].Value():NULL;
  }
interface Flyweight
  {
   void Operation(int extrinsic_state);
  };
class ConcreteFlyweight:public Flyweight
  {
public:
   void              Operation(int extrinsic_state);
protected:
   int               intrinsic_state;
  };
void ConcreteFlyweight::Operation(int extrinsic_state)
  {
   intrinsic_state=extrinsic_state;
   Print("The intrinsic state - %d",intrinsic_state);
  }
class UnsharedConcreteFlyweight:public Flyweight
  {
protected:
   int               all_state;
public:
   void              Operation(int extrinsic_state);
  };
void UnsharedConcreteFlyweight::Operation(int extrinsic_state)
  {
   all_state=extrinsic_state;
   Print("all state - %d",all_state);
  }
class FlyweightFactory
  {
protected:
   Reference        pool;
public:
                     FlyweightFactory(void);
   Flyweight*        Flyweight(string key);
  };
FlyweightFactory::FlyweightFactory(void)
  {
   pool.Add("1",new ConcreteFlyweight);
   pool.Add("2",new ConcreteFlyweight);
   pool.Add("3",new ConcreteFlyweight);
  }
Flyweight* FlyweightFactory::Flyweight(string key)
  {
   if(!pool.Has(key))
     {
      pool.Add(key,new ConcreteFlyweight());
     }
   return pool[key];
  }
class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   int extrinsic_state=7;
   Flyweight* flyweight;
   FlyweightFactory factory;
   flyweight=factory.Flyweight("1");
   flyweight.Operation(extrinsic_state);
   flyweight=factory.Flyweight("10");
   flyweight.Operation(extrinsic_state);
   flyweight=new UnsharedConcreteFlyweight();
   flyweight.Operation(extrinsic_state);
   delete flyweight;
  }
}


Proxy

Nun werden wir das letzte strukturelle Entwurfsmuster, den Proxy, identifizieren. Dieses Muster hat viele Arten in Bezug auf die Vertreter, aber im Allgemeinen können wir sagen, dass der Proxy verwendet werden kann, um eine Alternative oder Platzhalter für ein anderes Objekt, um die Kontrolle in Bezug auf den Zugang zu diesem Objekt zu vervollständigen präsentieren. Sie wird auch als Surrogat bezeichnet.

Was bewirkt das Muster?

Dieses Muster bietet, wie bereits erwähnt, ein Surrogat zur Kontrolle des Zugriffs auf ein Objekt.

Das folgende Diagramm zeigt die Struktur des Proxy-Entwurfsmusters:

Proxy

Wie aus dem vorherigen Diagramm hervorgeht, gibt es folgende Teilnehmer:

  • Proxy.
  • Subject.
  • Real subject.

Was löst das Entwurfsmuster?

Die folgenden Situationen sind üblich, in denen wir den Proxy verwenden können:
  • Wenn wir einen lokalen Vertreter für ein Objekt in einem anderen Adressraum benötigen, können wir den entfernten Proxy verwenden, der dies ermöglicht.
  • Wenn wir ein umfangreiches Objekt auf Abruf benötigen, können wir den virtuellen Proxy verwenden, der diese Objekte erstellt.
  • Wenn wir den Zugriff auf das primäre oder ursprüngliche Objekt kontrollieren müssen, können wir den Schutzproxy verwenden, der dies tun kann.
  • wenn wir einen Ersatz für einen bloßen Zeiger benötigen, können wir die intelligente Referenz verwenden.

Wie können wir sie in MQL5 verwenden?

Wenn wir das Proxy-Muster in MQL5 kodieren müssen, um eine effektive Software zu erstellen, können wir dies durch die folgenden Schritte erreichen:

Deklaration des Raums von Proxy, um alles zu deklarieren, was wir in Bezug auf Variablen, Funktionen, Klassen, ... usw. brauchen:

namespace Proxy

Deklaration der Klasse Subject als Teilnehmer:

class Subject
  {
public:
   virtual void      Request(void)=0;
  };

Erstellen der Klasse RealSubject:

class RealSubject:public Subject
  {
public:
   void              Request(void);
  };
void RealSubject::Request(void)
  {
   Print("The real subject");
  }

Anlegen der Proxy-Klasse als Teilnehmer:

class Proxy:public Subject
  {
protected:
   RealSubject*      real_subject;
public:
                    ~Proxy(void);
   void              Request(void);
  };
Proxy::~Proxy(void)
  {
   delete real_subject;
  }
void Proxy::Request(void)
  {
   if(!CheckPointer(real_subject))
     {
      real_subject=new RealSubject;
     }
   real_subject.Request();
  }

Deklaration der Klasse Client:

class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }

Ausführen des Clients:

void Client::Run(void)
  {
   Subject* subject=new Proxy;
   subject.Request();
   delete subject;
  }

Im Folgenden ist der vollständige Code in einem Block dargestellt:

namespace Proxy
{
class Subject
  {
public:
   virtual void      Request(void)=0;
  };
class RealSubject:public Subject
  {
public:
   void              Request(void);
  };
void RealSubject::Request(void)
  {
   Print("The real subject");
  }
class Proxy:public Subject
  {
protected:
   RealSubject*      real_subject;
public:
                    ~Proxy(void);
   void              Request(void);
  };
Proxy::~Proxy(void)
  {
   delete real_subject;
  }
void Proxy::Request(void)
  {
   if(!CheckPointer(real_subject))
     {
      real_subject=new RealSubject;
     }
   real_subject.Request();
  }
class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   Subject* subject=new Proxy;
   subject.Request();
   delete subject;
  }
}


Schlussfolgerung

Am Ende dieses Artikels haben wir eine einfache Einführung und Informationen über das Thema der strukturellen Entwurfsmuster gegeben. In diesem Artikel haben wir jede Art von strukturellem Muster identifiziert, um zu verstehen, wie wir sauberen Code schreiben können, der wiederverwendbar, erweiterbar und leicht zu testen ist, indem wir jede Art dieser Muster tief verstehen, indem wir identifizieren, was das Muster ist, was das Muster tut, wie es strukturiert ist und was das Muster als Designprobleme löst.

Daher haben wir die folgenden strukturellen Entwurfsmuster ermittelt:

  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Facade
  • Flyweight
  • Proxy

Wie wir bereits im ersten Teil gesagt haben, ist das Entwurfsmuster sehr wichtig, um es als Softwareentwickler zu lernen, weil es eine Menge Zeit spart und es Ihnen ermöglicht, das Rad nicht neu zu erfinden, indem Sie vorgegebene, getestete und praktische Lösungen für bestimmte Fragen und Probleme verwenden, und Ihr Wissen über die objektorientierte Programmierung ist sehr hilfreich, um das Thema Entwurfsmuster zu verstehen.

Ich empfehle, mehr über dieses wichtige Thema zu lesen und empfehle die folgenden Quellen, um mehr zu erfahren:

  • Design Patterns - Elements of Reusable Object-Oriented Software by Eric Gamma, Richard Helm, Ralph Johnson, and John Vlissides
  • Design Patterns for Dummies von Steve Holzner
  • Head First Design Patterns von Eric Freeman, Elisabeth Robson, Bert Bates, and Kathy Sierra

Ich hoffe, dass Sie diesen Artikel als nützlich empfunden haben, dass er Ihr Wissen und Ihr Bewusstsein im Bereich der Softwareentwicklung erweitert hat und dass Sie davon profitieren, indem Sie mit der Programmiersprache MQL5 effektivere Software entwickeln. Wenn Sie diesen Artikel hilfreich und bekam Wert von ihm und Sie müssen mehr Artikel für mich zu lesen, können Sie meine Veröffentlichung Abschnitt navigieren und Sie werden viele Artikel über MQL5 Programmiersprache zu finden und auch Sie werden viele Artikel darüber, wie man Handelssysteme auf der Grundlage der beliebtesten technischen Indikatoren wie RSI, MACD, Bollinger Bands, gleitende Durchschnitte, Stochastik, und andere zu schaffen finden. Ich hoffe, dass Sie von ihnen profitieren und Ihr Wissen und Ihre Ergebnisse in der Softwareentwicklung und im Handel verbessern können.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/13724

Beigefügte Dateien |
Adapter_Class.mqh (1.07 KB)
Adapter_Object.mqh (0.72 KB)
Bridge.mqh (3.38 KB)
Composite.mqh (3.09 KB)
Decorator.mqh (3.53 KB)
Facade.mqh (3.43 KB)
Flyweight.mqh (3.47 KB)
Proxy.mqh (1.79 KB)
Kombinatorisch symmetrische Kreuzvalidierung in MQL5 Kombinatorisch symmetrische Kreuzvalidierung in MQL5
In diesem Artikel stellen wir die Implementierung der kombinatorisch symmetrischen Kreuzvalidierung in reinem MQL5 vor, um den Grad der Überanpassung nach der Optimierung einer Strategie unter Verwendung des langsamen vollständigen Algorithmus des Strategietesters zu messen.
Einführung in MQL5 (Teil 1): Ein Leitfaden für Einsteiger in den algorithmischen Handel Einführung in MQL5 (Teil 1): Ein Leitfaden für Einsteiger in den algorithmischen Handel
Tauchen Sie ein in die faszinierende Welt des algorithmischen Handels mit unserem einsteigerfreundlichen Leitfaden zur MQL5-Programmierung. Entdecken Sie die Grundlagen von MQL5, der Sprache, die den MetaTrader 5 antreibt, während wir die Welt des automatisierten Handels entmystifizieren. Vom Verständnis der Grundlagen bis hin zu den ersten Schritten in der Programmierung ist dieser Artikel Ihr Schlüssel, um das Potenzial des algorithmischen Handels auch ohne Programmierkenntnisse zu erschließen. Begleiten Sie uns auf eine Reise, auf der Einfachheit und Raffinesse im aufregenden Universum von MQL5 aufeinandertreffen.
Modifizierter Grid-Hedge EA in MQL5 (Teil I): Erstellung eines einfachen Hedge EA Modifizierter Grid-Hedge EA in MQL5 (Teil I): Erstellung eines einfachen Hedge EA
Wir werden einen einfachen Hedge EA als Basis für unseren fortgeschritteneren Grid-Hedge EA erstellen, der eine Mischung aus klassischen Grid- und klassischen Hedge-Strategien sein wird. Am Ende dieses Artikels werden Sie wissen, wie Sie eine einfache Hedge-Strategie erstellen können, und Sie werden auch erfahren, was die Leute darüber sagen, ob diese Strategie wirklich zu 100 % profitabel ist.
Algorithmen zur Optimierung mit Populationen: Algorithmus des Mind Evolutionary Computation (MEC) Algorithmen zur Optimierung mit Populationen: Algorithmus des Mind Evolutionary Computation (MEC)
Der Artikel befasst sich mit einem Algorithmus aus der MEC-Familie, dem Simple Mind Evolutionary Computation Algorithmus (Simple MEC, SMEC). Der Algorithmus zeichnet sich durch die Schönheit seiner Idee und die Einfachheit seiner Umsetzung aus.