
MQL pour "Nuls" : Comment Concevoir et Construire des Classes d'Objets
Introduction à la Programmation Orientée-Objet (POO)
Question des "nuls": N'ayant qu'une vague compréhension de la programmation procédurale, est-il possible de maîtriser la POO et de l'utiliser pour rédiger des stratégies de trading automatisées ? Ou cette tâche est-elle au-delà d'un utilisateur commun ?
Dans l'ensemble, il est possible d'utiliser le langage de programmation orienté-objet pour écrire un Expert Advisor ou un indicateur MQL5, sans utiliser les principes de programmation orientée objet. L'utilisation de nouvelles technologies dans vos élaborations n'est pas obligatoire. Choisissez la manière qui vous semble la plus simple. De plus, l'application de la POO ne peut donc plus garantir la rentabilité des robots de trading que vous créez.
Cependant, la transition vers une nouvelle approche (orientée objet) ouvre la voie à l'application de modèles mathématiques adaptatifs plus complexes de stratégies de trading à leurs Expert Advisors, qui réagiront aux modifications externes et se synchroniseront avec le marché.
Jetons donc un coup d'œil aux technologies sur lesquelles la POO est basée :
- Événements
- Classes d'objets
Les événements sont la base principale de la POO. Toute la logique du programme repose sur le traitement des événements constamment entrants. Les réactions appropriées à ceux-ci sont définies et décrites dans les classes d'objets. En d'autres termes, un objet de classe fonctionne en interceptant et en traitant le flux d'événements.
La seconde base est la classe d'objets, qui à son tour repose sur les « trois piliers » :
- Encapsulation - Protection de classe basée sur un principe de "boîte noire" : l'objet réagit aux événements, mais son implémentation factuelle reste inconnue.
- Legs - la possibilité de créer une nouvelle classe à partir d'une classe existante, tout en préservant toutes les propriétés et méthodes de la classe "ancêtre".
- Polymorphisme - la possibilité de modifier l'implémentation d'une méthode héritée dans une classe "descendante".
Les concepts de base sont mieux illustrés dans le code 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);
Le modificateur virtuel de cette méthode indique que le gestionnaire OnEvent peut être remplacé, mais le nom de la méthode dans ce cas reste le même que celui de la classe ancêtre.
2. Conception de cours
L'un des avantages clés de la POO est son évolutivité - ce qui indique que le système existant est en mesure de fonctionner avec de nouveaux composants, sans y apporter de modifications. De nouveaux composants peuvent être ajoutés à ce stade.
Examinez le processus de conception en créant un programme de conception visuelle des classes MasterWindows pour MQL5.
2.1. Je mets en scène : Ébauche de projet
Le processus de conception commence par un croquis, dessiné au crayon sur une feuille de papier. C'est l'un des moments les plus stimulants et passionnants de la programmation. Il faut tenir compte non seulement de dialogue entre le programme et l'utilisateur (l'interface), mais aussi l'organisation du traitement des données. Ce processus peut prendre plus d'une journée. Il est préférable de commencer par l'interface, car elle peut devenir (dans certains cas, comme dans notre exemple) déterminante lors de la structuration d'un algorithme.
Pour l'organisation du dialogue du programme créé, nous utiliserons le formulaire, similaire à la fenêtre de l'application Windows (voir schéma de la figure 1). Il comporte des lignes, et celles-ci sont à leur tour constituées de cellules et de cellules des objets graphiques. Et ainsi, dès l'étape du design conceptuel, on commence à voir la structure du programme et la classification des objets.
Figure 1. Forme du constructeur de classes (esquisse)
Avec un nombre assez considérable de lignes et de cellules (champs) dans le formulaire, elles sont construites à partir de seulement deux types d'objets graphiques : OBJ_EDIT and OBJ_BUTTON Ainsi, une fois que nous avons déterminé l'apparence visuelle, la structure et les objets de base créés par le programme, nous pouvons supposer que le projet de conception est prêt et qu'il est temps de passer à l'étape suivante.
2.2 Étape II : Conception de la Classe de Base
Il existe trois classes de ce type jusqu'à présent, et d'autres peuvent être ajoutées plus tard (si nécessaire) :
- cellule de classe CCell;
- classe ligne CRow, se compose de cellules de classe CCell;
- fenêtre de classe CWin, se compose de lignes de classe CRow.
Nous pouvons maintenant passer directement à la programmation des classes, mais ... nous devons encore résoudre une tâche très importante - l'échange de données entre les objets des classes. A ces fins, le langage de MQL5 comporte, outre les variables habituelles, une nouvelle structure type. Bien sûr, à ce stade de la conception, on ne peut pas voir toutes les connexions et il est difficile de les calculer. Par conséquent, nous remplirons progressivement la description des classes et des structures au fur et à mesure de l'avancement du projet. De plus, les principes de la POO non seulement n'entravent pas cela, mais en fait le contraire - encouragent la technologie ou la programmation.
Structure 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) };
Les structures qui ne comportent pas de chaînes et d'objets de tableaux dynamiques sont appelées structure simple. Les variables de telles structures peuvent être librement copiées les unes dans les autres, même s'il s'agit de structures différentes. La structure établie est exactement de ce type. Nous évaluerons plus tard son efficacité.
Classe de 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 de 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 de 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); };
Explications et recommandations :
- Toutes les classes de base (dans ce projet) comportent des méthodes de traitement des événements. elles sont nécessaires pour intercepter et transmettre des événements plus loin dans la chaîne. Sans mécanisme de réception et d'envoi d'événements, le programme (ou module) perd son interactivité.
- Lors de l’élaboration d'une classe de base, essayez de la construire avec un nombre réduit de méthodes. Ensuite, implémentez diverses extensions de cette classe dans les classes « descendantes », ce qui va booster la fonctionnalité des objets créés.
- N'utilisez pas un appel direct aux données internes d'une autre classe !
2.3. Stade III : Projet de travail
À ce stade, nous entamons la création du programme étape par étape En commençant par le cadre de support, nous augmenterons ses composants fonctionnels et le remplirons de contenu. Pendant cela, nous observerons l'exactitude du travail, appliquerons le débogage avec un code optimisé et suivrons les erreurs qui apparaissent.
Arrêtons-nous ici et examinerons la technologie de création du cadre, qui fonctionnera pour presque tous les programmes. La principale exigence pour cela - il doit être immédiatement opérationnel (compiler sans erreurs et faire fonctionner lors de l'exécution). Les concepteurs du langage ont pris soin de cela et conseillent d'utiliser le modèle Expert Advisor, qui est généré par l'assistant MQL5, comme cadre.
À titre d'exemple, examinons notre propre version de ce modèle :
1) Programme = 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); }
Ceci est le code complété de l'Expert Advisor. Aucune modification supplémentaire ne doit être apportée tout au long du projet !
2) Le module principal = classe
Tous les modules principaux et auxiliaires du projet commenceront leur élaboration à partir d'ici. Cette approche facilite la programmation de projets multi-modulaires complexes et facilite la recherche d'éventuelles erreurs. Mais les trouver est très difficile. Parfois, il est plus facile et plus rapide d'écrire un nouveau projet plutôt que de rechercher les "bugs" insaisissables.
//+------------------------------------------------------------------+ //| 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); };
Vous trouverez ci-dessous une première description approximative des principales méthodes de la 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 bibliothèque des classes de base et dérivées
La bibliothèque peut comporter n'importe quel nombre de classes dérivées et il est préférable de les regrouper dans des fichiers séparés, qui sont inclus avec la classe de base (le cas échéant). De cette façon, il sera plus facile d'apporter les modifications et ajouts nécessaires, ainsi que - la recherche d'erreurs.
Et donc, nous avons maintenant le cadre du programme. Testons-le et voyons s'il fonctionne correctement : compilez et exécutez. Si le test est réussi, nous pouvons alors commencer à remplir le projet avec des modules supplémentaires.
Commençons par la connexion des classes dérivées, et démarrons par les cellules :
Classe de nom | Image |
---|---|
Class CCellText | ![]() |
Class CCellEdit | ![]() |
Class CCellButton | ![]() |
Class CCellButtonType | ![]() |
Tableau 1. Bibliothèque de classes de cellules
Examinons en détail la création d'une seule classe dérivée de CCellButtonType. Cette classe crée des boutons de différents types.
//+------------------------------------------------------------------+ //| 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 } //+------------------------------------------------------------------+
Explications nécessaires :
- Nous introduisons une interdiction sur le traitement des événements dans le constructeur de classe. Cela est nécessaire pour préparer l'objet au travail et éliminer les distractions des événements entrants. Une fois toutes les opérations nécessaires terminées, nous autoriserons un tel traitement et l'objet commencera à fonctionner pleinement.
- La méthode draw utilise des données internes et reçoit des données externes. Par conséquent, les données doivent d'abord être testées pour la conformité, et ensuite seulement traitées, afin d'éviter des situations exceptionnelles. Mais nous n'effectuerons pas ce test dans ce cas particulier. Pourquoi Imaginez que l'objet de classe est un soldat, et les soldats n'ont pas nécessairement besoin de connaître les plans des généraux. Leur travail consiste à suivre clairement, rapidement et rigoureusement les ordres de leurs commandants, au lieu d'analyser les ordres reçus et de prendre des décisions indépendantes. Par conséquent, toutes les données externes doivent être acquittées avant de commencer à travailler avec sa classe.
Maintenant, nous devons tester toute la bibliothèque de cellules. Pour se faire, nous allons insérer le code suivant dans le module principal (provisoirement à des fins de test) et exécuter 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 }
Et il ne faut pas oublier de transférer les événements pour les classes résultantes ! Si cela n'est pas fait, la gestion des projets peut devenir très difficile, voire impossible.
//+------------------------------------------------------------------+ //| 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); } }
En conséquence, nous voyons toutes les options disponibles pour les objets de la bibliothèque de classes de cellules.
Figure 2. Bibliothèque de cellule classes
Testons l'efficacité opérationnelle et les réactions des objets aux événements :
- Nous entrons dans le champ d'édition différentes variables, au lieu de "default". Si les valeurs varient, le test a réussi.
- Nous appuyons sur les boutons, ils restent dans l'état enfoncé jusqu'à ce qu'ils soient à nouveau enfoncés. Ce n'est cependant pas une réaction satisfaisante. Nous avons besoin que le bouton revienne automatiquement à son état d'origine, après avoir appuyé une fois dessus. Et c'est là que nous pouvons démontrer la puissance de la POO - la possibilité de legs. Notre programme peut utiliser plus d'une douzaine de boutons et il n'est pas nécessaire d'ajouter la fonctionnalité souhaitée pour chacun d'eux séparément. Il suffit de modifier la classe de base CCell, et tous les objets des classes dérivées commenceront miraculeusement à fonctionner correctement !
//+------------------------------------------------------------------+ //| 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(); } } } }
Ainsi, la bibliothèque de cellules de classe est testée et liée au projet.
L'étape suivante consiste à ajouter une bibliothèque de lignes :
Classe de nom | Image |
---|---|
Classe CRowType1 (0) | ![]() |
Classe CRowType1 (1) | ![]() |
Classe CRowType1 (2) | ![]() |
Classe CRowType1 (3) | ![]() |
Classe CRowType2 | ![]() |
Classe CRowType3 | ![]() |
Classe CRowType4 | ![]() |
Classe CRowType5 | ![]() |
Classe CRowType6 | ![]() |
Tableau2. Bibliothèque de classes de lignes
et nous le testons de la même manière. Après tous les tests, nous passons à l'étape suivante.
2.4 Étape IV : Construire le projet
À ce stade, tous les modules nécessaires ont été créés et testés. Nous procédons maintenant à la construction du projet. Nous créons d'abord une cascade : la forme de la fenêtre comme dans la figure 1 et la remplissons de fonctionnalités, c'est-à-dire des réactions programmées de tous les éléments et modules aux événements entrants.
Pour ce faire, nous avons un cadre prêt du programme et la préparation du module principal. Commençons par ceci. C'est l'une des classes "descendantes", de la classe de base CWin, par conséquent, toutes les méthodes et champs publics de la classe "ancêtre" lui ont été transmis par legs. Par conséquent, nous devons simplement surcharger quelques méthodes et une nouvelle classe CMasterWindows est prête :
//--- 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); } }
En lui-même, le module principal est assez petit, puisqu'il n'est responsable que de la création de la fenêtre de l'application. Ensuite, il passe le contrôle au module exécutable WinEXE, où se déroule la chose la plus intéressante - la réaction aux événements entrants.
Auparavant, nous avons créé une structure WinCell simple pour l'échange de données entre objets, et maintenant, tous les avantages de cette approche deviennent manifestes. Le processus de copie de tous les membres de la structure est très rationnel et compact :
STR1.Property = Property; STR2.Property = Property; STR3.Property = Property; STR4.Property = Property; STR5.Property = Property; STR6.Property = Property;
A ce stade, nous pouvons terminer l'examen détaillé de la conception des classes et passer à la technologie visuelle de leur construction, ce qui accélère considérablement le processus de création de nouvelles classes.
3. Conception visuelle des classes
Une classe peut être construite beaucoup plus rapidement, et peut être visualisée plus facilement, dans le mode de conception visuelle MasterWindows pour MQL5 :
Figure 3. Le processus de conception visuelle
Tout ce qui est requis du développeur - c'est de dessiner le formulaire de la fenêtre, en utilisant les moyens du formulaire MasterWindows, puis de déterminer simplement la réaction à l'événement prévu. Le code lui-même est créé automatiquement. Et c'est tout! Le projet est terminé.
Un exemple de code généré de la classe CMasterWindows, ainsi que de l'Expert Advisor, est illustré à la Figure 4 (un fichier est créé dans le dossier ...\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); }
Avec le lancement de ceci, nous constatons la fenêtre conçue suivante:
Figure 4.4. Expert Advisor project1 - le résultat de la conception visuelle des classes
Conclusion
- Les classes doivent être conçues étape par étape. En décomposant la tâche en modules, une classe distincte est créée pour chacun d'eux. Les modules, à leur tour, sont décomposés en micro-modules de classes dérivées ou de base.
- Essayez de ne pas surcharger les classes de base avec des méthodes intégrées - leur nombre doit être réduit au minimum.
- La conception des classes avec l'utilisation de l'environnement de conception visuelle est très simple, même pour un « nul », car le code est généré automatiquement.
Emplacement des pièces jointes :
- masterwindows.mq5 - ...\MQL5\Experts\
- restant dans le dossier - ...\MQL5\Include\
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/53





- Applications de trading gratuites
- Plus de 8 000 signaux à copier
- Actualités économiques pour explorer les marchés financiers
Vous acceptez la politique du site Web et les conditions d'utilisation