
MQL per "Duri di Comprendonio": Come Progettare e Costruire Classi di Oggetti
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:
- Eventi
- 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":
- Incapsulamento - Protezione della classe basata sul principio della "scatola nera": l'oggetto reagisce agli eventi, ma la sua effettiva implementazione rimane sconosciuta.
- Ereditarietà - la possibilità di creare una nuova classe da una esistente, preservando tutte le proprietà e i metodi della classe "antenata".
- 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.
//+------------------------------------------------------------------+ //| 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) { }
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); };
private: int X,Y; void EditXY();
class CNew: public CObject
// 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.
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 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
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 Expert Advisor1 - il risultato del visual design delle classi
Conclusione
- 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.
- Cerca di non sovraccaricare le classi di base con metodi incorporati: il numero di questi dovrebbe essere ridotto al minimo.
- 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





- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso