English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
MQL para "Principiantes": como projetar e construir classes de objeto

MQL para "Principiantes": como projetar e construir classes de objeto

MetaTrader 5Experts | 2 janeiro 2014, 07:04
4 274 0
Sergey Pavlov
Sergey Pavlov

Introdução à programação orientada a objeto (OOP)

Perguntas de "iniciantes": tendo apenas o mais vago entendimento de programação procedural, é possível dominar o OPP e usá-lo para escrever estratégias de negociação automatizadas? Ou esta tarefa está muito além de um usuário comum?

Em geral, é possível usar a linguagem de programação orientada a objeto para escrever Expert Advisor ou indicador, sem o uso dos princípios de programação orientada o objeto. O uso de novas tecnologias em seus desenvolvimentos não é obrigatório. Escolha a maneira que acredita ser a mais simples. Além disso, a aplicação de OOP, além do mais, pode não garantir a rentabilidade de robôs negociadores que você criar.

No entanto, a transição para uma nova abordagem (orientada ao objeto), abre os terrenos para aplicar modelos matemáticos de estratégias de negociação mais complexas e adaptativas aos Aconselhamentos de Especialista, que reagirão a mudanças externas e sincronizarão com o mercado.

Então vamos dar uma olhada nas tecnologias nas quais o OOP se baseia:

  1. Eventos
  2. Classes de objeto

Os eventos são a base principal do OOP. Toda a lógica do programa é construída no processamento dos constantes eventos entrantes. As reações apropriadas a eles são definidas e descritas nas classes de objeto. Em outras palavras, um objeto de classe funciona interceptando e processando o fluxo de eventos.

A segunda base é a classe de objetos, que por sua vez repousa nos "três pilares":

  1. Encapsulamento - Proteção de classe com base em um princípio de "caixa preta": o objeto reage a eventos, mas sua real implementação permanece desconhecida.
  2. Sucessão - a possibilidade de criar uma nova classe a partir de uma existente, enquanto se preserva todas as propriedades e métodos da classe "antepassada".
  3. Polimorfismo - a habilidade de alterar a implementação de um método herdado de uma classe "descendente".

Os conceitos básicos são melhor demonstrados no código de Aconselhamento de Especialista.

Eventos:

//+------------------------------------------------------------------+
//| 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 de objeto:

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);
  };

Encapsulamento:

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

Sucessão:

class CNew: public CObject

Polimorfismo:

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

O modificador virtual deste método significa que o manipulador do OnEvent pode ser sobreposto, mas o nome do método neste caso permanece o mesmo que o da classe antecessora.

2. Projetando classes

Umas das vantagens mais significativas do OOP é que ela é extensível - que significa que o sistema existente é capaz de trabalhar com novos componentes, sem fazer quaisquer alterações a ele. Os novos componentes podem ser acrescentados neste estágio.

Leve em consideração o processo do desenvolvimento criando um programa de desenho visual de classes MasterWindows para MQL5.

2.1. Estágio I: rascunho do projeto

O processo de desenvolvimento começa com um esboço, feito a lápis em uma folha de papel. Este é um dos momentos mais desafiadores e empolgantes na programação. Devemos considerar não apenas o diálogo entre o programa e o usuário (a interface), mas também a organização do processamento de dados. Este processo pode levar mais de um dia. É melhor iniciar com a interface, pois ela pode tornar-se (em alguns casos, como em nosso exemplo) definidora ao estruturar um algoritmo.

Para a organização do diálogo do programa criado, utilizaremos o formulário, de forma semelhante à janela do aplicativo do Windows (ver esboço na Figura 1). Ela contém linhas e estas, por sua vez, consistem de células e células dos objetivos gráficos. E então, já no estágio do desenvolvimento conceitual, começamos a ver a estrutura do programa e a classificação de objetos.

Figura 1. Formulário do construtor de classes (esboço)

Figura 1. Formulário do construtor de classes (esboço)

Com um número suficientemente grande de fileiras e células (campos) no formulário, eles são construídos a partir de apenas dois tipos de objetos gráficos: OBJ_EDIT e OBJ_BUTTON. Assim, uma vez que determinarmos a aparência visual, a estrutura e os objetos básicos criados pelo programa, podemos presumir que o rascunho do desenho está pronto e é hora de seguir para o próximo estágio.

2.2 Estágio II: desenvolvendo a classe base

Existem três classes como esta até o momento, e podem ser acrescentadas outras mais tarde (se necessário):

  • célula de classe CCelll;
  • fileira de classe CRow, consiste de célula da classe CCell;
  • janela de classe CWin, consiste em linhas da classe CRow.

Agora podemos seguir diretamente para as classes de programação, mas... ainda temos que resolver uma tarefa importante - a troca de dados entre os objetos de classe. Para tal finalidade, a linguagem de MQL5 contém, além das variáveis habituais, um novo tipo - estrutura. É claro, neste estágio de desenvolvimento, não podemos ver todas as conexões e é difícil calculá-las. Portanto, preencheremos a descrição de classes e estruturas de maneira gradual, conforme o projeto progride. Além do mais, os princípios do OOP não apenas não impedem isto, como, pelo contrário - incentivam a tecnologia ou a programação.

Estrutura 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)
  };

As estruturas que não contenham sequências de caracteres e objetos de matrizes dinâmicas são chamadas de estrutura simples. As variáveis de tais estruturas podem ser copiadas livremente umas nas outras, mesmo que sejam estruturas diferentes. A estrutura estabelecida é exatamente deste tipo. Avaliaremos sua efetividade mais tarde.

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);
  };

Explicações e recomendações:

  • Todas as classes base (neste projeto) contém métodos de processamento de eventos. Eles são exigidos para interceptar e transmitir eventos mais ao longo da cadeira. Sem um mecanismo para receber e enviar eventos, o programa (ou módulo) perde sua interatividade.
  • Ao desenvolver uma classe base, tente construí-la com um número mínimo de métodos. Em seguida, implemente várias extensões desta classe nas classes "descendentes", que impulsionará a funcionalidade dos objetos criados.
  • Não utilize um apelo direto para os dados internos de uma outra classe!

2.3. Estágio III: projeto de trabalho

Neste ponto, começamos a criação passo a passo do programa. Iniciando com a estrutura de suporte, aumentaremos seus componentes funcionais e o preencheremos com conteúdos. Durante isto, monitoraremos a correção do trabalho, aplicaremos depuração com um código otimizado e rastrearemos os erros que forem aparecendo.

Vamos parar aqui e analisar a tecnologia de criação da estrutura, que funcionará para quase qualquer programa. A exigência principal para ela - ela deve ser imediatamente operacional (compilar sem erros e funcionar na execução). Os desenvolvedores cuidaram da linguagem e aconselham a utilizar o modelo do Aconselhamento de Especialista, que é gerado pelo Assistente do MQL5, como uma estrutura.

Como um exemplo, vamos levar em consideração nossa própria versão deste modelo:

1) Programa = 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 
//--- 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);
  }

Este é o código completado do Aconselhamento de Especialista. Nenhuma alteração adicional precisar ser acrescentada ao longo do projeto!

2) O módulo principal = classe

Todos os módulos principais e auxiliares do projeto começarão seu desenvolvimento a partir daqui. Esta abordagem facilita a programação de projetos multi-modulares complexos e facilita a busca por possíveis erros. Mas, encontrá-los é muito difícil. Às vezes é mais fácil e mais rápido escrever um novo projeto do que procurar pelos ardilosos "bugs".

//+------------------------------------------------------------------+
//|                                           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);
  };

Abaixo se encontra uma descrição inicial bruta dos principais métodos da 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) A biblioteca de classes básicas e derivadas

A biblioteca pode conter qualquer número de classes derivadas e é melhor agrupá-las em arquivos separados, que estão inclusos ao longo da classe base (se houver uma). Desta forma, será mais fácil fazer as alterações e adições necessárias, assim como procurar erros.

E então, temos a estrutura do programa. Vamos testá-la e ver se ela funciona corretamente: compilar e executar. Se o teste for bem sucedido, então podemos começar a preencher o projeto com módulos adicionais.

Vamos começar com a conexão das classes derivadas e iniciar com as células:

Nome da classe
Imagem
Classe CCellText

Classe CCellEdit

Classe CCellButton

Classe CCellButtonType

Tabela 1. Biblioteca de classes de células

Vamos dar uma olhada detalhada na criação de uma classe derivada única do CCellButtonType. Esta classe cria botões de vários tipos.

//+------------------------------------------------------------------+
//| 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
  }
//+------------------------------------------------------------------+

Explicações necessárias:

  • Introduzimos uma proibição no processamento de eventos dentro do construtor de classe. Isto é necessário para preparar o objeto para o trabalho e eliminar as distrações de eventos entrantes. Mediante a conclusão de todas as operações necessárias, permitiremos tal processamento e o objeto começará a funcionar de forma completa.
  • O método de desenho utiliza dados internos e recebe dados externos. Portanto, os dados devem ser primeiramente testados quanto ao cumprimento e apenas então ser processados para evitar situações excepcionais. Mas não executaremos este teste neste caso em particular. Por quê? Imagine que um objeto de classe seja um soldado e soldados não necessariamente precisam saber os planos dos generais. O trabalho deles é seguir as ordens de seus comandantes de forma clara, rápida e rigorosa, ao invés de analisar os comandos recebidos e tomar decisões independentes. Portanto, todos os dados externos devem ser compilados antes de iniciarmos o trabalho com a classe deles.

Agora devemos testar toda a biblioteca de células. Para fazer isto, vamos inserir o seguinte código dentro do módulo principal (temporariamente para efeitos de teste) e executar o Conselheiro Especialista.

//--- include file with classes
#include 
//+------------------------------------------------------------------+
//| 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 não devemos esquecer de transferir os eventos para as classes resultantes! Se isto não for feito, o manuseio dos projetos podem se tornar muito difícil ou até mesmo impossível.

//+------------------------------------------------------------------+
//| 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);
     }
  }

Como resultado, vemos todas as opções disponíveis para objetos da biblioteca de classes de células.

Figura 2. Biblioteca de classes de células

Figura 2. Biblioteca de classes de células

Vamos testar a eficiência e as respostas dos objetos aos eventos:

  • Entramos no campo de edição de diferentes variáveis, ao invés de "por padrão". Se os valores variarem, então o teste foi bem sucedido.
  • Pressionamos os botões, eles permanecem no estado pressionado até que sejam pressionados novamente. Isto não é, contudo, uma reação satisfatória. Precisamos que o botão retorne ao seu estado original automaticamente depois que o pressionamos uma vez. E aqui é quando podemos demonstrar o poder do OOP - a possibilidade da sucessão. O nosso programa pode estar utilizando mais que uma dúzia de botões e não é necessário acrescentar a funcionalidade desejada para cada um deles separadamente. É suficiente alterar a classe base CCell e todos os objetos das classes derivadas miraculosamente começarão a funcionar corretamente!
//+------------------------------------------------------------------+
//| 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();
           }
        }
     }
  }

Assim, a biblioteca de células classe é testada e vinculada ao projeto.

O próximo passo é acrescentar uma biblioteca de linhas:

Nome da classe
Imagem
Classe CRowType1 (0)

Classe CRowType1 (1)

Classe CRowType1 (2)

Classe CRowType1 (3)

Classe CRowType2

Classe CRowType3

Classe CRowType4

Classe CRowType5

Classe CRowType6

Tabela 2. Biblioteca de classes de linhas

e testamos da mesma maneira. Depois de todos os testes, prosseguimos para o próximo estágio.

2.4 Estágio IV: construindo o projeto

Neste ponto, todos os módulos necessários foram criados e testados. Agora prosseguimos para a construção do projeto. Primeiro, criamos uma cascata: o formato da janela conforme a Figura 1 e a preenchemos com funcionalidade, por exemplo, reações programadas de todos os elementos e módulos para eventos entrantes.

Para fazer isto, temos um quadro pronto do programa e a preparação do módulo principal. Vamos começar com isto. É uma das classes "descendentes", da classe de base CWin, portanto, todos os métodos públicos e campos da classe "antecessora" foram transferidos a ele por sucessão. Então, precisamos simplesmente anular alguns métodos e uma nova classe CMasterWindows está pronta:

//--- include files with classes
#include 
#include 
#include 
//+------------------------------------------------------------------+
//| 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);
     }
  }

Por si só, o módulo principal é bem pequeno, já que é responsável por nada além da criação da janela do aplicativo. A seguir, passa o controle ao módulo executável WinEXE, no qual acontece a coisa mais interessante - a reação aos eventos entrantes.

Anteriormente, criamos uma simples estrutura WinCell para a troca de dados entre objetos e agora, todas as vantagens desta abordagem se tornam claras. O processo de cópia de todos os membros da estrutura é muito racional e compacto:

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

Neste estágio, podemos finalizar a consideração detalhada do desenho da classe e seguir para a tecnologia visual de suas construções, o que aumenta consideravelmente a velocidade do processo de criar novas classes.

3. Desenho visual de classes

Uma classe pode ser construída muito mais rapidamente e pode ser visualizada mais facilmente no modo de desenho visual MasterWindows para MQL5:


Figura 3. O processo de desenho visual

Figura 3. O processo de desenho visual

Tudo o que é exigido do desenvolvedor é desenhar o formulário da janela, utilizando os meios do formulário MasterWindows e, em seguida, simplesmente determinar a reação do evento planejado. O código por si só é criado automaticamente. E é isto! O projeto está concluído.

Um exemplo de um código gerado da classe CMasterWindows, assim como um Aconselhamento de Especialista, é mostrado na Figura 4 (um arquivo é criado na pasta ...\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 
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);
  }

Com o lançamento deste, veremos a seguinte janela desenhada:

Figure 4. Aconselhamento de projeto1 - o resultado do desenho visual das classes

Figura 4. Aconselhamento de Especialista projeto 1 - o resultado do desenho visual das classes

Conclusão

  1. As classes precisam ser desenhadas estágio por estágio. Desmembrando a tarefa em módulos, uma classe separada é criada para cada um deles. Os módulos, por sua vez, são desmembrados em micro-módulos de classes de base derivadas.
  2. Tente não sobrecarregar as classes de base com métodos integrados - o número destes deve ser mantidos ao mínimo.
  3. O desenho de classes com o uso de ambiente de desenho visual é muito simples, mesmo para um "iniciante", pois o código é gerado automaticamente.

Localização de anexos:

  • masterwindows.mq5 - ...\MQL5\Experts\
  • permanecendo na pasta - ...\MQL5\Include\

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/53

Arquivos anexados |
Algoritmos genéticos - é fácil! Algoritmos genéticos - é fácil!
Neste artigo o autor fala sobre cálculos evolutivos com o uso de um algoritmo genético desenvolvido pessoalmente. Ele demonstra o funcionamento do algoritmo, usando exemplos e fornece recomendações práticas para seu uso.
Criando um indicador com buffers de indicador múltiplos para iniciantes Criando um indicador com buffers de indicador múltiplos para iniciantes
Os códigos complexos consistem em um conjunto de códigos simples. Se você está familiarizado com eles, não parece tão complicado. Neste artigo, considerarei como criar um indicador com múltiplos buffers de indicador. Como exemplo, o indicador Aroon é analisado detalhadamente, e duas versões diferentes do código são apresentadas.
Um exemplo de uma estratégia de negociação baseada na diferença de fuso horário em diferentes continentes Um exemplo de uma estratégia de negociação baseada na diferença de fuso horário em diferentes continentes
Navegando na internet é fácil encontrar muitas estratégias que darão a você uma série de recomendações. Vamos pegar uma abordagem interna e ver o processo de criação de estratégia, com base nas diferenças de fusos horários em diferentes continentes.
Interação entre o MetaTrader 5 e MATLAB Interação entre o MetaTrader 5 e MATLAB
Este artigo cobre os detalhes da interação entre o MetaTrader 5 e o pacote matemático MatLab. Ele mostra o mecanismo da conversão de dados, o processo de desenvolvimento de uma biblioteca universal para interagir com o desktop MatLab. Ele também cobre o uso do DLL gerado pelo ambiente MatLab. Este artigo é destinado a leitores experientes que conhecem C++ e MQL5.