MQL para "Principiantes": como projetar e construir classes de objeto
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:
- Eventos
- 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":
- 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.
- 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".
- 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.
//+------------------------------------------------------------------+ //| 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);
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)
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
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
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:
Figura 4. Aconselhamento de Especialista projeto 1 - o resultado do desenho visual das classes
Conclusão
- 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.
- Tente não sobrecarregar as classes de base com métodos integrados - o número destes deve ser mantidos ao mínimo.
- 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
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso