English Deutsch
preview
Connexusのクライアント(第7回):クライアント層の追加

Connexusのクライアント(第7回):クライアント層の追加

MetaTrader 5 | 31 3月 2025, 13:07
54 0
joaopedrodev
joaopedrodev

はじめに

この記事は、Connexusと呼ばれるライブラリを構築する連載の続きです。最初の記事では、WebRequest関数の基本的な動作を理解し、各パラメータについて解説した後、この関数をどのように使うか、またその際の課題についてサンプルコードを通して学びました。前回の記事では、HTTPメソッドとは何か、また、リクエストが正常に処理されたかどうか、あるいはクライアントやサーバーによってエラーが発生したことを示すステータスコードについて理解しました。

今回の第7回目の記事では、ライブラリで最も期待されている部分を追加します。それは、WebRequest関数を使用してリクエストをおこなうことです。ただし、直接アクセスするのではなく、プロセス内でいくつかのクラスやインターフェイスを利用する形になります。それでは、始めましょう。

ライブラリの現在の状態を振り返るために、現時点での図を以下に示します。

ここでの目標は、CHttpRequestオブジェクト、つまり、ヘッダー、本文、URL、メソッド、タイムアウトが既に構成されている準備済みのHTTPリクエストを受信し、WebRequest関数を使用してHTTPリクエストを効果的に送信することです。また、リクエストを処理し、ヘッダー、本文、ステータスコード、リクエストの合計期間などのレスポンスデータを含むCHttpResponseオブジェクトを返す必要があります。


CHttpClientクラスの作成

最も期待されるクラス、つまりライブラリの最終クラスを作成しましょう。このクラスは、ライブラリユーザーがインスタンス化して使用するクラスになります。このクラスはCHttpClient.mqhと呼ばれ、パスInclude/Connexus/Core/CHttpClient.mqhに配置されます。最初、ファイルは次のようになります。適切なインポートはすでに追加しています。
//+------------------------------------------------------------------+
//|                                                   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)
  {
  }
//+------------------------------------------------------------------+
このクラスは、WebRequest関数を使用してリクエストオブジェクト(CHttpRequest)をHTTPリクエストに変換し、データを含むCHttpResponseオブジェクトを返す役割を担います。このクラスを作成しましょう。これをおこなうには、Send()メソッドを作成します。このメソッドは、CHttpRequestとCHttpResponseの2つのオブジェクトを受信し、ブール値を返す必要があります。
class CHttpClient
  {
public:
                     CHttpClient(void);
                    ~CHttpClient(void);
   
   //--- Basis function
   bool              Send(CHttpRequest &request, CHttpResponse &response);
  };

この関数を実装してみましょう。まず、WebRequest関数のパラメータを簡単に確認してます。2つのバリエーションがあり、最初はパラメータの少ない方を使用します。

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
);
受信したオブジェクトには、すでにこれらのパラメータがすべて用意され、設定されているので、追加するだけです。実装は次のようになります。
//+------------------------------------------------------------------+
//| 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());
  }
//+------------------------------------------------------------------+

以下の各手順を説明するために、コメントにいくつかの数字を追加しました。これはライブラリの中心的な機能であるため、ここで何が起こっているかを理解することが非常に重要です。

  1. リクエスト:ここでは、body_requestと呼ばれるuchar型の配列を作成します。リクエストの本文にアクセスし、配列body_requestを渡すことによってリクエストの本文をバイナリで取得します。このようにして、本文データを配列内に挿入することでこの配列が変更されます。
  2. レスポンス:WebRequest関数がサーバーからのレスポンスの本文とヘッダーを返すために使用する2つの変数を作成します。
  3. 送信:これがすべての中心です。WebRequest関数を呼び出して、URLやタイムアウトなどのすべてのパラメータと、手順2で作成した変数を渡して、レスポンスの本文とヘッダーを受け取ります。ここでは、GetMicrosecondCount()関数を使用してリクエストの前後の時間を保存する2つの補助変数も作成します。この方法では、リクエストの前後の時間を取得し、リクエストの継続時間をミリ秒単位で計算します。
  4. レスポンスにデータを追加:ここでは、リクエストがレスポンスした後、成功したかどうかに関係なく、Clear()関数を使用してレスポンスオブジェクトのすべての値をリセットし、式「(end-start)/1000」を使用して期間を追加します。また、関数によって返されるステータスコードを定義し、レスポンスオブジェクトに本文とヘッダーを追加します。
  5. 戻る:最後の手順では、リクエストが成功コード(200~299)を返したかどうかを確認します。この方法では、リクエストが完了したかどうかを知ることができ、詳細については、レスポンスオブジェクト、つまりCHttpResponseの内容を確認するだけです。

このクラスを作成すると、更新された図は次のようになります。

最終的に、すべてのクラスはHttpClientにあります。簡単なテストをしてみましょう。



最初のテスト

これまでに構築したすべての機能を使用して、最初の簡単なテストを実行してみましょう。HTTPリクエストをテストテストするための無料オンラインサービスであるhttpbin.orgに、GETリクエストとPOSTリクエストを送信してみます。これは、kennethreitzによって作成されたオープンソースプロジェクトです(リンク)。以前の記事でもこの公開APIを使用しましたが、簡単に説明すると、httpbinはミラーのように機能し、サーバーに送信したものはすべてクライアントに返されます。これは、リクエストが正常に完了したかどうか、またサーバーが受け取ったデータを確認したいアプリケーションをテストする際に非常に便利です。

コードに進み、Expertsフォルダ内にTestRequest.mq5という新しいファイルを作成します。最終的なパスはExperts/Connexus/Test/TestRequest.mq5になります。ファイルからはOnInit関数のみを使用するため、その他の部分は省略します。コードは次のようになります。

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

#includeを使用してインポートを追加してライブラリをインポートします。

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Connexus/Core/HttpClient.mqh>

ここで、3つのオブジェクトを作成します。

  • CHttpRequest:URLとHTTPメソッドが定義される場所。GETリクエストではタイムアウト、本文、ヘッダーはオプションです。タイムアウトのデフォルト値は5000ミリ秒(5秒)です。
  • CHttpResponse:リクエストの結果をステータスコードと説明とともに保存し、リクエストの期間もミリ秒単位で保存します。
  • CHttpClient:実際にリクエストを実行するクラス
//--- 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;

最後に、Send()関数を呼び出してリクエストをおこない、データを端末に出力します。完全なコードは次のようになります。

//+------------------------------------------------------------------+
//|                                                  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);
  }
//+------------------------------------------------------------------+
コードを実行すると、端末に次の結果が表示されます。
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"
}

---------------

これはレスポンスオブジェクトからのデータであり、説明付きのステータスコード、ミリ秒単位のリクエストの期間、ヘッダー、レスポンス本文が含まれます。これにより、MQL5プログラムを外部APIに接続する際の開発者の作業が大幅に容易になります。HTTPリクエストを非常にシンプルにできました。進化を比較するために、ライブラリを使用しない場合とライブラリを使用した場合のリクエストの動作を思い出してみましょう。

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

ライブラリを使用すると、文字列、JSON、バイナリなどのさまざまな形式で本体にアクセスしたり、その他の利用可能なリソースにアクセスしたりするのが非常に簡単になります。URL、本文、ヘッダーなどのリクエストの一部を変更し、同じオブジェクトを使用して別のリクエストをおこなうこともできます。これにより、複数のリクエストを簡単な方法で連続して実行できるようになります。


ちょっとした問題があります、結合です

すべてが正しく動作しているように見え、実際に確かに動作しています。しかし、どんなものにも改善の余地があり、Connexusライブラリも例外ではありません。では、この問題とは一体何でしょうか。CHttpClientクラス内で直接WebRequest関数を使用していると、非常に結合度の高いコードになりますが、「結合度の高いコード」とはどういう意味でしょうか。

少し考えてみてください。トランプで家を作っている場面を想像してみましょう。すべてのカードが完璧にバランスを取らなければ、家は倒れてしまいます。今度は、軽くて柔軟にカードを動かせる城を作った代わりに、コンクリートのブロックでできた城を想像してください。もしカードを1枚取り出したり、位置を変えたりするとどうなると思いますか。そう、城全体が崩れてしまうのです。これが結合度の高いコードの問題です。

プログラミングにおける「結合」とは、コードの各部分が互いに強く依存しており、1つを変更すると他の部分にも影響を与えなければならない状態のことです。これはまるで靴ひもに誤って固い結び目を作ってしまい、すべてを壊さずにほどくのが非常に難しいようなものです。関数を変更したり、クラスを修正したり、機能を追加したりする際に、すべてが絡み合っているため、非常に厄介になります。

たとえば、ある関数が別の関数に強く依存していて、これらが独立して動作できない場合、そのコードは密に結合されています。これは良くないことです。なぜなら、時間が経つにつれて、変更を加えるのが難しくなり、バグの修正や新機能の追加、さらには改善が困難になり、何かを壊すリスクが増えてしまうからです。まさにこれがCHttpClientクラスで起こっていることで、Send() 関数は直接 WebRequest に依存しています。現在の状態では、これらを分離することができません。理想的な状態では、各部分がレゴのピースのように独立していて、大きな問題なく自由に組み立てたり外したりできる「分離された」コードを目指すべきです。

結合度の低いコードを作成するには、インターフェイス依存性注入を使用します。必要な操作を定義するインターフェイスを作成し、クラスが特定の実装ではなくこの抽象化に依存できるようにするという考え方です。したがって、CHttpClientは、WebRequestやモック関数(FakeWebRequest)など、インターフェイスを実装する任意のオブジェクトと通信できます。インターフェイス依存性注入の概念を見てみましょう。

  • インターフェイスの役割:インターフェイスを使用すると、他のクラスが実装できる契約を定義できます。この場合、IHttpTransportというインターフェイスを作成し、CHttpClientがHTTPリクエストを実行するために必要なメソッドを定義します。この方法では、CHttpClientはWebRequest関数ではなくIHttpTransportインターフェイスに依存するため、結合が軽減されます。
  • 依存性注入:CHttpClientをIHttpTransportの具体的な実装に接続するには、依存性注入を使用します。この手法は、依存関係(WebRequestまたはFakeWebRequest)をクラス内で直接インスタンス化するのではなく、CHttpClientにパラメータとして渡すことで構成されます。

コードの結合度が高い状態を避けることの利点は何でしょうか。

  • 保守性:結合度が低いコードでは、何かを変更する際に、まるで車のエンジン全体を分解せずに部品を交換するように、一部だけを変更できます。コードの各部分が独立しているため、他の部分に影響を与えることなく修正が可能です。バグを修正したり機能を改善したりする際に、システム全体を壊すリスクが減ります。
  • 再利用性:レゴのピースのように考えてください。結合度が低いコードでは、機能のブロックを他のプロジェクトやシステムの一部で再利用できます。複雑な依存関係に縛られることなく、効率的に再利用できます。
  • テスト可能性:インターフェイスを使用することで、WebRequestを期待する動作を模倣するモック関数に差し替えることが可能になります。これにより、実際のHTTPリクエストに依存せずにテストが実行でき、テストが容易になります。このシミュレーションの概念は、次のトピックであるモックにつながります。


モックについて

モックは実際のオブジェクトや関数をシミュレートしたバージョンで、外部の実装に依存せずにクラスの動作をテストするために使用されます。インターフェイスを使ってCHttpClientをWebRequestから分離することで、同じ契約を満たすシミュレートされたバージョン、いわゆる「モック」を渡す選択肢が得られます。これにより、実際のHTTPリクエストをおこなわなくても、制御された環境でCHttpClientをテストし、さまざまなレスポンスシナリオに対してどのように反応するかを予測できます。

CHttpClientがWebRequestを直接呼び出すシステムでは、テストは制限され、サーバーの接続性や動作に依存することになります。しかし、WebRequest関数のモックを注入してシミュレートされた結果を返すことで、異なるシナリオをテストし、CHttpClientの応答を個別に検証できます。このようなモックは、エラー発生時や予期しないレスポンスがあった場合でも、ライブラリが期待通りに動作することを確認するのに非常に役立ちます。

次に、CHttpRequestクラスの柔軟性とテスト可能性を向上させるために、インターフェイスとモックをどのように実装するかを見ていきます。

ハンズオンコード

実際には、これをコードでどのように実行するのでしょうか。今回は、クラスやインターフェイスなどのより高度な言語機能を使用します。これまでの記事を読んできた方は、すでにクラスについてある程度理解していると思いますが、ここでその概念を復習してみましょう。。

  • クラス:オブジェクトを作成オブジェクトを作成するための「テンプレート」として、クラス内ではメソッドや属性を定義できます。メソッドはオブジェクトのアクションや動作、属性はそのオブジェクトの特性やデータです。MQL5の例で考えてみましょう。CPositionというクラスがあるとします。このクラス内で、価格、タイプ(買いまたは売り)、テイクプロフィット、ストップロス、ボリュームなどの属性を定義できます。クラスはオブジェクト(インスタンス)を作成するために使用され、そのインスタンスは固有の特性を持っています。
  • インターフェイス:インターフェイスは「契約」のようなもので、クラスが実装しなければならないメソッドのみを定義しますが、そのメソッドがどのように実装されるかは示しません。インターフェイスはメソッドの実装を含まず、メソッドのシグネチャ(名前、パラメーター、戻り値の型)だけが含まれます。

この概念に基づいて、ライブラリ内での実際の使用例を見てみましょう。一歩ずつ進めます。WebRequest関数にアクセスしたいのですが、コードが過度に結合されてしまうため、直接アクセスしたくありません。代わりに、自分で定義したWebRequest関数をライブラリで問題なく使用できるようにしたいと考えています。そこで、関数とそれを呼び出すユーザーの間に層を作成しましょう。この層こそがインターフェイスです。

コードに直接進む前に、いくつかの図を使用して説明します。

図に示すように、HttpClientクラスはWebRequest関数に直接アクセスします。クラスと関数の間に層を追加します。この層はIHttpTransportインターフェイスになります。

このインターフェイスを使用すると、WebRequest関数を使用してこのインターフェイスを実装するためのCHttpTransportクラスを作成できます。下の図の通りです。

では、これらすべてを活用して、インターフェイスのさまざまな実装を作成し、それをCHttpClientクラスに配信してみましょう。ライブラリはネイティブではWebRequestで実装されているCHttpTransportを使用しますが、図に示すように、必要な数だけ他の実装を追加できます。

実際には、フォルダ構造は次のようになります。以下に示すように、「Interface」という新しいフォルダと他の2つのファイルを作成します。
|--- Connexus
|--- |--- Core
|--- |--- |--- HttpTransport.mqh
|--- |--- Interface
|--- |--- |--- IHttpTransport.mqh

まず、関数の入力と出力を定義するインターフェイスコードを作成しましょう。

//+------------------------------------------------------------------+
//| 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);
  };
//+------------------------------------------------------------------+
関数を宣言しているだけで、関数の本体を定義しているわけではないことに注意してください。関数が整数を返す必要があること、メソッドの名前がRequestになること、および期待されるパラメータが何であるかをコンパイラに伝えます。本体を定義するには、クラスを使用する必要があります。また、クラスがインターフェイスを実装する必要があることをコンパイラに伝えます。実際には次のようになります。
//+------------------------------------------------------------------+
//| 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));
  }
//+------------------------------------------------------------------+

インターフェイスとモックを使用することで、CHttpRequestクラスをWebRequest関数から分離し、より柔軟で保守しやすいライブラリを作成しました。このアプローチにより、Connexusを使用する開発者は、実際のリクエストをおこなわずにさまざまなシナリオでライブラリの動作をテストできる柔軟性が得られ、よりスムーズな統合と新しいニーズや機能への迅速な適応が可能になります。

ここで、これをCHttpClientクラスで使用する必要があるので、IHttpTransportインターフェイスをインポートしてインスタンスを作成します。

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

依存性注入を実行するには、使用されるトランスポート層を受け取るクラスに新しいコンストラクターを追加し、それをm_transportに格納します。また、トランスポート層が定義されていない場合は、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();
  }
//+------------------------------------------------------------------+

トランスポート層ができたので、それを「送信」機能で使用してみましょう。

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

クラスの使用方法は変更されないことを覚えておく価値があります。つまり、この記事の前半のテストセクションで使用したのと同じコードが引き続き有効です。違いは、ライブラリの最終的な機能を変更できるようになったことです。


結論

これで、ライブラリの次の段階であるクライアント層の作成が完了しました。インターフェイスとモックを使用することで、CHttpClientクラスをWebRequest関数から分離し、より柔軟で保守しやすいライブラリを作成することができました。このアプローチにより、Connexusを使用する開発者は、実際のリクエストを行わずに様々なシナリオでライブラリの動作をテストできるようになり、よりスムーズな統合が可能となり、新しいニーズや機能に迅速に対応できるようになります。

インターフェイスとモックを使った分離の実践により、コードの品質が大幅に向上し、拡張性、テスト可能性、そして長期的な保守性が向上します。このアプローチは、開発者が柔軟なテストとモジュール式の置き換えを必要とすることが多いライブラリにとって特に有利であり、Connexusライブラリのすべてのユーザーにとって付加価値を提供します。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16324

知っておくべきMQL5ウィザードのテクニック(第47回):時間差分を用いた強化学習 知っておくべきMQL5ウィザードのテクニック(第47回):時間差分を用いた強化学習
時間差分学習は、エージェントの訓練中に予測された報酬と実際の報酬の差に基づいてQ値を更新する強化学習のアルゴリズムの一つです。特に、状態と行動のペアにこだわらずにQ値を更新する点に特徴があります。したがって、これまでの記事と同様に、ウィザードで作成したエキスパートアドバイザー(EA)での適用方法を検討していきます。
知っておくべきMQL5ウィザードのテクニック(第48回):ビル・ウィリアムズのアリゲーター 知っておくべきMQL5ウィザードのテクニック(第48回):ビル・ウィリアムズのアリゲーター
ビル・ウィリアムズが考案したアリゲーターインジケーターは、明確なシグナルを生成し、他のインジケーターと組み合わせて使用されることが多い、多機能なトレンド識別インジケーターです。MQL5ウィザードのクラスとアセンブリを活用することで、パターンベースでさまざまなシグナルをテストできるため、このインジケーターも検討対象となります。
MQL5での取引戦略の自動化(第1回):Profitunityシステム(ビル・ウィリアムズ著「Trading Chaos」) MQL5での取引戦略の自動化(第1回):Profitunityシステム(ビル・ウィリアムズ著「Trading Chaos」)
この記事では、ビル・ウィリアムズのProfitunityシステムを詳しく分析し、その核心となる構成要素や、市場の混乱の中での独自の取引アプローチを解説します。MQL5用いたシステムの実装方法を、主要なインジケーターやエントリー/エグジットシグナルの自動化に焦点を当てながら説明します。さらに、戦略のテストと最適化をおこない、さまざまな市場環境におけるパフォーマンスについて考察します。
PythonからMQL5へ:量子に着想を得た取引システムへの旅 PythonからMQL5へ:量子に着想を得た取引システムへの旅
この記事では、量子に着想を得た取引システムの開発について検討し、Pythonプロトタイプから実際の取引のためのMQL5実装への移行について説明します。このシステムは、量子シミュレーターを使用した従来のコンピューター上で実行されますが、重ね合わせや量子もつれなどの量子コンピューティングの原理を使用して市場の状態を分析します。主な機能には、8つの市場状態を同時に分析する3量子ビットシステム、24時間のルックバック期間、および市場分析用の7つのテクニカル指標が含まれます。精度率は控えめに思えるかもしれませんが、適切なリスク管理戦略と組み合わせると大きな優位性が得られます。