
Cliente no Connexus (Parte 7): Adicionando a camada de cliente
Introdução
Este artigo é a continuação de uma série de textos em que estamos criando uma biblioteca chamada Connexus. No primeiro artigo, entendemos os fundamentos da função WebRequest, analisamos cada um de seus parâmetros e ainda criamos um exemplo de código que demonstrava o uso dessa função e suas dificuldades. No artigo anterior, estudamos o que são métodos http e também os códigos de status retornados pelo servidor, que informam se a requisição foi processada com sucesso ou se ocorreu algum erro gerado pelo cliente ou pelo servidor.
Neste sétimo artigo da série, vamos adicionar a parte mais esperada de toda a biblioteca, vamos fazer uma requisição usando a função WebRequest, não vamos criar o acesso direto a ela, e no processo serão utilizados alguns classes e interfaces. Vamos lá!
Só para lembrar o estado atual da biblioteca, aqui está o esquema atual:
O objetivo aqui é obter um objeto CHttpRequest, ou seja, uma requisição HTTP pronta, já configurada com cabeçalho, corpo, URL, método e tempo limite, e enviar a requisição HTTP de forma eficiente por meio da função WebRequest. Ele também deve processar a requisição e retornar um objeto CHttpResponse com os dados da resposta, como cabeçalho, corpo, código de status e a duração total da requisição.
Criação da classe CHttpClient
Vamos criar a classe mais esperada de todas, a última da biblioteca, que será a classe instanciada e utilizada pelo usuário da biblioteca. Essa classe se chamará CHttpClient.mqh e estará localizada no seguinte caminho: Include/Connexus/Core/CHttpClient.mqh. Inicialmente, o arquivo ficará parecido com este, já adicionei a importação correspondente://+------------------------------------------------------------------+ //| HttpClient.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "HttpRequest.mqh" #include "HttpResponse.mqh" #include "../Constants/HttpMethod.mqh" //+------------------------------------------------------------------+ //| class : CHttpClient | //| | //| [PROPERTY] | //| Name : CHttpClient | //| Heritage : No heritage | //| Description : Class responsible for linking the request and | //| response object with the transport layer. | //| | //+------------------------------------------------------------------+ class CHttpClient { public: CHttpClient(void); ~CHttpClient(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CHttpClient::CHttpClient(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CHttpClient::~CHttpClient(void) { } //+------------------------------------------------------------------+Esta classe tem a função de converter o objeto de requisição, ou seja, o CHttpRequest, em uma requisição HTTP utilizando a função WebRequest e devolver um objeto CHttpResponse com os dados. Vamos criar essa classe. Para isso, vamos criar o método Send(), que deve receber dois objetos, um CHttpRequest e outro CHttpResponse, e deve retornar um valor boolean.
class CHttpClient { public: CHttpClient(void); ~CHttpClient(void); //--- Basis function bool Send(CHttpRequest &request, CHttpResponse &response); };
Vamos trabalhar na implementação dessa função. Primeiro, vamos revisar rapidamente os parâmetros da função WebRequest. Ela possui 2 variações, e inicialmente vamos usar a que tem menos parâmetros:
int WebRequest( const string method, // HTTP method const string url, // URL const string headers, // headers int timeout, // timeout const char &data[], // the array of the HTTP message body char &result[], // an array containing server response data string &result_headers // headers of server response );Já temos todos esses parâmetros prontos e configurados nos objetos recebidos, portanto basta adicioná-los. A implementação ficará assim:
//+------------------------------------------------------------------+ //| Basis function | //+------------------------------------------------------------------+ bool CHttpClient::Send(CHttpRequest &request, CHttpResponse &response) { //--- 1. Request uchar body_request[]; request.Body().GetAsBinary(body_request); //--- 2. Response uchar body_response[]; string headers_response; //--- 3. Send ulong start = GetMicrosecondCount(); int status_code = WebRequest( request.Method().GetMethodDescription(), // HTTP method request.Url().FullUrl(), // URL request.Header().Serialize(), // Headers request.Timeout(), // Timeout body_request, // The array of the HTTP message body body_response, // An array containing server response data headers_response // Headers of server response ); ulong end = GetMicrosecondCount(); //--- 4. Add data in Response response.Clear(); response.Duration((end-start)/1000); response.StatusCode() = status_code; response.Body().AddBinary(body_response); response.Header().Parse(headers_response); //--- 5. Return is success return(response.StatusCode().IsSuccess()); } //+------------------------------------------------------------------+
Adicionei alguns números nos comentários apenas para explicar cada etapa abaixo, e é muito importante que você entenda o que está acontecendo aqui, pois esta é a função central da biblioteca:
- REQUISIÇÃO: Aqui vamos criar um array do tipo uchar chamado body_request. Temos acesso ao corpo da requisição e obtemos esse corpo em formato binário passando o array body_request. Assim, esse array será alterado, recebendo os dados do corpo.
- RESPOSTA: Criamos duas variáveis que serão usadas pela função WebRequest para armazenar o corpo e o cabeçalho da resposta do servidor.
- ENVIAR: Esta é a parte principal de tudo, chamamos a função WebRequest e passamos todos os parâmetros, incluindo URL e timeout, além das variáveis criadas no passo 2 para receber o corpo e o cabeçalho da resposta. Aqui também criamos duas outras variáveis auxiliares que utilizam a função GetMicrosecondCount() para armazenar o tempo antes e depois da requisição. Assim, obtemos o tempo inicial e final para calcular a duração da requisição em milissegundos.
- ADICIONAR DADOS À RESPOSTA: Aqui, após receber a resposta da requisição, independentemente de ter sido bem-sucedida ou não, usamos a função Clear() para reinicializar todos os valores do objeto response e adicionamos a duração usando a fórmula: (fim - início)/1000. Também definimos o código de status retornado pela função e adicionamos o texto e o cabeçalho ao objeto response.
- RETORNO: No último passo, verificamos se a requisição retornou algum código de sucesso (de 200 a 299). Assim podemos saber se a requisição foi executada, e para obter informações mais detalhadas basta verificar o conteúdo do objeto response, ou seja, do CHttpResponse.
Com a criação desta classe, o esquema atualizado ficará parecido com este:
No fim, todas as classes estão dentro de HttpClient. Vamos executar um teste simples.
Primeiro teste
Faremos o primeiro e mais simples teste com tudo o que criamos até agora. Vamos enviar uma requisição GET e uma requisição POST para httpbin.org, que é um serviço online gratuito para testar requisições HTTP. Ele foi criado por kennethreitz, é um projeto OpenSource (link). Já usamos essa API pública em artigos anteriores, mas resumidamente explico que o httpbin funciona como um espelho, ou seja, tudo o que enviamos ao servidor é retornado para o cliente. Isso é muito útil para testar aplicações em que queremos apenas confirmar se a requisição foi executada e quais dados o servidor recebeu.
Passando ao código, vamos criar um novo arquivo na pasta Experts com o nome TestRequest.mq5, o caminho final será Experts/Connexus/Test/TestRequest.mq5. Usaremos apenas a função OnInit do arquivo, então descartaremos as demais. O código ficará assim:
//+------------------------------------------------------------------+ //| TestRequest.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialize return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Importamos a biblioteca, adicionando a importação com #include
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Connexus/Core/HttpClient.mqh>
Agora vamos criar três objetos:
- CHttpRequest: Onde serão definidos o URL e o método HTTP. O tempo limite, o corpo e o cabeçalho são opcionais em requisições GET, o valor padrão do tempo limite é 5000 milissegundos (5 segundos).
- CHttpResponse: Armazena o resultado da requisição com o código de status e a descrição, além da duração da requisição em milissegundos.
- CHttpClient: Classe que de fato executa a requisição
//--- Request object CHttpRequest request; request.Method() = HTTP_METHOD_GET; request.Url().Parse("https://httpbin.org/get"); //--- Response object CHttpResponse response; //--- Client http object CHttpClient client;
E, por fim, chamamos a função Send() para enviar a requisição e exibir os dados no terminal. O código completo fica assim:
//+------------------------------------------------------------------+ //| TestRequest.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Connexus/Core/HttpClient.mqh> //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Request object CHttpRequest request; request.Method() = HTTP_METHOD_GET; request.Url().Parse("https://httpbin.org/get"); //--- Response object CHttpResponse response; //--- Client http object CHttpClient client; client.Send(request,response); Print(response.FormatString()); //--- Initialize return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+Ao executar o código, obtemos o seguinte resultado no terminal:
HTTP Response: --------------- Status Code: HTTP_STATUS_OK [200] - OK Duration: 845 ms --------------- Headers: Date: Fri, 18 Oct 2024 17:52:35 GMT Content-Type: application/json Content-Length: 380 Connection: keep-alive Server: gunicorn/19.9.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true --------------- Body: { "args": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Accept-Language": "pt,en;q=0.5", "Host": "httpbin.org", "User-Agent": "MetaTrader 5 Terminal/5.4620 (Windows NT 11.0.22631; x64)", "X-Amzn-Trace-Id": "Root=1-6712a063-5c19d0e03b85df9903cb0e91" }, "origin": "XXX.XX.XX.XX", "url": "https://httpbin.org/get" } ---------------
Esses são os dados do objeto response; temos o código de status com a descrição, a duração da requisição em milissegundos, o cabeçalho e o texto da resposta. Isso facilita muito a vida dos desenvolvedores ao conectar seus programas MQL5 a APIs externas. Conseguimos tornar a requisição HTTP muito simples e, só para comparar a evolução, lembro como funciona uma requisição sem usar a biblioteca e outra usando-a:
- Sem a biblioteca Connexus:
int OnInit() { //--- Defining variables string method = "GET"; // HTTP verb in string (GET, POST, etc...) string url = "https://httpbin.org/get"; // Destination URL string headers = ""; // Request header int timeout = 5000; // Maximum waiting time 5 seconds char data[]; // Data we will send (body) array of type char char result[]; // Data received as an array of type char string result_headers; // String with response headers string body = "{\"key\":\"value\"}"; StringToCharArray(body,data,0,WHOLE_ARRAY,CP_UTF8); ArrayRemove(data,ArraySize(data)-1); //--- Calling the function and getting the status_code int status_code = WebRequest(method,url,headers,timeout,data,result,result_headers); //--- Print the data Print("Status code: ",status_code); Print("Response: ",CharArrayToString(result)); // We use CharArrayToString to display the response in string form. //--- return(INIT_SUCCEEDED); }
- Com a biblioteca Connexus:
int OnInit() { //--- Request object CHttpRequest request; request.Method() = HTTP_METHOD_GET; request.Url().Parse("https://httpbin.org/get"); request.Body().AddString("{\"key\":\"value\"}"); //--- Response object CHttpResponse response; //--- Client http object CHttpClient client; client.Send(request,response); Print(response.FormatString()); //--- Initialize return(INIT_SUCCEEDED); }
É muito mais fácil trabalhar com a biblioteca, acessar o corpo em vários formatos, como string, json ou binary, e todos os outros recursos disponíveis. Também é possível alterar partes da requisição, como o URL, o corpo e o cabeçalho, e usar o mesmo objeto para executar outra requisição. Isso permite fazer várias requisições em sequência de forma simplificada.
Temos um pequeno problema, ACOPLAMENTO (COUPLING)!
Parece que tudo funciona corretamente, e de fato funciona, mas tudo pode ser melhorado e, aqui, na biblioteca Connexus, não é diferente. Mas, afinal, qual é esse problema? Ao usar a função WebRequest diretamente na classe CHttpClient, obtemos um código muito acoplado, mas o que é um código muito acoplado?
Imagine o seguinte: você está construindo um castelo de cartas. Cada carta precisa estar perfeitamente equilibrada para que tudo permaneça em pé. Agora imagine que, em vez de um castelo leve e flexível, no qual você pode mover uma carta sem derrubar tudo, você tem algo parecido com um bloco de concreto. Se você tirar uma carta ou movê-la, adivinha o que acontece? O castelo inteiro desaba. É exatamente isso que ocorre com um código acoplado.
Quando falamos de acoplamento em programação, é como se partes do seu código estivessem tão presas umas às outras que, ao mover uma, você precisasse mover várias outras também. É como aquele nó apertado que você faz sem querer no cadarço do sapato, e agora é difícil de desfazer sem estragar tudo de vez. Alterar uma função, modificar uma classe ou adicionar algo novo se torna uma dor de cabeça, porque tudo está grudado.
Por exemplo, se você tem uma função que depende fortemente de outra função, e essas funções não conseguem "viver" separadamente, o código está fortemente acoplado. E isso é ruim, porque com o tempo se torna difícil dar manutenção, adicionar novas funcionalidades ou até mesmo depurar sem correr o risco de quebrar algo. É exatamente isso que acontece dentro da classe CHttpClient, onde a função Send() depende diretamente da WebRequest; no estado atual, não é possível separar uma da outra. Em um mundo ideal, precisamos de um código "desacoplado", em que cada parte possa funcionar de forma independente, como peças de Lego: é possível encaixar e remover sem grandes problemas.
Para criar um código menos acoplado, usamos interfaces e injeção de dependência. A ideia é criar uma interface que defina as operações necessárias, permitindo que a classe dependa dessa abstração em vez de uma implementação específica. Dessa forma, o CHttpClient pode interagir com qualquer objeto que implemente a interface, seja o WebRequest ou uma função mock (FakeWebRequest). Vamos entender a ideia de interfaces e injeção de dependência:
- Papel das interfaces: As interfaces permitem definir um contrato que outros classes podem implementar. No nosso caso, podemos criar uma interface chamada IHttpTransport, que definirá os métodos necessários para o CHttpClient executar a requisição HTTP. Assim, o CHttpClient passará a depender da interface IHttpTransport em vez da função WebRequest, o que reduz o acoplamento.
- Injeção de dependência: Para conectar o CHttpClient a uma implementação específica de transporte HTTP, usaremos a injeção de dependência. Esse método consiste em passar a dependência (WebRequest ou FakeWebRequest) como parâmetro para o CHttpClient, em vez de instanciá-la diretamente dentro da classe.
Quais são as vantagens de não deixar o código acoplado?
- Manutenibilidade: Com um código desacoplado, se você precisar mudar algo, é como substituir uma peça do carro sem desmontar todo o motor. As partes do seu código são independentes, então a alteração de uma não afeta as outras. Quer corrigir um erro ou melhorar alguma função? Vá em frente e faça as mudanças sem medo de comprometer o restante do sistema.
- Reutilização: Imagine que você tem peças de Lego; com um código desacoplado, você pode pegar blocos funcionais e usá-los em outros projetos ou em outras partes do sistema, sem ficar preso a dependências complicadas.
- Testabilidade: Ao usar uma interface, torna-se possível substituir o WebRequest por uma função mock, que simula o comportamento esperado, permitindo realizar testes sem depender de requisições HTTP reais. Essa conceito de simulação nos leva à próxima parte: mocks.
O que são Mocks?
Mocks são versões simuladas de objetos ou funções reais, usadas para testar o comportamento de uma classe sem depender de implementações externas. Quando separamos o CHttpClient do WebRequest por meio de uma interface, temos a possibilidade de passar uma versão simulada que cumpre o mesmo contrato, chamada de "mock". Assim, podemos testar o CHttpClient em um ambiente controlado e prever como ele reagirá em diferentes cenários de resposta, sem realmente executar chamadas HTTP.
Em um sistema onde o CHttpClient faz chamadas diretas ao WebRequest, os testes são limitados e dependem da conexão e do comportamento do servidor. No entanto, ao injetar uma função mock do WebRequest, que retorna resultados simulados, podemos testar diversos cenários e verificar isoladamente a resposta do CHttpClient. Esses mocks são extremamente úteis para garantir que a biblioteca se comporte conforme o esperado, mesmo em situações de erro ou respostas inesperadas.
A seguir, veremos como implementar interfaces e mocks para aumentar a flexibilidade e a testabilidade da nossa classe CHttpRequest.
Mão no código
Na prática, como vamos fazer isso no código? Vamos utilizar alguns recursos mais avançados da linguagem, como classes e interfaces. Se você chegou até aqui e leu os artigos anteriores, já está em certa medida familiarizado com classes, mas vamos revisar o conceito.
- Classe: Como um "molde" para criar um objeto, dentro de uma classe você pode definir métodos e atributos. Métodos são ações ou comportamentos, enquanto atributos são características ou dados do objeto. Vamos considerar um exemplo no contexto do MQL5. Você tem uma classe chamada CPosition. Nessa classe, é possível definir atributos como preço, tipo (compra ou venda), take-profit, stop-loss, volume etc. A classe é usada para criar objetos (instâncias), e cada objeto possui suas próprias características únicas.
- Interface: Uma interface é como um "contrato", que define apenas os métodos que a classe deve implementar, mas não especifica como esses métodos devem ser implementados. A interface não contém implementação, apenas a assinatura dos métodos (nomes, parâmetros e tipos de retorno).
Seguindo esse conceito, vamos analisar um exemplo prático dentro da biblioteca. Vamos por etapas. Queremos acessar a função WebRequest, mas não quero chamá-la diretamente, pois vimos que isso deixa o código excessivamente acoplado. Quero que você tenha a sua própria função WebRequest e que a biblioteca consiga usá-la sem complicações. Vamos criar camadas entre a função e quem a chama. Essa camada será a interface.
Antes de irmos para o código, vou explicar isso com a ajuda de alguns esquemas.
Como mostrado no esquema, a classe HttpClient chama diretamente a função WebRequest. Vamos adicionar uma camada entre a classe e a função, e essa camada será a interface IHttpTransport.
Com essa interface, podemos criar uma classe CHttpTransport para implementar essa interface utilizando a função WebRequest. Como mostrado no esquema abaixo:
Agora podemos aproveitar tudo isso: é possível criar várias implementações diferentes da interface e passá-las para a classe CHttpClient. Inicialmente, a biblioteca usará a CHttpTransport, que tem sua implementação baseada na WebRequest, mas podemos adicionar quantas outras quisermos, como mostrado no esquema:
|--- Connexus |--- |--- Core |--- |--- |--- HttpTransport.mqh |--- |--- Interface |--- |--- |--- IHttpTransport.mqh
Primeiro, criamos o código da interface, que define as entradas e saídas da função:
//+------------------------------------------------------------------+ //| transport interface | //+------------------------------------------------------------------+ interface IHttpTransport { int Request(const string method,const string url,const string cookie,const string referer,int timeout,const char &data[],int data_size,char &result[],string &result_headers); int Request(const string method,const string url,const string headers,int timeout,const char &data[],char &result[],string &result_headers); }; //+------------------------------------------------------------------+Note que aqui apenas declaro a função, não defino o corpo dela. Informo ao compilador que a função deve retornar um número inteiro, o nome do método será Request, e indico os parâmetros esperados. Para definir o corpo, precisamos usar uma classe, e então dizemos ao compilador que a classe deve implementar a interface. Na prática, isso fica assim:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "../Interface/IHttpTransport.mqh" //+------------------------------------------------------------------+ //| class : CHttpTransport | //| | //| [PROPERTY] | //| Name : CHttpTransport | //| Heritage : IHttpTransport | //| Description : class that implements the transport interface, | //| works as an extra layer between the request and | //| the final function and WebRequest. | //| | //+------------------------------------------------------------------+ class CHttpTransport : public IHttpTransport { public: CHttpTransport(void); ~CHttpTransport(void); int Request(const string method,const string url,const string cookie,const string referer,int timeout,const char &data[],int data_size,char &result[],string &result_headers); int Request(const string method,const string url,const string headers,int timeout,const char &data[],char &result[],string &result_headers); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CHttpTransport::CHttpTransport(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CHttpTransport::~CHttpTransport(void) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CHttpTransport::Request(const string method,const string url,const string cookie,const string referer,int timeout,const char &data[],int data_size,char &result[],string &result_headers) { return(WebRequest(method,url,cookie,referer,timeout,data,data_size,result,result_headers)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CHttpTransport::Request(const string method,const string url,const string headers,int timeout,const char &data[],char &result[],string &result_headers) { return(WebRequest(method,url,headers,timeout,data,result,result_headers)); } //+------------------------------------------------------------------+
Usando interfaces e mocks, separamos a classe CHttpRequest da função WebRequest, criando uma biblioteca mais flexível e de fácil manutenção. Essa abordagem oferece aos desenvolvedores que utilizam o Connexus a possibilidade de testar o comportamento da biblioteca em diferentes cenários sem precisar executar requisições reais, garantindo uma integração mais suave e a capacidade de adaptação rápida a novas necessidades e funcionalidades.
Agora só precisamos usar isso na classe CHttpClient, então importamos o IHttpTransport e criamos uma instância:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "../Interface/IHttpTransport.mqh" //+------------------------------------------------------------------+ //| class : CHttpClient | //| | //| [PROPERTY] | //| Name : CHttpClient | //| Heritage : No heritage | //| Description : Class responsible for linking the request and | //| response object with the transport layer. | //| | //+------------------------------------------------------------------+ class CHttpClient { private: IHttpTransport *m_transport; // Instance to store the transport implementation public: CHttpClient(void); ~CHttpClient(void); //--- Basis function bool Send(CHttpRequest &request, CHttpResponse &response); }; //+------------------------------------------------------------------+
Para realizar a injeção de dependência, adicionamos ao classe um novo construtor que receberá a camada de transporte a ser usada e a armazenará em m_transport. Também alteramos o construtor padrão para que, quando a camada de transporte não for definida, ele automaticamente seja implementado com CHttpTransport:
//+------------------------------------------------------------------+ //| class : CHttpClient | //| | //| [PROPERTY] | //| Name : CHttpClient | //| Heritage : No heritage | //| Description : Class responsible for linking the request and | //| response object with the transport layer. | //| | //+------------------------------------------------------------------+ class CHttpClient { private: IHttpTransport *m_transport; // Instance to store the transport implementation public: CHttpClient(IHttpTransport *transport); CHttpClient(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CHttpClient::CHttpClient(IHttpTransport *transport) { m_transport = transport; } //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CHttpClient::CHttpClient(void) { m_transport = new CHttpTransport(); } //+------------------------------------------------------------------+
Agora que temos a camada de transporte, vamos utilizá-la na função "Send" (Enviar).
//+------------------------------------------------------------------+ //| Basis function | //+------------------------------------------------------------------+ bool CHttpClient::Send(CHttpRequest &request, CHttpResponse &response) { //--- Request uchar body_request[]; request.Body().GetAsBinary(body_request); //--- Response uchar body_response[]; string headers_response; //--- Send ulong start = GetMicrosecondCount(); int status_code = m_transport.Request(request.Method().GetMethodDescription(),request.Url().FullUrl(),request.Header().Serialize(),request.Timeout(),body_request,body_response,headers_response); ulong end = GetMicrosecondCount(); //--- Add data in Response response.Clear(); response.Duration((end-start)/1000); response.StatusCode() = status_code; response.Body().AddBinary(body_response); response.Header().Parse(headers_response); //--- Return is success return(response.StatusCode().IsSuccess()); } //+------------------------------------------------------------------+
É importante lembrar que a forma de usar a classe não muda, ou seja, os mesmos códigos que utilizamos anteriormente nesta série, na parte de testes, continuam funcionando. A diferença é que agora podemos substituir a função final da biblioteca.
Considerações finais
Com isso, concluímos mais uma etapa no desenvolvimento da biblioteca: a criação da camada de cliente. Usando interfaces e mocks, separamos a classe CHttpClient da função WebRequest, construindo uma biblioteca mais flexível e de fácil manutenção. Esse método oferece aos desenvolvedores que utilizam o Connexus a capacidade de testar o comportamento da biblioteca em diferentes cenários sem executar chamadas reais, garantindo uma integração mais fluida e a adaptação rápida a novas necessidades e funcionalidades.
A prática de desacoplamento com o uso de interfaces e mocks aumenta significativamente a qualidade do código, promovendo extensibilidade, testabilidade e manutenção de longo prazo. Essa abordagem é especialmente vantajosa em bibliotecas, onde os desenvolvedores frequentemente precisam de testes flexíveis e substituição de módulos, o que agrega ainda mais valor para todos os usuários da biblioteca Connexus.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16324
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- 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