English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
MQL per "Duri di Comprendonio": Come Progettare e Costruire Classi di Oggetti

MQL per "Duri di Comprendonio": Come Progettare e Costruire Classi di Oggetti

MetaTrader 5Expert Advisors | 16 dicembre 2021, 10:04
224 0
Sergey Pavlov
Sergey Pavlov

Introduzione alla Programmazione Orientata agli Oggetti (OOP)

Domanda di "Duri di Comprendonio": Avendo solo una vaga comprensione della programmazione procedurale, è possibile padroneggiare l'OOP e utilizzarlo per scrivere strategie di trading automatizzato? O questo compito va oltre un utente comune?

In generale, è possibile utilizzare il linguaggio di programmazione orientata agli oggetti per scrivere un Expert Advisor o un indicatore MQL5, senza l'uso dei principi di programmazione orientata agli oggetti. L'uso di nuove tecnologie nei tuoi sviluppi non è obbligatorio. Scegli il modo che ritieni sia il più semplice. Inoltre, l'applicazione dell'OOP in più non può garantire la redditività dei robot di trading che crei.

Tuttavia, il passaggio a un nuovo approccio (orientato agli oggetti), apre le basi per l'applicazione di modelli matematici adattivi più complessi delle strategie di trading ai propri Expert Advisor, che reagiranno ai cambiamenti esterni e si sincronizzeranno con il mercato.

Quindi diamo un'occhiata alle tecnologie su cui si basa l'OOP:

  1. Eventi
  2. Classi di oggetti

Gli eventi sono la base principale dell'OOP. L'intera logica del programma si basa sull'elaborazione degli eventi costantemente in arrivo. Le reazioni appropriate ad essi sono definite e descritte nelle classi di oggetti. In altre parole, un oggetto classe funziona intercettando ed elaborando il flusso degli eventi.

La seconda base è la classe degli oggetti, che a sua volta si poggia su "tre pilastri":

  1. Incapsulamento - Protezione della classe basata sul principio della "scatola nera": l'oggetto reagisce agli eventi, ma la sua effettiva implementazione rimane sconosciuta. 
  2. Ereditarietà - la possibilità di creare una nuova classe da una esistente, preservando tutte le proprietà e i metodi della classe "antenata".
  3. Polimorfismo - la capacità di modificare l'implementazione di un metodo ereditato in una classe "discendente".

I concetti di base illustrati meglio nel codice Expert Advisor.

Eventi

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()                        // OnInit event processing
  {
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)     // OnDeInit event processing
  {
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()                       // OnTick event processing
  {
  }
//+------------------------------------------------------------------+
//| Expert Timer function                                            |
//+------------------------------------------------------------------+
void OnTimer()                      // OnTimer event processing
  {
  }
//+------------------------------------------------------------------+
//| Expert Chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,     // OnChartEvent event processing
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
  }

Classe Oggetto:

class CNew:public CObject
  {
private:
   int               X,Y;
   void              EditXY();
protected:
   bool              on_event;      //events processing flag
public:
   // Class constructor
   void              CNew();
   // OnChart event processing method
   virtual void      OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Incapsulamento:

private:
   int               X,Y;
   void              EditXY();

Ereditarietà

class CNew: public CObject

Polimorfismo

   // OnChart event processing method
   virtual void      OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);

Il modificatore virtuale di questo metodo significa che l’handler OnEvent può essere sovrascritto, ma il nome del metodo in questo caso rimane lo stesso della classe antenata.

2. Classi di Progettazione

Uno dei vantaggi più significativi dell'OOP è la sua estendibilità, il che significa che il sistema esistente è in grado di lavorare con nuovi componenti, senza apportare modifiche. In questa fase è possibile aggiungere nuovi componenti.

Considera il processo di progettazione creando un programma di visual design delle classi MasterWindows per MQL5.

2.1. I fase: Bozza di Progetto

Il processo di progettazione inizia con uno schizzo, disegnato a matita su un foglio di carta. Questo è uno dei momenti più impegnativi ed emozionanti della programmazione. Dobbiamo considerare non solo il dialogo tra il programma e l'utente (l'interfaccia), ma anche l'organizzazione dell'elaborazione dei dati. Questo processo potrebbe richiedere più di un giorno. È meglio iniziare con l'interfaccia, perché può diventare (in alcuni casi, come nel nostro esempio) determinante durante la strutturazione di un algoritmo.

Per l'organizzazione del dialogo del programma creato, utilizzeremo il form, simile alla finestra dell'applicazione Windows (vedi schizzo nella Figura 1). Contiene righe, e queste a loro volta sono costituite da celle e celle degli oggetti grafici. E così, già nella fase di progettazione concettuale, cominciamo a vedere la struttura del programma e la classificazione degli oggetti.

Figura 1. Forma del costruttore delle classi (schizzo)

Figure 1. Forma del costruttore di classi (schizzo)

Con un numero sufficientemente grande di righe e celle (campi) nel modulo, vengono costruiti solo da due tipi di oggetti grafici: OBJ_EDIT e OBJ_BUTTON. Pertanto, una volta determinato l'aspetto visivo, la struttura e gli oggetti di base creati dal programma, possiamo presumere che la bozza del progetto sia pronta ed è ora di passare alla fase successiva.

2.2 Fase II: Progettare la Classe Base

Finora esistono tre classi di questo tipo e se ne possono aggiungere altre in seguito (se necessario):

  • cella di classe CCell;
  • riga di classe CRow, costituita da celle di classe CCell;
  • finestra di classe CWin, è costituita da righe di classe CRow.

Ora possiamo procedere direttamente alla programmazione delle classi, ma ... dobbiamo ancora risolvere un compito molto importante: lo scambio di dati tra oggetti di classi. A tal fine, il linguaggio di MQL5 contiene, oltre alle solite variabili, una nuova struttura tipo. Naturalmente, in questa fase della progettazione, non possiamo vedere tutte le connessioni ed è difficile calcolarle. Pertanto, riempiremo gradualmente la descrizione delle classi e delle strutture man mano che il progetto avanza. Inoltre, i principi dell'OOP non solo non lo ostacolano, ma di fatto il contrario: incoraggiano la tecnologia o la programmazione.

Struttura WinCell:

struct WinCell
  {
   color             TextColor;     // text color
   color             BGColor;       // background color
   color             BGEditColor;   // background color while editing
   ENUM_BASE_CORNER  Corner;         // anchor corner
   int               H;            // cell height
   int               Corn;         // displacement direction (1;-1)
  };

Le strutture che non contengono stringhe e oggetti di array dinamici sono chiamate strutture semplici. Le variabili di tali strutture possono essere liberamente copiate l'una nell'altra, anche se si tratta di strutture diverse. La struttura stabilita è proprio di questo tipo. Valuteremo la sua efficacia in seguito.

Classe base CCell:

//+------------------------------------------------------------------+
//| CCell base class                                                 |
//+------------------------------------------------------------------+
class CCell
  {
private:
protected:
   bool              on_event;      // event processing flag
   ENUM_OBJECT       type;           // cell type
public:
   WinCell           Property;     // cell property
   string            name;          // cell name
   //+---------------------------------------------------------------+
   // Class constructor
   void              CCell();
   virtual     // Draw method
   void              Draw(string m_name,
                          int m_xdelta,
                          int m_ydelta,
                          int m_bsize);
   virtual     // Event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Classe base CRow:

//+------------------------------------------------------------------+
//| CRow base class                                                  |
//+------------------------------------------------------------------+
class CRow
  {
protected:
   bool              on_event;      // event processing flag
public:
   string            name;          // row name
   WinCell           Property;     // row property
   //+---------------------------------------------------------------+
   // Class constructor
   void              CRow();
   virtual     // Draw method
   void              Draw(string m_name,
                          int m_xdelta,
                          int m_ydelta,
                          int m_bsize);
   virtual     // Event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Classe base CWin:

//+------------------------------------------------------------------+
//| Base CWin class (WINDOW)                                         |
//+------------------------------------------------------------------+
class CWin
  {
private:
   void              SetXY(int m_corner); //Coordinates
protected:
   bool              on_event;   // event processing flag
public:
   string            name;       // window name
   int               w_corner;   // window corner
   int               w_xdelta;   // vertical delta
   int               w_ydelta;   // horizontal detla
   int               w_xpos;     // X coordinate
   int               w_ypos;     // Y coordinate
   int               w_bsize;    // Window width
   int               w_hsize;    // Window height
   int               w_h_corner; // hide mode corner
   WinCell           Property;   // Property
   //---
   CRowType1         STR1;       // CRowType1
   CRowType2         STR2;       // CRowType2
   CRowType3         STR3;       // CRowType3
   CRowType4         STR4;       // CRowType4
   CRowType5         STR5;       // CRowType5
   CRowType6         STR6;       // CRowType6
   //+---------------------------------------------------------------+
   // Class constructor
   void              CWin();
   // Set window properties
   void              SetWin(string m_name,
                            int m_xdelta,
                            int m_ydelta,
                            int m_bsize,
                            int m_corner);
   virtual     // Draw window method
   void              Draw(int &MMint[][3],
                          string &MMstr[][3],
                          int count);
   virtual     // OnEventTick handler
   void              OnEventTick();
   virtual     // OnChart event handler method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Spiegazioni e raccomandazioni:

  • Tutte le classi base (in questo progetto) contengono metodi di elaborazione degli eventi. Sono necessari per intercettare e trasmettere eventi più avanti lungo la catena. Senza un meccanismo per la ricezione e l'invio di eventi, il programma (o modulo) perde la sua interattività.
  • Quando si sviluppa una classe base, prova a crearla con un numero minimo di metodi. Quindi, implementa varie estensioni di questa classe nelle classi "discendenti" che aumenteranno la funzionalità degli oggetti creati.
  • Non utilizzare un appello diretto ai dati interni di un'altra classe!

2.3. Fase III: Progetto di Lavoro

A questo punto iniziamo la creazione passo dopo passo del programma. Partendo dalla struttura portante, aumenteremo le sue componenti funzionali e la riempiremo di contenuti. Durante questo, controlleremo la correttezza del lavoro, applicheremo il debug con un codice ottimizzato e terremo traccia degli errori che compaiono.

Fermiamoci qui e consideriamo la tecnologia della creazione del framework che funzionerà per quasi tutti i programmi. Il requisito principale per questo - dovrebbe essere immediatamente operativo (compilare senza errori ed eseguire all'esecuzione). I designer del linguaggio si sono occupati di questo e consigliano di utilizzare il modello Expert Advisor, generato dal Wizard MQL5, come framework.

Ad esempio, consideriamo la nostra versione di questo modello:

1) Programma = Expert Advisor

//+------------------------------------------------------------------+
//|                                                MasterWindows.mq5 |
//|                                                 Copyright DC2008 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "DC2008"
#property link      "http://www.mql5.com"
#property version   "1.00"
//--- include files with classes
#include <ClassMasterWindows.mqh>
//--- Main module declaration
CMasterWindows    MasterWin;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Launch of the main module
   MasterWin.Run();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Deinitialization of the main module
   MasterWin.Deinit();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- call OnTick event handler of main module
   MasterWin.OnEventTick();
  }
//+------------------------------------------------------------------+
//| Expert Event function                                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- call OnChartEvent handler of main module
   MasterWin.OnEvent(id,lparam,dparam,sparam);
  }

Questo è il codice completo dell'Expert Advisor. Non è necessario aggiungere ulteriori modifiche durante il progetto!

2) Il modulo principale = classe

Tutti i moduli principali e ausiliari del progetto inizieranno da qui il loro sviluppo. Questo approccio facilita la programmazione di progetti multi-modulari complessi e facilita la ricerca di possibili errori. Ma trovarli è molto difficile. A volte è più facile e veloce scrivere un nuovo progetto piuttosto che cercare i "bug" evasivi.

//+------------------------------------------------------------------+
//|                                           ClassMasterWindows.mqh |
//|                                                 Copyright DC2008 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "DC2008"
#property link      "http://www.mql5.com"
//+------------------------------------------------------------------+
//| Main module: CMasterWindows class                                |
//+------------------------------------------------------------------+
class CMasterWindows
  {
protected:
   bool              on_event;   // event processing flag
public:
   // Class constructor
   void              CMasterWindows();
   // Method of launching the main module (core algorithm)
   void              Run();
   // Deinitialization method
   void              Deinit();
   // OnTick event processing method
   void              OnEventTick();
   // OnChartEvent event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Di seguito è riportata una descrizione iniziale di massima dei principali metodi della classe.

//+------------------------------------------------------------------+
//| CMasterWindows class constructor                                 |
//+------------------------------------------------------------------+
void CMasterWindows::CMasterWindows()
   {
//--- class members initialization
   on_event=false;   // disable events processing
   }
//+------------------------------------------------------------------+
//| Метод запуска главного модуля (основной алгоритм)                |
//+------------------------------------------------------------------+
void CMasterWindows::Run()
   {
//--- Main functional of the class: runs additional modules
   ObjectsDeleteAll(0,0,-1);
   Comment("MasterWindows for MQL5     © DC2008");
//---
   on_event=true;   // enable events processing
   }
//+------------------------------------------------------------------+
//| Deinitialization method                                          |
//+------------------------------------------------------------------+
void CMasterWindows::Deinit()
   {
//--- 
   ObjectsDeleteAll(0,0,-1);
   Comment("");
   }
//+------------------------------------------------------------------+
//| OnTick() event processing method                                 |
//+------------------------------------------------------------------+
void CMasterWindows::OnEventTick()
   {
   if(on_event) // event processing is enabled
     {
     //---
     }
   }
//+------------------------------------------------------------------+
//| OnChartEvent() event processing method                           |
//+------------------------------------------------------------------+
void CMasterWindows::OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   if(on_event) // event processing is enabled
     {
     //---
     }
  }

3) La libreria delle classi base e derivate

La libreria può contenere un numero qualsiasi di classi derivate ed è meglio raggrupparle in file separati che sono inclusi insieme alla classe base (se presente). In questo modo, sarà più facile apportare le modifiche e le aggiunte necessarie, nonché cercare errori.

E così, ora abbiamo la struttura del programma. Proviamolo e vediamo se funziona correttamente: compila ed esegui. Se il test ha esito positivo, possiamo iniziare a riempire il progetto con moduli aggiuntivi.

Iniziamo con la connessione delle classi derivate e iniziamo con le celle:

Nome classe
Immagine
 Class CCellText

 Class CCellEdit

 Classe CCellButton

 Classe CCellButtonType

Tabella1. Libreria di classi di celle

Diamo uno sguardo dettagliato alla creazione di una singola classe derivata di CCellButtonType. Questa classe crea pulsanti di vario tipo.

//+------------------------------------------------------------------+
//| CCellButtonType class                                            |
//+------------------------------------------------------------------+
class CCellButtonType:public CCell
  {
public:
   ///Class constructor
   void              CCellButtonType();
   virtual     ///Draw method
   void              Draw(string m_name,
                          int m_xdelta,
                          int m_ydelta,
                          int m_type);
  };
//+------------------------------------------------------------------+
//| CCellButtonType class constructor                                |
//+------------------------------------------------------------------+
void CCellButtonType::CCellButtonType()
  {
   type=OBJ_BUTTON;
   on_event=false;   //disable events processing
  }
//+------------------------------------------------------------------+
//| CCellButtonType class Draw method                                |
//+------------------------------------------------------------------+
void CCellButtonType::Draw(string m_name,
                           int m_xdelta,
                           int m_ydelta,
                           int m_type)
  {
//--- creating an object with specified name
   if(m_type<=0) m_type=0;
   name=m_name+".Button"+(string)m_type;
   if(ObjectCreate(0,name,type,0,0,0,0,0)==false)
      Print("Function ",__FUNCTION__," error ",GetLastError());
//--- object properties initializartion
   ObjectSetInteger(0,name,OBJPROP_COLOR,Property.TextColor);
   ObjectSetInteger(0,name,OBJPROP_BGCOLOR,Property.BGColor);
   ObjectSetInteger(0,name,OBJPROP_CORNER,Property.Corner);
   ObjectSetInteger(0,name,OBJPROP_XDISTANCE,m_xdelta);
   ObjectSetInteger(0,name,OBJPROP_YDISTANCE,m_ydelta);
   ObjectSetInteger(0,name,OBJPROP_XSIZE,Property.H);
   ObjectSetInteger(0,name,OBJPROP_YSIZE,Property.H);
   ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
   if(m_type==0) // Hide button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(MIN_WIN));
      ObjectSetString(0,name,OBJPROP_FONT,"Webdings");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,12);
     }
   if(m_type==1) // Close button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(CLOSE_WIN));
      ObjectSetString(0,name,OBJPROP_FONT,"Wingdings 2");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
     }
   if(m_type==2) // Return button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(MAX_WIN));
      ObjectSetString(0,name,OBJPROP_FONT,"Webdings");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,12);
     }
   if(m_type==3) // Plus button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,"+");
      ObjectSetString(0,name,OBJPROP_FONT,"Arial");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,10);
     }
   if(m_type==4) // Minus button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,"-");
      ObjectSetString(0,name,OBJPROP_FONT,"Arial");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,13);
     }
   if(m_type==5) // PageUp button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(PAGE_UP));
      ObjectSetString(0,name,OBJPROP_FONT,"Wingdings 3");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
     }
   if(m_type==6) // PageDown button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(PAGE_DOWN));
      ObjectSetString(0,name,OBJPROP_FONT,"Wingdings 3");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
     }
   if(m_type>6) // empty button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,"");
      ObjectSetString(0,name,OBJPROP_FONT,"Arial");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,13);
     }
   on_event=true;   //enable events processing
  }
//+------------------------------------------------------------------+

Spiegazioni necessarie:

  • Introduciamo un divieto di elaborazione degli eventi nel costruttore della classe. Ciò è necessario per preparare l'oggetto per il lavoro ed eliminare le distrazioni degli eventi in arrivo. Al completamento di tutte le operazioni necessarie, consentiremo tale trattamento e l'oggetto inizierà a funzionare pienamente.
  • Il metodo draw utilizza dati interni e riceve dati esterni. Pertanto, i dati dovrebbero essere prima verificati per la conformità e solo successivamente elaborati, al fine di evitare situazioni eccezionali. Ma non eseguiremo questo test in questo caso particolare. Perché? Immagina che l'oggetto della classe sia un soldato e che i soldati non debbano necessariamente conoscere i piani dei generali. Il loro compito è quello di seguire in modo chiaro, rapido e rigoroso gli ordini dei loro comandanti, invece di analizzare i comandi ricevuti e prendere decisioni indipendenti. Pertanto, tutti i dati esterni devono essere rispettati prima di iniziare a lavorare con la sua classe.

Ora dobbiamo testare l'intera libreria di celle. Per fare ciò, inseriremo il seguente codice nel modulo principale (temporaneamente a scopo di test) ed eseguiremo l'Expert Advisor.

//--- include file with classes
#include <ClassUnit.mqh>
//+------------------------------------------------------------------+
//| Main module: CMasterWindows class                                |
//+------------------------------------------------------------------+
class CMasterWindows
  {
protected:
   bool              on_event;   // events processing flag
   WinCell           Property;   // cell property
   CCellText         Text;
   CCellEdit         Edit;
   CCellButton       Button;
   CCellButtonType   ButtonType;
public:
   // Class constructor
   void              CMasterWindows();
   // Main module run method (core algorithm)
   void              Run();
   // Deinitialization method
   void              Deinit();
   // OnTick event processing method
   void              OnEventTick();
   // OnChart event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };
//+------------------------------------------------------------------+
//| Main module run method (core algorithm)                          |
//+------------------------------------------------------------------+
void CMasterWindows::Run()
  {
//--- core algorithm - it launches additional modules
   ObjectsDeleteAll(0,0,-1);
   Comment("MasterWindows for MQL5     © DC2008");
//--- Text field
   Text.Draw("Text",50,50,150,"Text field");
//--- Edit field
   Edit.Draw("Edit",205,50,150,"default value",true);
//--- LARGE BUTTON
   Button.Draw("Button",50,80,200,"LARGE BUTTON");
//--- Hide button
   ButtonType.Draw("type0",50,100,0);
//--- Close button
   ButtonType.Draw("type1",70,100,1);
//--- Return  button
   ButtonType.Draw("type2",90,100,2);
//--- Plus button
   ButtonType.Draw("type3",110,100,3);
//--- Minus button
   ButtonType.Draw("type4",130,100,4);
//--- None button
   ButtonType.Draw("type5",150,100,5);
//--- None button
   ButtonType.Draw("type6",170,100,6);
//--- None button
   ButtonType.Draw("type7",190,100,7);
//---
   on_event=true;   // enable events processing
  }

E non dobbiamo dimenticare di trasferire gli eventi per le classi risultanti! Se ciò non viene fatto, la gestione dei progetti può diventare molto difficile o addirittura impossibile.

//+------------------------------------------------------------------+
//| CMasterWindows class OnChart event processing method             |
//+------------------------------------------------------------------+
void CMasterWindows::OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   if(on_event) // event processing is enabled
     {
      //--- process events for the cell class objects
      Text.OnEvent(id,lparam,dparam,sparam);
      Edit.OnEvent(id,lparam,dparam,sparam);
      Button.OnEvent(id,lparam,dparam,sparam);
      ButtonType.OnEvent(id,lparam,dparam,sparam);
     }
  }

Di conseguenza vediamo tutte le opzioni disponibili per gli oggetti della libreria delle classi di celle.

Figura 2. Libreria di classi di celle

Figura 2. Libreria delle classi di cella

Verifichiamo l'efficienza lavorativa e le risposte degli oggetti agli eventi:

  • Inseriamo nel campo di modifica diverse variabili, invece di "default". Se i valori variano, il test ha avuto esito positivo.
  • Premiamo i pulsanti, rimangono nello stato premuto fino a quando non vengono premuti di nuovo. Tuttavia, questa non è una reazione soddisfacente. Abbiamo bisogno che il pulsante torni automaticamente al suo stato originale, dopo averlo premuto una volta. Ed è qui che possiamo dimostrare il potere dell'OOP - la possibilità dell'ereditarietà. Il nostro programma può utilizzare più di una dozzina di pulsanti e non è necessario aggiungere la funzionalità desiderata per ciascuno di essi separatamente. È sufficiente cambiare la classe base CCell e tutti gli oggetti delle classi derivate inizieranno miracolosamente a funzionare correttamente!
//+------------------------------------------------------------------+
//| CCell class OnChart event processing method                      |
//+------------------------------------------------------------------+
void CCell::OnEvent(const int id,
                    const long &lparam,
                    const double &dparam,
                    const string &sparam)
  {
   if(on_event) // event processing is enabled
     {
      //--- button click event
      if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,".Button",0)>0)
        {
         if(ObjectGetInteger(0,sparam,OBJPROP_STATE)==1)
           {
            //--- if button stays pressed
            Sleep(TIME_SLEEP);
            ObjectSetInteger(0,sparam,OBJPROP_STATE,0);
            ChartRedraw();
           }
        }
     }
  }

Pertanto, la libreria delle celle di classe viene testata e collegata al progetto.

Il prossimo passo è aggiungere una libreria di linee:

 Nome classe
Immagine
 Class CRowType1 (0)

 Class CRowType1 (1)

 Class CRowType1 (2)

 Class CRowType1 (3)

 Class CRowType2

 Class CRowType3

 Class CRowType4

 Class CRowType5

 Class CRowType6

Tabella2. Libreria di classi di riga

e lo testiamo allo stesso modo. Dopo tutti i test si passa alla fase successiva.

2.4 Fase IV: Costruire il Progetto

A questo punto tutti i moduli necessari sono stati creati e testati. Ora procediamo alla costruzione del progetto. Per prima cosa creiamo una cascata: la forma della finestra come in Figura 1 e la riempiamo di funzionalità, cioè reazioni programmate di tutti gli elementi e moduli agli eventi in arrivo.

Per fare ciò, abbiamo un frame pronto del programma e la preparazione del modulo principale. Cominciamo con questo. È una delle classi "discendenti" della classe base CWin, quindi tutti i metodi e i campi pubblici della classe "antenata" gli sono stati trasmessi per via ereditaria. Quindi abbiamo semplicemente bisogno di sovrascrivere alcuni metodi e una nuova classe CMasterWindows è pronta:

//--- include files with classes
#include <ClassWin.mqh>
#include <InitMasterWindows.mqh>
#include <ClassMasterWindowsEXE.mqh>
//+------------------------------------------------------------------+
//| CMasterWindows class                                             |
//+------------------------------------------------------------------+
class CMasterWindows:public CWin
  {
protected:
   CMasterWindowsEXE WinEXE;     // executable module
public:
   void              Run();      // Run method
   void              Deinit();   // Deinitialization method
   virtual                       // OnChart event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };
//+------------------------------------------------------------------+
//| CMasterWindows class deinitialization method                     |
//+------------------------------------------------------------------+
void CMasterWindows::Deinit()
  {
//---(delete all objects)
   ObjectsDeleteAll(0,0,-1);
   Comment("");
  }
//+------------------------------------------------------------------+
//| CMasterWindows class Run method                                  |
//+------------------------------------------------------------------+
void CMasterWindows::Run()
  {
   ObjectsDeleteAll(0,0,-1);
   Comment("MasterWindows for MQL5     © DC2008");
//--- creating designer window and launch executable object
   SetWin("CWin1",1,30,250,CORNER_RIGHT_UPPER);
   Draw(Mint,Mstr,21);
   WinEXE.Init("CWinNew",30,18);
   WinEXE.Run();
  }
//+------------------------------------------------------------------+
//| CMasterWindows class event processing method                     |
//+------------------------------------------------------------------+
void CMasterWindows::OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   if(on_event) // event processing is enabled
     {
      //--- Close button click in the main window
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,"CWin1",0)>=0
         && StringFind(sparam,".Button1",0)>0)
        {
         ExpertRemove();
        }
      //--- OnChart event processing for all objects
      STR1.OnEvent(id,lparam,dparam,sparam);
      STR2.OnEvent(id,lparam,dparam,sparam);
      STR3.OnEvent(id,lparam,dparam,sparam);
      STR4.OnEvent(id,lparam,dparam,sparam);
      STR5.OnEvent(id,lparam,dparam,sparam);
      STR6.OnEvent(id,lparam,dparam,sparam);
      WinEXE.OnEvent(id,lparam,dparam,sparam);
     }
  }

Di per sé, il modulo principale è piuttosto piccolo, poiché non è responsabile di nient'altro che della creazione della finestra dell'applicazione. Successivamente passa il controllo al modulo eseguibile WinEXE, dove avviene la cosa più interessante: la reazione agli eventi in arrivo.

In precedenza, abbiamo creato una semplice struttura WinCell per lo scambio di dati tra oggetti e ora tutti i vantaggi di questo approccio diventano chiari. Il processo di copia di tutti i membri della struttura è molto razionale e compatto:

   STR1.Property = Property;
   STR2.Property = Property;
   STR3.Property = Property;
   STR4.Property = Property;
   STR5.Property = Property;
   STR6.Property = Property;

A questo punto possiamo terminare la considerazione dettagliata del design delle classi e passare alla tecnologia visiva della loro costruzione, che accelera notevolmente il processo di creazione di nuove classi.

3. Visual design delle classi

Una classe può essere costruita molto più velocemente e può essere visualizzata più facilmente, nella modalità di progettazione visiva di MasterWindows per MQL5:


Figura 3. Il processo di visual design

Figura 3. Il processo di visual design

Tutto ciò che è richiesto allo sviluppatore è di disegnare il modulo della finestra, utilizzando i mezzi del modulo MasterWindows e quindi, semplicemente determinare la reazione all'evento pianificato. Il codice stesso viene creato automaticamente. E questo è tutto! Il progetto è completato.

Un esempio di codice generato della classe CMasterWindows, oltre che dell'Expert Advisor, è mostrato nella Figura 4 (viene creato un file nella cartella ...\MQL5\Files):

//****** Project (Expert Advisor): project1.mq5
//+------------------------------------------------------------------+
//|        Code has been generated by MasterWindows Copyright DC2008 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "DC2008"
//--- include files with classes
#include <ClassWin.mqh>
int Mint[][3]=
  {
     {1,0,0},
     {2,100,0},
     {1,100,0},
     {3,100,0},
     {4,100,0},
     {5,100,0},
     {6,100,50},
     {}
  };
string Mstr[][3]=
  {
     {"New window","",""},
     {"NEW1","new1",""},
     {"NEW2","new2",""},
     {"NEW3","new3",""},
     {"NEW4","new4",""},
     {"NEW5","new5",""},
     {"NEW6","new6",""},
     {}
  };
//+------------------------------------------------------------------+
//| CMasterWindows class (main unit)                                 |
//+------------------------------------------------------------------+
class CMasterWindows:public CWin
  {
private:
   long              Y_hide;          // Window shift vertical in hide mode
   long              Y_obj;           // Window shift vertical
   long              H_obj;           // Window shift horizontal
public:
   bool              on_hide;         // HIDE mode flag
   CArrayString      units;           // Main window lines
   void              CMasterWindows() {on_event=false; on_hide=false;}
   void              Run();           // Run method
   void              Hide();          // Hide method
   void              Deinit()         {ObjectsDeleteAll(0,0,-1); Comment("");}
   virtual void      OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };
//+------------------------------------------------------------------+
//| CMasterWindows class Run method                                  |
//+------------------------------------------------------------------+
void CMasterWindows::Run()
  {
   ObjectsDeleteAll(0,0,-1);
   Comment("Code has been generated by MasterWindows for MQL5 © DC2008");
//--- creating main window and launch executable module
   SetWin("project1.Exp",50,100,250,CORNER_LEFT_UPPER);
   Draw(Mint,Mstr,7);
  }
//+------------------------------------------------------------------+
//| CMasterWindows class Hide method                                 |
//+------------------------------------------------------------------+
void CMasterWindows::Hide()
  {
   Y_obj=w_ydelta;
   H_obj=Property.H;
   Y_hide=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,0)-Y_obj-H_obj;;
//---
   if(on_hide==false)
     {
      int n_str=units.Total();
      for(int i=0; i<n_str; i++)
        {
         long y_obj=ObjectGetInteger(0,units.At(i),OBJPROP_YDISTANCE);
         ObjectSetInteger(0,units.At(i),OBJPROP_YDISTANCE,(int)y_obj+(int)Y_hide);
         if(StringFind(units.At(i),".Button0",0)>0)
            ObjectSetString(0,units.At(i),OBJPROP_TEXT,CharToString(MAX_WIN));
        }
     }
   else
     {
      int n_str=units.Total();
      for(int i=0; i<n_str; i++)
        {
         long y_obj=ObjectGetInteger(0,units.At(i),OBJPROP_YDISTANCE);
         ObjectSetInteger(0,units.At(i),OBJPROP_YDISTANCE,(int)y_obj-(int)Y_hide);
         if(StringFind(units.At(i),".Button0",0)>0)
            ObjectSetString(0,units.At(i),OBJPROP_TEXT,CharToString(MIN_WIN));
        }
     }
//---
   ChartRedraw();
   on_hide=!on_hide;
  }
//+------------------------------------------------------------------+
//| CMasterWindows class OnChartEvent event processing method        |
//+------------------------------------------------------------------+
void CMasterWindows::OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   if(on_event // event handling is enabled
      && StringFind(sparam,"project1.Exp",0)>=0)
     {
      //--- call of OnChartEvent handlers
      STR1.OnEvent(id,lparam,dparam,sparam);
      STR2.OnEvent(id,lparam,dparam,sparam);
      STR3.OnEvent(id,lparam,dparam,sparam);
      STR4.OnEvent(id,lparam,dparam,sparam);
      STR5.OnEvent(id,lparam,dparam,sparam);
      STR6.OnEvent(id,lparam,dparam,sparam);
      //--- creating graphic object
      if(id==CHARTEVENT_OBJECT_CREATE)
        {
         if(StringFind(sparam,"project1.Exp",0)>=0) units.Add(sparam);
        }
      //--- edit [NEW1] in Edit STR1
      if(id==CHARTEVENT_OBJECT_ENDEDIT
         && StringFind(sparam,".STR1",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW3] : Plus button STR3
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR3",0)>0
         && StringFind(sparam,".Button3",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW3] : Minus button STR3
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR3",0)>0
         && StringFind(sparam,".Button4",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW4] : Plus button STR4
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR4",0)>0
         && StringFind(sparam,".Button3",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW4] : Minus button STR4
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR4",0)>0
         && StringFind(sparam,".Button4",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW4] : Up button STR4
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR4",0)>0
         && StringFind(sparam,".Button5",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW4] : Down button STR4
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR4",0)>0
         && StringFind(sparam,".Button6",0)>0)
        {
        //--- event processing code
        }
      //--- [new5] button click STR5
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR5",0)>0
         && StringFind(sparam,".Button",0)>0)
        {
        //--- event processing code
        }
      //--- [NEW6] button click STR6
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR6",0)>0
         && StringFind(sparam,"(1)",0)>0)
        {
        //--- event processing code
        }
      //--- [new6] button click STR6
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR6",0)>0
         && StringFind(sparam,"(2)",0)>0)
        {
        //--- event processing code
        }
      //--- button click [] STR6
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR6",0)>0
         && StringFind(sparam,"(3)",0)>0)
        {
        //--- event processing code
        }
      //--- Close button click in the main window
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".Button1",0)>0)
        {
         ExpertRemove();
        }
      //--- Hide button click in the main window
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".Button0",0)>0)
        {
         Hide();
        }
     }
  }
//--- Main module declaration
CMasterWindows MasterWin;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- launch main module
   MasterWin.Run();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- main module deinitialization
   MasterWin.Deinit();
  }
//+------------------------------------------------------------------+
//| Expert Event function                                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- call OnChartEvent event handler
   MasterWin.OnEvent(id,lparam,dparam,sparam);
  }

Con il lancio di quest’ultimo, vedremo la seguente finestra progettata:

Figura 4. Progetto Advisor1 - il risultato del visual design delle classi

Figura 4. Progetto Expert Advisor1 - il risultato del visual design delle classi

Conclusione

  1. Le classi devono essere progettate fase per fase. Scomponendo l'attività in moduli, viene creata una classe separata per ciascuno di essi. I moduli, a loro volta, sono scomposti in micromoduli di classi derivate o di base.
  2. Cerca di non sovraccaricare le classi di base con metodi incorporati: il numero di questi dovrebbe essere ridotto al minimo.
  3. La progettazione delle classi con l'utilizzo dell'ambiente di visual design è molto semplice, anche per un "duro di comprendonio", perché il codice viene generato automaticamente.

Posizione degli allegati:

  • masterwindows.mq5 - ...\MQL5\Experts\
  • rimanendo nella cartella - ...\MQL5\Include\

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/53

File allegati |
Algoritmi Genetici - È Facile! Algoritmi Genetici - È Facile!
In questo articolo, l'autore parla di calcoli evolutivi con l'uso di un algoritmo genetico sviluppato personalmente. Dimostra il funzionamento dell'algoritmo, usando esempi e fornisce consigli pratici per il suo utilizzo.
Creazione di un Indicatore con più Indicatori Buffer per Principianti Creazione di un Indicatore con più Indicatori Buffer per Principianti
I codici complessi sono costituiti da un insieme di codici semplici. Se li conosci, non sembra così complicato. In questo articolo, parleremo di come creare un indicatore con più buffer di indicatori. Ad esempio, l'indicatore Aroon viene analizzato in dettaglio e vengono presentate due diverse versioni del codice.
Un Esempio di Strategia di Trading Basata sulle Differenze di Fuso Orario nei Diversi Continenti Un Esempio di Strategia di Trading Basata sulle Differenze di Fuso Orario nei Diversi Continenti
Navigando in Internet, è facile trovare molte strategie che ti daranno vari consigli. Prendiamo l'approccio di un insider e esaminiamo il processo di creazione della strategia, basato sulle differenze nei fusi orari nei diversi continenti.
Interazione MetaTrader 5 e MATLAB Interazione MetaTrader 5 e MATLAB
In questo articolo si parlerà dei dettagli dell'interazione tra MetaTrader 5 e il pacchetto matematico MatLab. Mostra il meccanismo di conversione dei dati, il processo di sviluppo di una libreria universale per interagire con il desktop MatLab. Copre anche l'uso di DLL generate dall'ambiente MatLab. Questo articolo è destinato a lettori esperti che conoscono C++ e MQL5.