English Русский Español Português
preview
Entwicklung eines MQL5 RL-Agenten mit Integration von RestAPI (Teil 4): Organisieren von Funktionen in Klassen in MQL5

Entwicklung eines MQL5 RL-Agenten mit Integration von RestAPI (Teil 4): Organisieren von Funktionen in Klassen in MQL5

MetaTrader 5Beispiele | 12 Juli 2024, 09:47
22 0
Jonathan Pereira
Jonathan Pereira

Einführung

Willkommen zum vierten Teil unserer Serie, in der wir die Erstellung eines Reinforcement Learning Agenten in MQL5 mit RestAPI-Integration untersuchen. Vor dem heutigen Artikel haben wir uns mit so wichtigen Aspekten wie der Verwendung von RestAPI in MQL5, der Erstellung von MQL5-Funktionen für die Interaktion mit der REST-API in Tic-Tac-Toe sowie der Ausführung von automatischen Bewegungen und Testskripten beschäftigt. Dies gab uns eine solide Grundlage und half uns zu verstehen, wie MQL5 mit externen Elementen interagiert.

In diesem Artikel werden wir einen wichtigen Schritt machen und unsere Funktionen in MQL5 in Klassen organisieren. Zu diesem Zweck werden wir die objektorientierte Programmierung (OOP) verwenden. OOP ist eine Art, Code zu schreiben, die dazu beiträgt, ihn zu organisieren und leicht zu verstehen. Das ist wichtig, weil es uns die Wartung und Verbesserung des Codes erleichtert. Der Code ist gut organisiert und modular, wir können ihn in verschiedenen Teilen des Projekts oder sogar in zukünftigen Projekten verwenden.

In diesem Artikel werden wir auch sehen, wie man bestehende MQL5-Funktionen in Klassen umstrukturiert. Wir werden sehen, wie dies den Code lesbarer und effizienter machen kann. Außerdem enthält der Artikel praktische Beispiele, die zeigen, wie die Anwendung der vorgestellten Ideen die Wartung und Verbesserung des Codes erleichtern kann.

Die objektorientierte Programmierung (OOP) ist eine leistungsfähige Methode zur Entwicklung von Software. In MQL5 ist die Verwendung von Klassen ein großer Vorteil gegenüber der prozeduralen Codeschreibmethode. In diesem Teil werden wir uns ansehen, wie wir die Qualität unseres Projekts mithilfe dieses Merkmals verbessern können. Schauen wir uns vier wichtige Aspekte an:

  1. Kapselung und Modularität: Klassen helfen dabei, verwandte Funktionen und Variablen an einem Ort zu organisieren, was die Wartung erleichtert und Fehler reduziert.

  2. Wiederverwendung des Codes: Sobald Sie eine Klasse geschrieben haben, können Sie sie an verschiedenen Stellen verwenden, was Zeit spart und die Konsistenz des Codes gewährleistet.

  3. Leichte Wartung und Verbesserung: Wenn Funktionen in Klassen aufgeteilt sind, ist es einfacher, Fehler zu finden und zu beheben oder Verbesserungen vorzunehmen, da die klare Struktur den Code leichter zugänglich macht.

  4. Abstraktion und Flexibilität: Klassen fördern die Abstraktion, indem sie die Komplexität verbergen und nur das offenlegen, was wir brauchen. Dadurch wird der Code intuitiver und flexibler.

Wir werden sehen, dass die Umstrukturierung von Funktionen in Klassen in MQL5 nicht nur der Schönheit halber erfolgt, sondern dass es sich um eine bedeutende Änderung handelt, die den Code effizienter, verständlicher und wartungsfreundlicher macht. In diesem Artikel wird gezeigt, wie isolierte Funktionen in wohldefinierte Klassenmethoden umgewandelt werden können, was sowohl unmittelbare als auch langfristige Vorteile mit sich bringt. Dies wird nicht nur unser aktuelles Projekt verbessern, sondern uns auch helfen, eine solide Grundlage für zukünftige MQL5-Projekte zu schaffen.


Aktueller Stand des Codes 

In seinem derzeitigen Zustand besteht unser Code aus einer Reihe von Funktionen zur Bearbeitung von HTTP-Anfragen, wie SendGetRequest, SendPostRequest und Request. Diese Funktionen sind für das Senden von GET- und POST-Anfragen an die API, die Verarbeitung der Antworten und die Beseitigung möglicher Fehler zuständig.

//+------------------------------------------------------------------+
//|                                                      Request.mqh |
//|                                    Copyright 2023, Lejjo Digital |
//|                           https://www.mql5.com/en/users/14134597 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Lejjo Digital"
#property link      "https://www.mql5.com/en/users/14134597"
#property version   "1.00"

#define ERR_HTTP_ERROR_FIRST        ERR_USER_ERROR_FIRST+1000 //+511

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000, bool debug=false)
  {
   char data[];
   uchar result[];
   string result_headers;
   int res = -1;

   int data_size = StringLen(query_param);

   if(data_size > 0)
     {
      StringToCharArray(query_param, data, 0, data_size);
      res = WebRequest("GET", url + "?" + query_param, NULL, NULL, timeout, data, data_size, result, result_headers);
     }
   else
     {
      res = WebRequest("GET", url, headers, timeout, data, result, result_headers);
     }

   if(res >= 200 && res <= 204)  // OK
     {
      //--- delete BOM
      int start_index = 0;
      int size = ArraySize(result);
      for(int i = 0; i < fmin(size, 8); i++)
        {
         if(result[i] == 0xef || result[i] == 0xbb || result[i] == 0xbf)
            start_index = i + 1;
         else
            break;
        }
      out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);

      if(debug)
         Print(out);

      return res;
     }
   else
     {
      if(res == -1)
        {
         return (_LastError);
        }
      else
        {
         //--- HTTP errors
         if(res >= 100 && res <= 511)
           {
            out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);

            if(debug)
               Print(out);

            return res;
           }
         return (res);
        }
     }

   return (0);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int SendPostRequest(const string url, const string payload, string &out, string headers = "", const int timeout = 5000, bool debug=false)
  {
   char data[];
   uchar result[];
   string result_headers;
   int res = -1;

   ArrayResize(data, StringToCharArray(payload, data, 0, WHOLE_ARRAY) - 1);

   if(headers == "")
     {
      headers = "Content-Type: application/json\r\n";
     }

   res = WebRequest("POST", url, headers, timeout, data, result, result_headers);

   if(res >= 200 && res <= 204)  // OK
     {
      //--- delete BOM
      int start_index = 0;
      int size = ArraySize(result);
      for(int i = 0; i < fmin(size, 8); i++)
        {
         if(result[i] == 0xef || result[i] == 0xbb || result[i] == 0xbf)
            start_index = i + 1;
         else
            break;
        }
      out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);

      if(debug)
         Print(out);

      return res;
     }
   else
     {
      if(res == -1)
        {
         return (_LastError);
        }
      else
        {
         //--- HTTP errors
         if(res >= 100 && res <= 511)
           {
            out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);

            if(debug)
               Print(out);

            return res;
           }
         return (res);
        }
     }

   return res;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int Request(string method,
            string &out,
            const string url,
            const string payload = "",
            const string query_param = "",
            string headers = "",
            const int timeout = 5000)
  {
   ResetLastError();

   if(method == "GET")
     {
      return SendGetRequest(url, query_param, out, headers, timeout);
     }
   else
      if(method == "POST")
        {
         return SendPostRequest(url, payload, out, headers, timeout);
        }

   return -1;
  }
//+------------------------------------------------------------------+


Herausforderungen und Probleme bei diesem Ansatz:

  1. Mangel an Kapselung und Modularität: Derzeit sind die Funktionen isoliert, und es gibt keinen klaren Mechanismus, um sie nach Funktionalität oder Zweck zu gruppieren. Dies erschwert die Pflege und das Verständnis des logischen Ablaufs.

  2. Begrenzte Wiederverwendung von Code: Da die Funktionen spezifisch und nicht in einer modularen Struktur organisiert sind, ist die Wiederverwendung von Code in verschiedenen Kontexten oder Projekten begrenzt, was zu Code-Duplizierung führen kann. Dies wiederum erhöht das Risiko von Unstimmigkeiten und Fehlern.

  3. Komplexe Wartung und Erweiterbarkeit: Ohne eine klare Trennung der Zuständigkeiten wird das Erkennen und Beheben von Fehlern sowie das Hinzufügen neuer Funktionen zu einer komplexen Aufgabe. Dies ist besonders problematisch bei Projekten, die sich ausweiten oder ständig aktualisiert werden müssen.

Beispiele für die aktuelle Funktionsorganisation:

In ihrem derzeitigen Zustand werden die Funktionen nach einem Verfahrensschema ausgeführt. Die Funktion SendGetRequest beispielsweise nimmt URL-Parameter, Anforderungsparameter und andere entgegen und gibt das Ergebnis auf der Grundlage der WebRequest-Antwort zurück. In ähnlicher Weise behandelt SendPostRequest POST-Anfragen. Die Anforderungsfunktionen dienen zur Erleichterung von GET- und POST-Funktionsaufrufen, je nach der von uns angegebenen HTTP-Methode.

Die Funktion SendGetRequest:

int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000, bool debug=false)
  {
   char data[];
   uchar result[];
   string result_headers;
   int res = -1;

   int data_size = StringLen(query_param);

   if(data_size > 0)
     {
      StringToCharArray(query_param, data, 0, data_size);
      res = WebRequest("GET", url + "?" + query_param, NULL, NULL, timeout, data, data_size, result, result_headers);
     }
   else
     {
      res = WebRequest("GET", url, headers, timeout, data, result, result_headers);
     }

   if(res >= 200 && res <= 204)  // OK
     {
      //--- delete BOM
      int start_index = 0;
      int size = ArraySize(result);
      for(int i = 0; i < fmin(size, 8); i++)
        {
         if(result[i] == 0xef || result[i] == 0xbb || result[i] == 0xbf)
            start_index = i + 1;
         else
            break;
        }
      out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);

      if(debug)
         Print(out);

      return res;
     }
   else
     {
      if(res == -1)
        {
         return (_LastError);
        }
      else
        {
         //--- HTTP errors
         if(res >= 100 && res <= 511)
           {
            out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);

            if(debug)
               Print(out);

            return res;
           }
         return (res);
        }
     }

   return (0);
  }

Die Funktion SendPostRequest:

int SendPostRequest(const string url, const string payload, string &out, string headers = "", const int timeout = 5000, bool debug=false)
  {
   char data[];
   uchar result[];
   string result_headers;
   int res = -1;

   ArrayResize(data, StringToCharArray(payload, data, 0, WHOLE_ARRAY) - 1);

   if(headers == "")
     {
      headers = "Content-Type: application/json\r\n";
     }

   res = WebRequest("POST", url, headers, timeout, data, result, result_headers);

   if(res >= 200 && res <= 204)  // OK
     {
      //--- delete BOM
      int start_index = 0;
      int size = ArraySize(result);
      for(int i = 0; i < fmin(size, 8); i++)
        {
         if(result[i] == 0xef || result[i] == 0xbb || result[i] == 0xbf)
            start_index = i + 1;
         else
            break;
        }
      out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);

      if(debug)
         Print(out);

      return res;
     }
   else
     {
      if(res == -1)
        {
         return (_LastError);
        }
      else
        {
         //--- HTTP errors
         if(res >= 100 && res <= 511)
           {
            out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);

            if(debug)
               Print(out);

            return res;
           }
         return (res);
        }
     }

   return res;
  }

Beachten Sie, dass die Funktionen mehrere sich wiederholende Elemente enthalten, was z. B. die Behandlung von Reaktionen auf verschiedene Fehler erschwert, da sie in einem Teil angewendet und in einem anderen ignoriert werden können, oder ähnliche Situationen.

Dieser Ansatz ist zwar funktional, nutzt aber nicht die Vorteile der Objektorientierung, wie z. B. Kapselung und Modularität. Jede Funktion arbeitet relativ unabhängig, ohne eine einzige Struktur, die sie miteinander verbindet oder ihr Verhalten auf einheitliche Weise steuert.


Die Bedeutung von OOP

OOP ist ein Programmierparadigma, das „Objekte“ als grundlegende Bausteine verwendet. Bei diesen Objekten handelt es sich um Datenstrukturen, die aus Datenfeldern und als Methoden bezeichneten Prozeduren bestehen und reale Entitäten oder Konzepte darstellen. In OOP hat jedes Objekt die Fähigkeit, Nachrichten zu empfangen und zu senden und Daten zu verarbeiten, während es als autonome Einheit mit spezifischen Funktionen oder Verantwortlichkeiten innerhalb des Softwaresystems agiert.

Vorteile von OOP bei der Wartung und Skalierung von Projekten:

  1. Leichtere Wartung: OOP macht Software aufgrund ihres modularen Aufbaus einfacher zu warten. Jedes Objekt ist eine unabhängige Einheit mit eigener Logik und eigenen Daten, was bedeutet, dass Änderungen an einem bestimmten Objekt im Allgemeinen keine Auswirkungen auf andere Objekte haben. Diese Funktion macht den Prozess der Aktualisierung, Fehlerbehebung und Verbesserung des Systems sehr viel überschaubarer.

  2. Verbesserte Skalierbarkeit: OOP ermöglicht es Entwicklern, Systeme zu erstellen, die in Größe und Komplexität leicht skaliert werden können. Das Hinzufügen neuer Funktionen wird effizienter, da neue Objekte mit spezifischen Funktionen erstellt werden können, ohne dass umfangreiche Änderungen am bestehenden Code vorgenommen werden müssen.

  3. Wiederverwendung des Codes: Die Vererbung, eines der Grundprinzipien der OOP, ermöglicht es Entwicklern, neue Klassen auf der Grundlage bestehender Klassen zu erstellen. Dies fördert die Wiederverwendung von Code, reduziert Redundanzen und erleichtert die Wartung.

Wie hilft die Modularität, unseren Code zu verbessern?

Modularität ist einer der Hauptvorteile von OOP. Es bietet Entwicklern die folgenden Funktionen:

  1. Aufschlüsseln Komplexer Systeme: Mit Hilfe von OOP kann ein komplexes System in kleinere, überschaubare Komponenten (Objekte) mit jeweils klar definierten Zuständigkeiten aufgeteilt werden. Dadurch wird das System leichter zu verstehen, zu entwickeln und zu pflegen.

  2. Fokus auf Abstraktion: Die Modularität ermöglicht es den Entwicklern, sich auf die Abstraktion zu konzentrieren und an Konzepten auf hoher Ebene statt an Details auf niedriger Ebene zu arbeiten, wodurch komplexe Probleme leichter zu lösen sind und der Code sauberer wird.

  3. Förderung von Flexibilität und Erweiterbarkeit: Objekte und Klassen können so gestaltet werden, dass sie flexibel und erweiterbar sind, sodass das System im Laufe der Zeit weiterentwickelt und angepasst werden kann, ohne dass es komplett neu geschrieben werden muss.

  4. Ermutigung zur Zusammenarbeit: In einer kollaborativen Entwicklungsumgebung können verschiedene Teams oder Entwickler gleichzeitig an verschiedenen Modulen oder Objekten arbeiten, was die Effizienz erhöht und die Entwicklungszeit verkürzt.

Die Verwendung von OOP in unserem Projekt mit RestAPI-Integration bietet einen robusten Ansatz zur Bewältigung der Softwarekomplexität, der die Wartbarkeit, Skalierbarkeit und allgemeine Codequalität erheblich verbessert.


Refaktorierung von Funktionen in Klassen

Da wir nun wissen, wie wichtig OOP ist und wie es die Wartbarkeit und Skalierbarkeit unserer Projekte verbessern kann, schlage ich vor, die bestehenden Funktionen in Klassen umzuwandeln. Zur besseren Veranschaulichung dieses Prozesses stellen wir ein Diagramm zur Verfügung, das zeigt, wie der neue objektorientierte Code besser organisiert und verständlich sein wird. Wir werden schrittweise vorgehen, um unseren prozeduralen Code in besser organisierten und verständlichen objektorientierten Code umzuwandeln.


Umsetzung

Schritt 1. Definition von Schnittstellen. Wir beginnen mit der Definition von Schnittstellen für unsere Objekte, die die Methoden und Funktionen beschreiben, die sie haben sollen. Wir haben zwei: IHttpRequest und IHttpResponseProcessor. Diese Schnittstellen definieren die Verträge, die unsere konkreten Klassen einhalten müssen.

//+------------------------------------------------------------------+
//| Interface for HttpRequest                                        |
//+------------------------------------------------------------------+
interface IHttpRequest
{
public:
   virtual int Request(string method, string &out, const string url, const string payload = "", const string query_param = "") = 0;
   virtual int ValidateMethod(string method) = 0;
   virtual int PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param) = 0;
   virtual int PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload) = 0;
};

//+------------------------------------------------------------------+
//| Interface for HttpResponseProcessor                              |
//+------------------------------------------------------------------+
interface IHttpResponseProcessor
{
public:
   virtual int ProcessResponse(int res, string &out, uchar &result[]) = 0;
   virtual int ProcessSuccessResponse(string &out, uchar &result[]) = 0;
   virtual int ProcessErrorResponse(int res, string &out, uchar &result[]) = 0;
   virtual int DetectAndSkipBOM(uchar &result[], int size) = 0;
};

Schritt 2. Erstellen der abstrakten Klassen. Wir erstellen abstrakte Klassen, die diese Schnittstellen implementieren. Diese Klassen haben keine eigentliche Implementierung von Methoden, sondern definieren die entsprechenden Strukturen. Die abstrakten Klassen sind HttpResponseProcessorBase und HttpRequestBase.

//+------------------------------------------------------------------+
//| Abstract base class for HttpResponseProcessor                    |
//+------------------------------------------------------------------+
class HttpResponseProcessorBase : public IHttpResponseProcessor
{
public:
   HttpResponseProcessorBase() {}
   virtual int ProcessResponse(int res, string &out, uchar &result[]) override = 0;
   virtual int ProcessSuccessResponse(string &out, uchar &result[]) override = 0;
   virtual int ProcessErrorResponse(int res, string &out, uchar &result[]) override = 0;
   virtual int DetectAndSkipBOM(uchar &result[], int size) override = 0;
};

//+------------------------------------------------------------------+
//| Abstract base class for HttpRequest                              |
//+------------------------------------------------------------------+
class HttpRequestBase : public IHttpRequest
{
protected:
   string m_headers;
   int m_timeout;
   IHttpResponseProcessor *responseProcessor;

public:
   HttpRequestBase(string headers = "", int timeout = 5000) : m_headers(headers), m_timeout(timeout)
   {
      if (responseProcessor == NULL)
      {
         responseProcessor = new HttpResponseProcessor();
      }
   }
   virtual int Request(string method, string &out, const string url, const string payload = "", const string query_param = "") override;
   virtual int ValidateMethod(string method) override;
   virtual int PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param) override = 0;
   virtual int PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload) override = 0;
   virtual int ProcessResponse(int res, string &out, uchar &result[]) = 0;
};

Die Klasse HttpRequestBase:

  1. HttpRequestBase(string headers = "", int timeout = 5000): Dies ist der Konstruktor für die Klasse HttpRequestBase. Die beiden optionalen Parameter headers und timeout geben die HTTP-Header an, die in Anfragen gesendet werden sollen, bzw. den Timeout für die Antwort. Der Konstruktor initialisiert die angegebenen Werte und erstellt eine Instanz der Klasse HttpResponseProcessor (die Klasse, die HTTP-Antworten verarbeitet).

  2. virtual int Request(string method, string &out, const string url, const string payload = "", const string query_param = ""): Mit dieser virtuellen Methode können Sie eine HTTP-Anfrage stellen. Enthält die HTTP-Methode (GET oder POST), die Ziel-URL, den möglichen Request Body(Payload) und die Request-Parameter(query_param). Koordiniert die Funktionsaufrufe von PerformGetRequest und PerformPostRequest auf der Grundlage der angegebenen Methode und verarbeitet dann die Antwort mit der ProcessResponse-Methode.

  3. virtual int ValidateMethod(string method): Diese Methode prüft die Gültigkeit der angegebenen HTTP-Methode (GET oder POST). Gibt true zurück, wenn sie gültig ist, und false, wenn nicht.

  4. virtual int PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param): Diese abstrakte virtuelle Methode muss von abgeleiteten Klassen implementiert werden. Führt eine HTTP-GET-Anfrage an die angegebene URL durch und gibt die Antwortdaten (responde) im Parameter data, das Ergebnis (result) im Parameter result und die Antwort-Header in result_headers zurück.

  5. virtual int PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload): Diese abstrakte virtuelle Methode muss von abgeleiteten Klassen implementiert werden. Führt eine HTTP-POST-Anforderung an die angegebene URL mit einem Anforderungskörper (Payload) aus und gibt die Antwortdaten im Parameter data, das Ergebnis im Parameter result und die Antwort-Header in result_headers zurück.

  6. virtual int ProcessResponse(int res, string &out, uchar &result[]): Diese abstrakte virtuelle Methode muss von abgeleiteten Klassen implementiert werden. Sie verarbeitet die HTTP-Antwort auf der Grundlage des Antwortcodes „res“. Ist die Antwort erfolgreich (der Antwortcode liegt im Bereich von 200 bis 299), wird ProcessSuccessResponse aufgerufen. Andernfalls wird ProcessErrorResponse aufgerufen. Das Ergebnis wird in out gespeichert, und die Rohdaten der Antwort befinden sich in result.


Schritt 3. Erstellen konkreter Klassen. Lassen Sie uns konkrete Klassen erstellen, die Methoden der Schnittstelle implementieren. HttpRequest und HttpResponseProcessor sind konkrete Klassen.

//+------------------------------------------------------------------+
//| Concrete class for HttpRequest                                   |
//+------------------------------------------------------------------+
class HttpRequest : public HttpRequestBase
{
public:
   HttpRequest(string headers = "", int timeout = 5000) : HttpRequestBase(headers, timeout) {}

   virtual int PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param) override;
   virtual int PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload) override;
   virtual int ProcessResponse(int res, string &out, uchar &result[]) override;
};

//+------------------------------------------------------------------+
//| Concrete class for HttpResponseProcessor                         |
//+------------------------------------------------------------------+
class HttpResponseProcessor : public HttpResponseProcessorBase
{
public:
   virtual int ProcessResponse(int res, string &out, uchar &result[]) override;
   virtual int ProcessSuccessResponse(string &out, uchar &result[]) override;
   virtual int ProcessErrorResponse(int res, string &out, uchar &result[]) override;
   virtual int DetectAndSkipBOM(uchar &result[], int size) override;
};


Schritt 4. Implementierung von Methoden konkreter Klassen. Lassen Sie uns konkrete Klassenmethoden mit echter Funktionalität implementieren. Hier haben wir die Methoden PerformGetRequest, PerformPostRequest, ProcessResponse, ProcessSuccessResponse, ProcessErrorResponse und DetectAndSkipBOM.

int HttpRequest::PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param)
{
   if (StringLen(query_param) > 0)
      return WebRequest("GET", url + "?" + query_param, NULL, NULL, m_timeout, data, StringLen(query_param), result, result_headers);

   return WebRequest("GET", url, m_headers, m_timeout, data, result, result_headers);
}

int HttpRequest::PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload)
{
   if (m_headers == "")
      m_headers = "Content-Type: application/json\r\n";
   ArrayResize(data, StringToCharArray(payload, data, 0, WHOLE_ARRAY) - 1);
   return WebRequest("POST", url, m_headers, m_timeout, data, result, result_headers);
}

int HttpRequest::ProcessResponse(int res, string &out, uchar &result[])
{
   if (res >= 200 && res <= 299)
      return responseProcessor.ProcessSuccessResponse(out, result);

   return responseProcessor.ProcessErrorResponse(res, out, result);
}

int HttpResponseProcessor::ProcessResponse(int res, string &out, uchar &result[])
{
   if (res >= 200 && res <= 299)
      return ProcessSuccessResponse(out, result);

   return ProcessErrorResponse(res, out, result);
}

int HttpResponseProcessor::ProcessSuccessResponse(string &out, uchar &result[])
{
   int size = ArraySize(result);
   int start_index = DetectAndSkipBOM(result, size);
   out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);
   return 0;
}

int HttpResponseProcessor::ProcessErrorResponse(int res, string &out, uchar &result[])
{
   ResetLastError();
   if (res == -1)
      return GetLastError();
   else if (res >= 100 && res <= 511)  // Errors HTTP
   {
      out = CharArrayToString(result);
      Print(out);
      return res;
   }
   return res;
}

int HttpResponseProcessor::DetectAndSkipBOM(uchar &result[], int size)
{
   int start_index = 0;
   for (int i = 0; i < MathMin(size, 3); i++)
   {
      if (result[i] == 0xef || result[i] == 0xbb || result[i] == 0xbf)
         start_index = i + 1;
      else
         break;
   }
   return start_index;
}

HttpRequest-Klasse:

  1. HttpRequest(string headers = "", int timeout = 5000): Dies ist ein Konstruktor der Klasse HttpRequest. Er ruft den Konstruktor der Basisklasse HttpRequestBase auf, um Header- und Timeout-Parameter auszulösen.

  2. virtual int PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param): Dies ist eine Implementierung der PerformGetRequest-Methode in der HttpRequest-Klasse. Führt eine HTTP-GET-Anfrage an die angegebene URL durch, einschließlich der Anfrageparameter, falls vorhanden. Die Rohdaten der Antwort werden in data gespeichert, die Ergebnisse in result, und die Kopfzeilen der Antwort in result_headers.

  3. virtual int PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload): Dies ist eine Implementierung der PerformPostRequest-Methode in der HttpRequest-Klasse. Führt eine HTTP-POST-Anfrage an die angegebene URL aus, einschließlich des Anfrage-Hauptteils (Payload). Wir speichern die Rohdaten der Antwort in data, die Ergebnisse in result und die Kopfzeilen der Antwort in result_headers.

  4. virtual int ProcessResponse(int res, string &out, uchar &result[]): An implementation of the ProcessResponse method in the HttpRequest class. Ruft ProcessSuccessResponse auf, wenn die Antwort erfolgreich ist (der Antwortcode liegt im Bereich zwischen 200 und 299), andernfalls ProcessErrorResponse. Wir speichern das Ergebnis in „out“ und die Rohdaten der Antwort in „result“.


Vorteile der Umstrukturierung:

Die Umstrukturierung des Codes unseres Projekts von einem prozeduralen zu einem objektorientierten Ansatz bringt mehrere bedeutende Vorteile mit sich. Wir werden sie diskutieren, indem wir den alten Code mit dem neuen Code vergleichen, der Klassen verwendet, und uns darauf konzentrieren, wie dies die Lesbarkeit, Wartbarkeit und Anpassbarkeit des Codes verbessert.

Vergleich von altem und neuem Code mit Klassen:

Vorheriger Code (verfahrensrechtlich):

  • Struktur: Der Code bestand aus einzelnen Funktionen (SendGetRequest, SendPostRequest, Request), die verschiedene Aspekte von HTTP-Anfragen behandelten.
  • Wartung: Jede Änderung an einer Funktion konnte ähnliche Änderungen an anderen Funktionen erforderlich machen, da sich der Code wiederholte und nicht wirklich eine gemeinsame Logik hatte.
  • Lesbarkeit: Obwohl jede einzelne Funktion relativ einfach war, war der Code als Ganzes schwieriger zu verstehen, insbesondere für neue Entwickler.

Neuer (objektorientierter) Code:

  • Struktur: Einführung von Schnittstellen (IHttpRequest, IHttpResponseProcessor) und abstrakten Klassen (HttpRequestBase, HttpResponseProcessorBase), gefolgt von konkreten Implementierungen (HttpRequest, HttpResponseProcessor).
  • Wartung: Der Code ist jetzt modularer und hat klar definierte Aufgaben für jede Klasse. Das macht es einfacher, Ihren Code zu aktualisieren und zu korrigieren, da Änderungen an einer Klasse normalerweise keine Auswirkungen auf andere Klassen haben.
  • Lesbarkeit: Durch die Einteilung in Klassen und Methoden wird der Code intuitiver. Alle Klassen und Methoden haben einen klaren Zweck, sodass es einfacher ist zu verstehen, was der Code tut und wie er funktioniert.

Verbesserte Lesbarkeit und Wartung:

Lesbarkeit

  • Logische Organisation: Der Code ist nun in Klassen mit spezifischen Funktionen unterteilt, wodurch die Beziehungen zwischen den verschiedenen Teilen des Codes leichter zu verstehen sind.
  • Beschreibende Namen: Bei der Verwendung von Klassen und Methoden können die Namen aussagekräftiger sein, um die Funktionalität der einzelnen Teile des Codes deutlich zu machen.

Wartung

  • Leicht zu aktualisieren: Änderungen an einem Teil des Codes (z. B. an der Logik der HTTP-Antwortverarbeitung) können an einer Stelle vorgenommen werden, ohne dass mehrere über den Code verteilte Funktionen geändert werden müssen.
  • Erweiterbarkeit: Das Hinzufügen neuer Funktionen oder die Anpassung des Codes an neue Anforderungen ist einfach, da die objektorientierte Struktur so konzipiert ist, dass sie erweiterbar und flexibel ist.
Anpassung an künftige Veränderungen
  • Skalierbarkeit: Wenn das Projekt wächst, wird es einfacher, neue Funktionen hinzuzufügen oder mit anderen APIs und Systemen zu integrieren. Klassen können erweitert oder neue Klassen auf der Grundlage bestehender Klassen erstellt werden.
  • Wiederverwendung des Codes: Komponenten können in verschiedenen Teilen des Projekts oder sogar in anderen Projekten wiederverwendet werden, was Zeit und Mühe spart.
  • Einfaches Testen: Das Testen des Codes wird einfacher, da Sie sich auf bestimmte Einheiten (Klassen oder Methoden) individuell konzentrieren können.

Die Umstellung unseres Codes auf einen objektorientierten Ansatz war eine strategische Änderung, die nicht nur die aktuelle Qualität unseres Projekts verbessert, sondern auch eine solide Grundlage für die zukünftige Entwicklung schafft. Durch diese Umwandlung erhalten wir einen saubereren Code, der leichter zu verstehen, zu pflegen und zu erweitern ist.

Durch die Kapselung der Logik in wohldefinierten Klassen wird Redundanz reduziert, die Übersichtlichkeit verbessert und die Effizienz des Codes erhöht. Dies ist besonders wichtig in einem sich ständig verändernden Umfeld, in dem Flexibilität und die Fähigkeit, schnell auf neue Anforderungen zu reagieren, wichtig sind.

Darüber hinaus erleichtert die mit OOP erreichte Modularität die Zusammenarbeit im Team, da an verschiedenen Teilen des Projekts gleichzeitig gearbeitet werden kann und das Risiko von Codekonflikten geringer ist. Dies öffnet auch die Tür zu fortschrittlicheren Entwicklungstechniken, wie z. B. Unit-Tests, die in einem objektorientierten Rahmen leichter zu implementieren sind.

//+------------------------------------------------------------------+
//|                                                     Requests.mqh |
//|                                    Copyright 2023, Lejjo Digital |
//|                           https://www.mql5.com/en/users/14134597 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Lejjo Digital"
#property link      "https://www.mql5.com/de/users/14134597"
#property version   "1.05"

//+------------------------------------------------------------------+
//| Interface for HttpRequest                                        |
//+------------------------------------------------------------------+
interface IHttpRequest
  {
public:
   virtual int       Request(string method, string &out, const string url, const string payload = "", const string query_param = "") = 0;
   virtual int       ValidateMethod(string method) = 0;
   virtual int       PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param) = 0;
   virtual int       PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload) = 0;
  };



//+------------------------------------------------------------------+
//| Interface for HttpResponseProcessor                              |
//+------------------------------------------------------------------+
interface IHttpResponseProcessor
  {
public:
   virtual int       ProcessResponse(int res, string &out, uchar &result[]) = 0;
   virtual int       ProcessSuccessResponse(string &out, uchar &result[]) = 0;
   virtual int       ProcessErrorResponse(int res, string &out, uchar &result[]) = 0;
   virtual int       DetectAndSkipBOM(uchar &result[], int size) = 0;
  };



//+------------------------------------------------------------------+
//| Abstract base class for HttpResponseProcessor                    |
//+------------------------------------------------------------------+
class HttpResponseProcessorBase : public IHttpResponseProcessor
  {
public:
                     HttpResponseProcessorBase() {};
   virtual int       ProcessResponse(int res, string &out, uchar &result[]) override = 0;
   virtual int       ProcessSuccessResponse(string &out, uchar &result[]) override = 0;
   virtual int       ProcessErrorResponse(int res, string &out, uchar &result[]) override = 0;
   virtual int       DetectAndSkipBOM(uchar &result[], int size) override = 0;
  };



//+------------------------------------------------------------------+
//| Abstract base class for HttpRequest                              |
//+------------------------------------------------------------------+
class HttpRequestBase : public IHttpRequest
  {
protected:
   string            m_headers;
   int               m_timeout;
   IHttpResponseProcessor *responseProcessor;

public:
                     HttpRequestBase(string headers = "", int timeout = 5000) : m_headers(headers), m_timeout(timeout)
     {

      if(responseProcessor == NULL)
        {
         responseProcessor = new HttpResponseProcessor();
        }

     }
   virtual int       Request(string method, string &out, const string url, const string payload = "", const string query_param = "") override;
   virtual int       ValidateMethod(string method) override;
   virtual int       PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param) override = 0;
   virtual int       PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload) override = 0;
   virtual int       ProcessResponse(int res, string &out, uchar &result[]) = 0;
  };
//+------------------------------------------------------------------+
//| Implement the Request function in HttpRequestBase class          |
//+------------------------------------------------------------------+
int HttpRequestBase::Request(string method, string &out, const string url, const string payload, const string query_param) override
  {
   if(!ValidateMethod(method))
     {
      out = "Método HTTP inválido.";
      return -1;
     }

   char data[];
   uchar result[];
   string result_headers;
   int res = -1;

   if(method == "GET")
      res = PerformGetRequest(data, result, result_headers, url, query_param);
   else
      if(method == "POST")
         res = PerformPostRequest(data, result, result_headers, url, payload);

   if(res >= 0)
      return ProcessResponse(res, out, result);
   else
     {
      out = "Error when making HTTP request.";
      return res;
     }
  }
//+------------------------------------------------------------------+
//| Implement the ValidateMethod function in HttpRequestBase class   |
//+------------------------------------------------------------------+
int HttpRequestBase::ValidateMethod(string method)
  {
   return (method == "GET" || method == "POST");
  }



//+------------------------------------------------------------------+
//| Concrete class for HttpRequest                                   |
//+------------------------------------------------------------------+
class HttpRequest : public HttpRequestBase
  {
public:
                     HttpRequest(string headers = "", int timeout = 5000) : HttpRequestBase(headers, timeout) {}

   virtual int       PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param) override;
   virtual int       PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload) override;
   virtual int       ProcessResponse(int res, string &out, uchar &result[]) override;
  };
//+------------------------------------------------------------------+
//| Implementation of functions for HttpRequest class                |
//+------------------------------------------------------------------+
int HttpRequest::PerformGetRequest(char &data[], uchar &result[], string &result_headers, const string url, const string query_param)
  {
   if(StringLen(query_param) > 0)
      return WebRequest("GET", url + "?" + query_param, NULL, NULL, m_timeout, data, StringLen(query_param), result, result_headers);

   return WebRequest("GET", url, m_headers, m_timeout, data, result, result_headers);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int HttpRequest::PerformPostRequest(char &data[], uchar &result[], string &result_headers, const string url, const string payload)
  {
   if(m_headers == "")
      m_headers = "Content-Type: application/json\r\n";
   ArrayResize(data, StringToCharArray(payload, data, 0, WHOLE_ARRAY) - 1);
   return WebRequest("POST", url, m_headers, m_timeout, data, result, result_headers);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int HttpRequest::ProcessResponse(int res, string &out, uchar &result[])
  {
   if(res >= 200 && res <= 299)
      return responseProcessor.ProcessSuccessResponse(out, result);

   return responseProcessor.ProcessErrorResponse(res, out, result);
  }



//+------------------------------------------------------------------+
//| Concrete class for HttpResponseProcessor                         |
//+------------------------------------------------------------------+
class HttpResponseProcessor : public HttpResponseProcessorBase
  {
public:
   virtual int       ProcessResponse(int res, string &out, uchar &result[]) override;
   virtual int       ProcessSuccessResponse(string &out, uchar &result[]) override;
   virtual int       ProcessErrorResponse(int res, string &out, uchar &result[]) override;
   virtual int       DetectAndSkipBOM(uchar &result[], int size) override;
  };
//+------------------------------------------------------------------+
//| Implementation of functions for HttpResponseProcessor class      |
//+------------------------------------------------------------------+
int       HttpResponseProcessor::ProcessResponse(int res, string &out, uchar &result[])
  {
   if(res >= 200 && res <= 299)
      return ProcessSuccessResponse(out, result);

   return ProcessErrorResponse(res, out, result);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int       HttpResponseProcessor::ProcessSuccessResponse(string &out, uchar &result[]) override
  {
   int size = ArraySize(result);
   int start_index = DetectAndSkipBOM(result, size);
   out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);
   return 0;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int       HttpResponseProcessor::ProcessErrorResponse(int res, string &out, uchar &result[]) override
  {
   ResetLastError();
   if(res == -1)
      return GetLastError();
   else
      if(res >= 100 && res <= 511)  // Errors HTTP
        {
         out = CharArrayToString(result);
         Print(out);
         return res;
        }
   return res;
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int       HttpResponseProcessor::DetectAndSkipBOM(uchar &result[], int size) override
  {
   int start_index = 0;
   for(int i = 0; i < MathMin(size, 3); i++)
     {
      if(result[i] == 0xef || result[i] == 0xbb || result[i] == 0xbf)
         start_index = i + 1;
      else
         break;
     }
   return start_index;
  };
//+------------------------------------------------------------------+


Beispiele für die Verwendung von Klassen

Dieser Abschnitt enthält praktische Beispiele für die Verwendung von Klassen, die zur Durchführung von HTTP-Anfragen in MQL5 erstellt wurden. Diese Beispiele veranschaulichen die Wiederverwendung von Code und die Effizienz der Erstellung neuer Funktionen.

Überprüfung des Erfolgs der Antwort:

void TestProcessSuccessResponse()
{
    HttpResponseProcessor processor;
    string output;
    uchar result[];

    // Simulate a successful response in JSON format
    string mockResponse = "{\"status\": \"success\", \"data\": \"Sample data\"}";
    StringToCharArray(mockResponse, result);

    // Process the simulated response
    processor.ProcessSuccessResponse(output, result);

    // Check the output
    Print("Success Test: ", output);
}

Erläuterung:

  • HttpResponseProcessor-Prozessor: Erstellen eines Prozessorobjekts der Klasse HttpResponseProcessor.
  • StringToCharArray: Konvertiert eine simulierte Antwortzeichenfolge in ein Zeichenarray.
  • processor.ProcessSuccessResponse(output, result): Ruft eine Methode zur Verarbeitung der simulierten Antwort auf.

Test der Fehlerreaktion:

void TestProcessErrorResponse()
{
    HttpResponseProcessor processor;
    string output;
    uchar result[];

    // Simulate an error response (404 Not Found)
    string mockResponse = "404 Not Found";
    StringToCharArray(mockResponse, result);

    // Process an error response
    processor.ProcessErrorResponse(404, output, result);

    // Check the output
    Print("Error Test: ", output);
}

Erläuterung:

  • Dieses Beispiel ähnelt dem vorherigen, konzentriert sich aber auf die Modellierung und Verarbeitung einer HTTP-Fehlerantwort.

Test zur Erkennung und zum Überspringen von Stücklisten:

void TestDetectAndSkipBOM()
{
    HttpResponseProcessor processor;
    uchar result[6] = {0xEF, 0xBB, 0xBF, 'a', 'b', 'c'}; // 'abc' with BOM UTF-8

    // Detect and skip the BOM (Byte Order Mark)
    int startIndex = processor.DetectAndSkipBOM(result, ArraySize(result));

    // Check the initial index after BOM
    Print("Start index after BOM: ", startIndex); // Expected: 3
}

Erläuterung:

  • uchar result[6] = {0xEF, 0xBB, 0xBF, 'a', 'b', 'c'};: Erzeugt ein Array mit einem UTF-8 BOM gefolgt von 'abc'.
  • processor.DetectAndSkipBOM(result, ArraySize(result));: Erkennt und überspringt BOM und gibt dann den Startindex des entsprechenden Inhalts zurück.

Durchführung des Tests und der HTTP-GET-Anfrage:

int OnInit()
{
    RunTests(); // Run the tests

    HttpRequest httpRequest("", 5000); // Create an instance of the HttpRequest class
    string output; // Variable to store the output

    // Perform the GET request
    int responseCode = httpRequest.Request("GET", output, "https://jsonplaceholder.typicode.com/posts/1");

    // Show the result
    Print("Response Code: ", responseCode);
    Print("Output: ", output);
}

Erläuterung:

  • HttpRequest httpRequest("", 5000): Erstellen eines httpRequest-Objekts der Klasse HttpRequest mit Standardeinstellungen.
  • httpRequest.Request("GET", output, "https://..."): Durchführen einer GET-Anfrage an die angegebene URL und Speichern der Antwort in der Ausgabevariablen.

Diese Beispiele zeigen, wie die Klassen HttpResponseProcessor und HttpRequest verwendet werden können, um verschiedene Aspekte von HTTP-Antworten zu behandeln, wie Erfolg, Fehler und das Vorhandensein einer Stückliste. Sie demonstrieren auch, wie einfach es ist, GET-Anfragen mit der Klasse HttpRequest zu stellen.

Die Modularisierung von Code in Klassen ist ein grundlegender Ansatz in der Programmierung, der die Schaffung eines organisierten und verständlichen Systems ermöglicht. Bei dieser Praxis wird der Code in unabhängige Einheiten, die so genannten Klassen, unterteilt, von denen jede ihre eigenen Aufgaben und Funktionen hat.

Mit dieser Technik können Entwickler ihren Code logischer und klarer strukturieren, wodurch er lesbarer und leichter verständlich wird. Das bedeutet, dass wir es statt mit monolithischem Code mit desorganisiertem Code zu tun haben; der Entwickler arbeitet mit kleinen Teilen des Systems, von denen jeder durch eine Klasse repräsentiert wird.

Der Vorteil dieses Ansatzes ist, dass Sie Klassen ganzheitlich entwerfen können, wobei die zugehörigen Methoden und Attribute in Gruppen zusammengefasst werden. Dadurch wird der Code nicht nur verständlicher, sondern auch leichter zu pflegen und weiterzuentwickeln, da es einfacher ist, Probleme in einzelnen Blöcken zu finden und zu beheben.

Darüber hinaus fördert die Modularität von Klassen die Wiederverwendung von Code, da Klassen an verschiedenen Stellen im Programm verwendet werden können, was Zeit und Mühe bei der Erstellung ähnlicher Funktionen spart.

Nachfolgend finden Sie ein vollständiges Beispiel mit Testcode, das die praktische Verwendung der Klassen HttpResponseProcessor und HttpRequest demonstriert. Dieses Beispiel soll veranschaulichen, wie Klassen effektiv für die Erstellung von HTTP-Anfragen und die Bearbeitung von Antworten, sowohl bei Erfolg als auch bei Fehler, verwendet werden können, und so ein detailliertes und vollständiges Verständnis der Funktionsweise des Codes vermitteln.

//+------------------------------------------------------------------+
//|                                                         test.mq5 |
//|                                    Copyright 2023, Lejjo Digital |
//|                           https://www.mql5.com/en/users/14134597 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Lejjo Digital"
#property link      "https://www.mql5.com/de/users/14134597"
#property version   "1.00"

#include "Requests.mqh"


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void TestProcessSuccessResponse()
  {
   HttpResponseProcessor processor;
   string output;
   uchar result[];

// Simulate a success report (example with JSON)
   string mockResponse = "{\"status\": \"success\", \"data\": \"Sample data\"}";
   StringToCharArray(mockResponse, result);

// Call ProcessSuccessResponse
   processor.ProcessSuccessResponse(output, result);

// Check that the output is as expected
   Print("Success Test: ", output);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void TestProcessErrorResponse()
  {
   HttpResponseProcessor processor;
   string output;
   uchar result[];

// Simulate an error response (example with error 404)
   string mockResponse = "404 Not Found";
   StringToCharArray(mockResponse, result);

// Call ProcessErrorResponse with a simulated error code
   processor.ProcessErrorResponse(404, output, result);

// Verify that the output is as expected
   Print("Error Test: ", output);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void TestDetectAndSkipBOM()
  {
   HttpResponseProcessor processor;
   uchar result[6] = {0xEF, 0xBB, 0xBF, 'a', 'b', 'c'}; // 'abc' with BOM UTF-8

// Call DetectAndSkipBOM
   int startIndex = processor.DetectAndSkipBOM(result, ArraySize(result));

// Check if the start index is correct
   Print("Índice de início após BOM: ", startIndex); // Expected: 3
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void RunTests()
  {
   TestProcessSuccessResponse();
   TestProcessErrorResponse();
   TestDetectAndSkipBOM();
  }


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
// Run HttpResponseProcessor tests
   RunTests();

// Create the HttpRequest class instance
   HttpRequest httpRequest("", 5000);

// Variables to store the output
   string output;

// Perform the GET request
   int responseCode = httpRequest.Request("GET", output, "https://jsonplaceholder.typicode.com/posts/1");

// Show the result
   Print("Response Code: ", responseCode);
   Print("Output: ", output);


//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+


Schlussfolgerung

Wir sind am Ende dieses Artikels angelangt, in dem wir uns mit der Umwandlung eines „traditionellen“ Projekts in ein objektorientiertes Projekt befasst haben. Der Wechsel von einer prozeduralen Codestruktur zu einer klassenbasierten Architektur sorgt nicht nur für eine sauberere Organisation, sondern macht den Code auch einfacher zu pflegen und zu erweitern.

Relevanz von OOP in MQL5:

  • Die Einführung des OOP-Paradigmas stellt einen bedeutenden Quantensprung in der Softwareentwicklung dar. In unserem Kontext, in dem MQL5 hauptsächlich für den algorithmischen Handel und die Automatisierung von Finanzmarktstrategien verwendet wird, ist die Bedeutung von gut strukturiertem und modularem Code noch größer.

Vorteile von Modularität und Kapselung:

  • Die Organisation des Codes in Klassen ermöglicht es uns, bestimmte Funktionen zu kapseln, um das System intuitiver und leichter wartbar zu machen. Jede Klasse wird zu einem Modul mit spezifischen Zuständigkeiten, wodurch es einfacher wird, Probleme zu erkennen und zu lösen und das System um neue Funktionen zu erweitern.

Vorteile der Wiederverwendung von Code:

  • OOP fördert die Wiederverwendung von Code. Durch die Erstellung wohldefinierter Klassen können Sie diese Strukturen in verschiedenen Teilen des Projekts oder sogar in anderen Projekten wiederverwenden. Das spart nicht nur Zeit, sondern verbessert auch die Konsistenz und Zuverlässigkeit des Codes.

Wartungsfreundlichkeit und Skalierbarkeit:

  • Die Wartung und Skalierung eines Projekts wird mit OOP wesentlich praktikabler. Wenn Ihr Projekt wächst oder sich an neue Anforderungen anpasst, ist die Möglichkeit, eine bestimmte Komponente zu ändern, ohne den Rest des Systems zu beeinträchtigen, ein unschätzbarer Vorteil.

Ich ermutige alle Leser, unabhängig von ihrer Programmiererfahrung, die OOP-Konzepte in ihren MQL5-Projekten anzuwenden. Die Umstellung auf OOP mag anfangs eine Herausforderung sein, aber die langfristigen Vorteile in Bezug auf Codequalität, Entwicklungseffizienz und Wartbarkeit sind unbestreitbar.

Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/13863

Neuronale Netze leicht gemacht (Teil 72): Entwicklungsvorhersage in verrauschten Umgebungen Neuronale Netze leicht gemacht (Teil 72): Entwicklungsvorhersage in verrauschten Umgebungen
Die Qualität der Vorhersage zukünftiger Zustände spielt eine wichtige Rolle bei der Methode des Goal-Conditioned Predictive Coding, die wir im vorherigen Artikel besprochen haben. In diesem Artikel möchte ich Ihnen einen Algorithmus vorstellen, der die Vorhersagequalität in stochastischen Umgebungen, wie z. B. den Finanzmärkten, erheblich verbessern kann.
Entwicklung eines Replay Systems (Teil 41): Beginn der zweiten Phase (II) Entwicklung eines Replay Systems (Teil 41): Beginn der zweiten Phase (II)
Wenn Ihnen bis zu diesem Punkt alles richtig erschien, bedeutet dies, dass Sie bei der Entwicklung von Anwendungen nicht wirklich an die langfristige Perspektive denken. Im Laufe der Zeit müssen Sie keine neuen Anwendungen mehr programmieren, sondern nur noch dafür sorgen, dass sie zusammenarbeiten. Schauen wir uns also an, wie man den Mauszeiger fertigstellt.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Entwicklung eines Replay Systems (Teil 40): Beginn der zweiten Phase (I) Entwicklung eines Replay Systems (Teil 40): Beginn der zweiten Phase (I)
Heute werden wir über die neue Phase des Replay/Simulator-Systems sprechen. In dieser Phase wird das Gespräch wirklich interessant und sehr inhaltsreich. Ich empfehle Ihnen dringend, den Artikel sorgfältig zu lesen und die darin enthaltenen Links zu nutzen. Dies wird Ihnen helfen, den Inhalt besser zu verstehen.