ソフトウェア開発とMQL5におけるデザインパターン(第3回):振る舞いパターン1

Mohamed Abdelmaaboud | 20 3月, 2024

はじめに

この記事では、ソフトウェア領域におけるデザインパターンについての連載を続けます。本連載の前の2つの記事では生成パターンと構造パターンの2タイプを特定しました。この記事では、振る舞いパターンとは何か、それがソフトウェアの作成、構築、開発時にどのように役立つかを特定して理解した後、3タイプ目である振る舞いデザインパターンについて説明します。  その後、MQL5でそれらをどのように使用し、信頼性が高く、保守、再利用、拡張が可能な、十分にテストされたMetaTrader 5用のソフトウェアを作成できるかを学びます。

以下のトピックは、この重要なパターンをカバーするために言及する内容についてです。

本連載から読む記事がこれが初めての方は、ソフトウェア開発における最も重要なトピックの1つであるデザインパターンを全体的に捉えるために、生成パターンと構造パターンに関する他の記事もご覧ください。

免責条項:すべての情報は「現状有姿」で提供され、情報提供のみを目的としており、取引目的やアドバイスを目的としたものではありません。いかなる結果も保証するものではありません。読者がこれらの資料を自分の取引口座で使用する場合、自己責任でおこなってください。

振る舞いパターン

デザインパターンについて話す文脈で、生成パターンと構造パターンについて話した後、これらのデザインパターンの最後のタイプである振る舞いパターンについて話します。生成パターンとは、オブジェクトを創造し、構成し、表現することによって、独立したソフトウェアやシステムを創造するのに役立つパターンであることを学びました。それに加えて、構造パターンとは、作成されたオブジェクトやクラスを使用して、より大きな構造を構築するために使用することができるパターンであることを学びました。

この記事では、オブジェクト間で責任をどのように割り当てたり設定したりするかに関係する振る舞いパターンを提示します。また、これらは、オブジェクト同士がどのように通信や相互作用をおこなうかを識別することもできます。

1つの記事ではカバーしきれないほど多くのパターンがあるため、この記事では最初の5つのパターンに焦点を当てます。

デザインパターンに関する前の2つの記事を読んだ方には、すべてのパターンをカバーするために使用されるアプローチにはお馴染みかと思います。


Chain of responsibility

このセクションでは、Chain of responsibilityとは何かを理解するために、Chain of responsibilityができること、解決できること、MQL5でどのように使用できるかを学びます。クライアントからの要求を処理する必要があるとき、全員の責任に基づいてクライアントの要求を処理できるオブジェクトが多数ある場合、このパターンを使って処理することができます。

このパターンを使用することの利点とは裏腹に、以下のような落とし穴もあります。

パターンの役割

このパターンは、多くのオブジェクトが要求を処理する機会を与えることで、要求の送信者と受信者を切り離すのに便利です。これは、受信オブジェクトを連結し、全員に要求を渡して、どのオブジェクトが要求を処理できるかを確認することで行われます。

以下はパターンの構造を示す図です。

CHAIN OF RESPONSIBILITY

前の図でわかるように、次のような参加者がいます。

デザインパターンが解決する問題

このパターンは以下に該当する場合に使用できます。

つまり、このパターンは以下のことを解決できます。

MQL5での使用方法

このセクションでは、MQL5でこのパターンを使用して効果的なMetaTrader 5ソフトウェアを作成する方法を学びます。MQL5でChain of responsibilityをコード化する手順は次のとおりです。

Chain_Of_Responsibility領域を宣言し、namespaceキーワードを使用して、その中にパターンの関数と変数を含めます。

namespace Chain_Of_Responsibility

クライアントからの要求を処理するHandlerクラスの参加者を宣言します。

class Handler
  {
public:
   Handler*          successor;
   virtual void      HandleRequest(int)=0;
                    ~Handler(void);
  };
Handler::~Handler(void)
  {
   delete successor;
  }

担当する要求を処理するまたは要求を処理できる場合はその後継者に要求を渡す、ConcreteHandler1クラスの参加者を宣言します。

class ConcreteHandler1:public Handler
  {
public:
   void              HandleRequest(int);
  };
void ConcreteHandler1::HandleRequest(int request)
  {
   if(request==1)
      Print("The request: ",request,". The request handled by: ",&this);
   else
      if(CheckPointer(successor))
        {
         Print("The request: ",request,". The request cannot be handled by ",&this,", but it is forwarding to the successor...");
         successor.HandleRequest(request);
        }
  }

ConcreteHandler2クラスも参加者として宣言します。

class ConcreteHandler2:public Handler
  {
public:
   void              HandleRequest(int);
  };
void ConcreteHandler2::HandleRequest(int request)
  {
   if(request==2)
      Print("The request: ",request,". The request handled by: ",&this);
   else
      if(CheckPointer(successor))
        {
         Print("The request: ",request,". The request cannot be handled by ",&this,", forwarding to successor...");
         successor.HandleRequest(request);
        }
  }

具体的なハンドラへの要求を開始するクライアントクラスをチェーンで宣言します。

class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output()
  {
   return __FUNCTION__;
  }

クライアントを実行する関数は、要求をチェーンに送り、要求は処理されるか後継者に渡されます。

void   Client::Run()
  {
   Handler* h1=new ConcreteHandler1();
   Handler* h2=new ConcreteHandler2();
   h1.successor=h2;
   h1.HandleRequest(1);
   h1.HandleRequest(2);
   delete h1;
  }

MQL5でChain of responsibilityパターンを使用するための完全なコードは次のように1つのブロックになります。

//+------------------------------------------------------------------+
//|                                      Chain_Of_Responsibility.mqh |
//+------------------------------------------------------------------+
namespace Chain_Of_Responsibility
{
class Handler
  {
public:
   Handler*          successor;
   virtual void      HandleRequest(int)=0;
                    ~Handler(void);
  };
Handler::~Handler(void)
  {
   delete successor;
  }
class ConcreteHandler1:public Handler
  {
public:
   void              HandleRequest(int);
  };
void ConcreteHandler1::HandleRequest(int request)
  {
   if(request==1)
      Print("The request: ",request,". The request handled by: ",&this);
   else
      if(CheckPointer(successor))
        {
         Print("The request: ",request,". The request cannot be handled by ",&this,", but it is forwarding to the successor...");
         successor.HandleRequest(request);
        }
  }
class ConcreteHandler2:public Handler
  {
public:
   void              HandleRequest(int);
  };
void ConcreteHandler2::HandleRequest(int request)
  {
   if(request==2)
      Print("The request: ",request,". The request handled by: ",&this);
   else
      if(CheckPointer(successor))
        {
         Print("The request: ",request,". The request cannot be handled by ",&this,", but it is forwarding to successor...");
         successor.HandleRequest(request);
        }
  }
class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output()
  {
   return __FUNCTION__;
  }
void   Client::Run()
  {
   Handler* h1=new ConcreteHandler1();
   Handler* h2=new ConcreteHandler2();
   h1.successor=h2;
   h1.HandleRequest(1);
   h1.HandleRequest(2);
   delete h1;
  }
}


Command

このセクションでは、もう1つの振る舞いパターンであるCommandパターンについて説明します。このパターンは「Action and Transaction」(行動とトランザクション)としても知られています。このパターンによって、要求をオブジェクトにカプセル化することができます。これにより、送信者や受信者を変更せずに、さまざまな要求のパラメータを設定できるようになります。つまり、送信者、プロセッサー、受信者が分離されます。これは、クラス内に巨大な機能がある場合に非常に役立ちます。このパターンは取り消し操作にも対応しています。

このパターンを使用する際には、ほとんどのものと同じようにいくつかの落とし穴があります。

パターンの役割

簡単に言うと、コマンドを受信して受信者に送信するカプセル化されたインボーカーを作成します。

以下はCommandパターンの図です。

Command

前の図でわかるように、このパターンには次のような参加者がいます。

デザインパターンが解決する問題

MQL5での使用方法

このセクションでは、MQL5でCommandパターンを使用するための方法を紹介します。

namespaceキーワードを使って、関数、変数、クラス...などを指定するCommand空間を宣言します。

namespace Command

要求の操作を実行するメソッドを特定する参加者として、Receiverクラスを宣言します。

class Receiver
  {
public:
                     Receiver(void);
                     Receiver(Receiver&);
   void              Action(void);
  };
Receiver::Receiver(void)
  {
  }
Receiver::Receiver(Receiver &src)
  {
  }
void Receiver::Action(void)
  {
  }

操作インターフェイスを宣言する参加者として、Commandクラスを宣言します。

class Command
  {
protected:
   Receiver*         m_receiver;
public:
                     Command(Receiver*);
                    ~Command(void);
   virtual void      Execute(void)=0;
  };
Command::Command(Receiver* receiver)
  {
   m_receiver=new Receiver(receiver);
  }
Command::~Command(void)
  {
   if(CheckPointer(m_receiver)==1)
     {
      delete m_receiver;
     }
  }

ConcreteCommandクラスを参加者として宣言し、受信者と行動またはコマンドのリンクを作成し、受信者の操作を呼び出した後にexecute()を実装します。

class ConcreteCommand:public Command
  {
protected:
   int               m_state;
public:
                     ConcreteCommand(Receiver*);
   void              Execute(void);
  };
ConcreteCommand::ConcreteCommand(Receiver* receiver):
   Command(receiver),
   m_state(0)
  {
  }
void ConcreteCommand::Execute(void)
  {
   m_receiver.Action();
   m_state=1;
  }

要求を実行するためのCommandを受け取る参加者として、Invokerクラスを宣言します。

class Invoker
  {
public:
                    ~Invoker(void);
   void              StoreCommand(Command*);
   void              Execute(void);
protected:
   Command*          m_command;
  };
Invoker::~Invoker(void)
  {
   if(CheckPointer(m_command)==1)
     {
      delete m_command;
     }
  }
void Invoker::StoreCommand(Command* command)
  {
   m_command=command;
  }
void Invoker::Execute(void)
  {
   m_command.Execute();
  }

クライアントクラスを参加者として宣言し、具体的なCommandの受信者を作成して実行します。

class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   Receiver receiver;
   Invoker invoker;
   invoker.StoreCommand(new ConcreteCommand(&receiver));
   invoker.Execute();
  }

完全なコードは次のように1つのブロックになります。

//+------------------------------------------------------------------+
//|                                                      Command.mqh |
//+------------------------------------------------------------------+
namespace Command
{
class Receiver
  {
public:
                     Receiver(void);
                     Receiver(Receiver&);
   void              Action(void);
  };
Receiver::Receiver(void)
  {
  }
Receiver::Receiver(Receiver &src)
  {
  }
void Receiver::Action(void)
  {
  }
class Command
  {
protected:
   Receiver*         m_receiver;
public:
                     Command(Receiver*);
                    ~Command(void);
   virtual void      Execute(void)=0;
  };
Command::Command(Receiver* receiver)
  {
   m_receiver=new Receiver(receiver);
  }
Command::~Command(void)
  {
   if(CheckPointer(m_receiver)==1)
     {
      delete m_receiver;
     }
  }
class ConcreteCommand:public Command
  {
protected:
   int               m_state;
public:
                     ConcreteCommand(Receiver*);
   void              Execute(void);
  };
ConcreteCommand::ConcreteCommand(Receiver* receiver):
   Command(receiver),
   m_state(0)
  {
  }
void ConcreteCommand::Execute(void)
  {
   m_receiver.Action();
   m_state=1;
  }
class Invoker
  {
public:
                    ~Invoker(void);
   void              StoreCommand(Command*);
   void              Execute(void);
protected:
   Command*          m_command;
  };
Invoker::~Invoker(void)
  {
   if(CheckPointer(m_command)==1)
     {
      delete m_command;
     }
  }
void Invoker::StoreCommand(Command* command)
  {
   m_command=command;
  }
void Invoker::Execute(void)
  {
   m_command.Execute();
  }
class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   Receiver receiver;
   Invoker invoker;
   invoker.StoreCommand(new ConcreteCommand(&receiver));
   invoker.Execute();
  }
}


Interpreter

もう1つの振る舞いパターンは、特定の言語を通じてオブジェクト間の相互作用を設定し、後でこの言語の内容を説明および解釈するためにこの表現を使用できるインタプリタでルールまたは文法の表現を定義できるようにします。

この種類のパターンを使う場合、次のような落とし穴があります。

パターンの役割

このパターンは、ある言語の文法を定義し、その言語の内容を表現し、その内容の解釈を得る方法を説明するのに役立ちます。

Interpreterパターンの図は以下のようになります。

Interpreter

前の図でわかるように、このパターンには次のような参加者がいます。

デザインパターンが解決する問題

確認したように、このパターンは、解釈する必要のある言語があり、その言語で内容を定義したり表現したりできる場合に使用できます。

このパターンを使える最良のケースを以下に挙げます。

つまり、このパターンを使用することで、次のようなメリットが得られます。

MQL5での使用方法

このセクションでは、このタイプのパターンをコード化または使用する簡単な方法を紹介します。InterpreterをMQL5で使用する手順を以下に示します。

関数、変数、クラスを定義し、宣言するために使用するInterpreterの領域を、お馴染みのようにnamespaceキーワードを使用して宣言します。

namespace Interpreter

コンテキストクラスを参加者として宣言します。

class Context
  {
public:
   string            m_source;
   char              m_vocabulary;
   int               m_position;
   bool              m_result;
   //---
                     Context(char,string);
   void              Result(void);
  };
Context::Context(char vocabulary,string source):
   m_source(source),
   m_vocabulary(vocabulary),
   m_position(0),
   m_result(false)
  {
  }
void Context::Result(void)
  {
  }

Abstractクラスを参加者として宣言します。

class AbstractExpression
  {
public:
   virtual void      Interpret(Context&)=0;
  };

interpretメソッドを実装する参加者としてTerminalExpressionクラスを宣言します。

class TerminalExpression:public AbstractExpression
  {
public:
   void              Interpret(Context&);
  };
void TerminalExpression::Interpret(Context& context)
  {
   context.m_result=
      StringSubstr(context.m_source,context.m_position,1)==
      CharToString(context.m_vocabulary);
  }

NonterminalExpressionクラスを参加者として宣言します。

class NonterminalExpression:public AbstractExpression
  {
protected:
   AbstractExpression* m_nonterminal_expression;
   AbstractExpression* m_terminal_expression;
public:
   void              Interpret(Context&);
                    ~NonterminalExpression(void);
  };
NonterminalExpression::~NonterminalExpression(void)
  {
   delete m_nonterminal_expression;
   delete m_terminal_expression;
  }
void NonterminalExpression::Interpret(Context& context)
  {
   if(context.m_position<StringLen(context.m_source))
     {
      m_terminal_expression=new TerminalExpression;
      m_terminal_expression.Interpret(context);
      context.m_position++;
      if(context.m_result)
        {
         m_nonterminal_expression=new NonterminalExpression;
         m_nonterminal_expression.Interpret(context);
        }
     }
  }

クライアントを参加者として宣言します。

class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void) {return __FUNCTION__;}
void Client::Run(void)
  {
   Context context_1('a',"aaa");
   Context context_2('a',"aba");
   AbstractExpression* expression;
   expression=new NonterminalExpression;
   expression.Interpret(context_1);
   context_1.Result();
   delete expression;
   expression=new NonterminalExpression;
   expression.Interpret(context_2);
   context_2.Result();
   delete expression;
  }

Interpreterパターンの完全なコードは次のように1つのブロックになります。

//+------------------------------------------------------------------+
//|                                                  Interpreter.mqh |
//+------------------------------------------------------------------+
namespace Interpreter
{
class Context
  {
public:
   string            m_source;
   char              m_vocabulary;
   int               m_position;
   bool              m_result;
                     Context(char,string);
   void              Result(void);
  };
Context::Context(char vocabulary,string source):
   m_source(source),
   m_vocabulary(vocabulary),
   m_position(0),
   m_result(false)
  {
  }
void Context::Result(void)
  {
  }
class AbstractExpression
  {
public:
   virtual void      Interpret(Context&)=0;
  };
class TerminalExpression:public AbstractExpression
  {
public:
   void              Interpret(Context&);
  };
void TerminalExpression::Interpret(Context& context)
  {
   context.m_result=
      StringSubstr(context.m_source,context.m_position,1)==
      CharToString(context.m_vocabulary);
  }
class NonterminalExpression:public AbstractExpression
  {
protected:
   AbstractExpression* m_nonterminal_expression;
   AbstractExpression* m_terminal_expression;
public:
   void              Interpret(Context&);
                    ~NonterminalExpression(void);
  };
NonterminalExpression::~NonterminalExpression(void)
  {
   delete m_nonterminal_expression;
   delete m_terminal_expression;
  }
void NonterminalExpression::Interpret(Context& context)
  {
   if(context.m_position<StringLen(context.m_source))
     {
      m_terminal_expression=new TerminalExpression;
      m_terminal_expression.Interpret(context);
      context.m_position++;
      if(context.m_result)
        {
         m_nonterminal_expression=new NonterminalExpression;
         m_nonterminal_expression.Interpret(context);
        }
     }
  }
class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void) {return __FUNCTION__;}
void Client::Run(void)
  {
   Context context_1('a',"aaa");
   Context context_2('a',"aba");
   AbstractExpression* expression;
   expression=new NonterminalExpression;
   expression.Interpret(context_1);
   context_1.Result();
   delete expression;
   expression=new NonterminalExpression;
   expression.Interpret(context_2);
   context_2.Result();
   delete expression;
  }
}


Iterator

ここでは、オブジェクト間の相互作用や通信の方法を設定する振る舞いパターンの1つであるIteratorデザインパターンについて説明します。このパターンは、リストのような集成体オブジェクトの存在をサポートしたり、その根底にある表現の詳細や内部構造を公開することなく、シーケンス化された方法で要素にアクセスする方法を与えてくれたりします。Cursor(カーソル)とも呼ばれます。

Iteratorパターンを使用することの利点とは裏腹に、以下のような落とし穴もあります。

パターンの役割

このパターンは、異なる方法で走査される可能性のある複雑な集合体の場合のバリエーションをサポートすることができます。なぜなら、更新された走査をサポートするためにIteratorのサブクラスを定義することに加えて、Iteratorのインスタンスを置き換えることによって走査アルゴリズムを更新することが容易だからです。集合体のインターフェイスがシンプルになります。Iteratorの走査状態を追跡することで、一度に多くの走査を処理することができます。

このパターンの図は以下のようになります。

Iterator

上記のIterator構造図でわかるように、このパターンには次のような参加者がいます。

デザインパターンが解決する問題

Iteratorパターンは、以下のような場合に使用できます。

MQL5での使用方法

このセクションでは、このパターンをMQL5でどのように使うことができるかを、以下の手順を通して学んでいきます。

#defineプリプロセッサを使用して、ERRITERAOR-UT-OF-BOUNDSを定義します。

#define ERR_ITERATOR_OUT_OF_BOUNDS 1

templateキーワードを使用し、定義されたIteratorインターフェイスのCurrentItemとしてTを宣言します。

template<typename T>
interface Iterator
  {
   void     First(void);
   void     Next(void);
   bool     IsDone(void);
   T        CurrentItem(void);
  };

また、templateキーワードを使い、定義されたAggregateインターフェイスの演算子としてTを宣言します。

template<typename T>
interface Aggregate
  {
   Iterator<T>*   CreateIterator(void);
   int            Count(void);
   T              operator[](int at);
   void           operator+=(T item);
  };

Iteratorインターフェイスを実装し、ConcreteIteratorクラスを宣言した後、集成体の走査を現在位置として追跡します。

template<typename T>
class ConcreteIterator:public Iterator<T>
  {
public:
   void              First(void);
   void              Next(void);
   bool              IsDone(void);
   T                 CurrentItem(void);
                     ConcreteIterator(Aggregate<T>&);
protected:
   Aggregate<T>*     m_aggregate;
   int               m_current;
  };
template<typename T> 
   ConcreteIterator::ConcreteIterator(Aggregate<T>& aggregate):
   m_aggregate(&aggregate),
   m_current(0)
  {
  }
template<typename T>
void ConcreteIterator::First(void)
  {
   m_current=0;
  }
template<typename T>
void ConcreteIterator::Next(void)
  {
   m_current++;
   if(!IsDone())
     {
     }
  }
template<typename T>
bool ConcreteIterator::IsDone(void)
  {
   return m_current>=m_aggregate.Count();
  }
template<typename T>
string ConcreteIterator::CurrentItem(void)
  {
   if(IsDone())
     {
      SetUserError(ERR_ITERATOR_OUT_OF_BOUNDS);
      return NULL;
     }
   return m_aggregate[m_current];
  }

適切な具象Iteratorのインスタンスを返り値として得るためのIterator生成インターフェイスを実装します。

class ConcreteAggregate:public Aggregate<string>
  {
public:
   Iterator<string>* CreateIterator(void);
   int               Count(void);
   void              operator+=(string item);
   string            operator[](int at);
protected:
   string            m_items[];
  };
Iterator<string>* ConcreteAggregate::CreateIterator(void)
  {
   return new ConcreteIterator<string>(this);
  }
void ConcreteAggregate::operator+=(string item)
  {
   int size=ArraySize(m_items);
   ArrayResize(m_items,size+1);
   m_items[size]=item;
  }
string ConcreteAggregate::operator[](int at)
  {
   return m_items[at];
  }
int ConcreteAggregate::Count()
  {
   return ArraySize(m_items);
  }

MQL5でIteratorパターンを使用するための完全なコードは次のように1つのブロックになります。

//+------------------------------------------------------------------+
//|                                                201021_104101.mqh |
//+------------------------------------------------------------------+
#define ERR_ITERATOR_OUT_OF_BOUNDS 1
template<typename T>
interface Iterator
  {
   void     First(void);
   void     Next(void);
   bool     IsDone(void);
   T        CurrentItem(void);
  };
template<typename T>
interface Aggregate
  {
   Iterator<T>*   CreateIterator(void);
   int            Count(void);
   T              operator[](int at);
   void           operator+=(T item);
  };

template<typename T>
class ConcreteIterator:public Iterator<T>
  {
public:
   void              First(void);
   void              Next(void);
   bool              IsDone(void);
   T                 CurrentItem(void);
                     ConcreteIterator(Aggregate<T>&);
protected:
   Aggregate<T>*     m_aggregate;
   int               m_current;
  };
template<typename T> 
   ConcreteIterator::ConcreteIterator(Aggregate<T>& aggregate):
   m_aggregate(&aggregate),
   m_current(0)
  {
  }
template<typename T>
void ConcreteIterator::First(void)
  {
   m_current=0;
  }
template<typename T>
void ConcreteIterator::Next(void)
  {
   m_current++;
   if(!IsDone())
     {
     }
  }
template<typename T>
bool ConcreteIterator::IsDone(void)
  {
   return m_current>=m_aggregate.Count();
  }
template<typename T>
string ConcreteIterator::CurrentItem(void)
  {
   if(IsDone())
     {
      SetUserError(ERR_ITERATOR_OUT_OF_BOUNDS);
      return NULL;
     }
   return m_aggregate[m_current];
  }
class ConcreteAggregate:public Aggregate<string>
  {
public:
   Iterator<string>* CreateIterator(void);
   int               Count(void);
   void              operator+=(string item);
   string            operator[](int at);
protected:
   string            m_items[];
  };
Iterator<string>* ConcreteAggregate::CreateIterator(void)
  {
   return new ConcreteIterator<string>(this);
  }
void ConcreteAggregate::operator+=(string item)
  {
   int size=ArraySize(m_items);
   ArrayResize(m_items,size+1);
   m_items[size]=item;
  }
string ConcreteAggregate::operator[](int at)
  {
   return m_items[at];
  }
int ConcreteAggregate::Count()
  {
   return ArraySize(m_items);
  }


Mediator

もう1つの振る舞いデザインパターンは、オブジェクト同士がどのように相互作用するかを設定する際に使用できます。このパターンはMediatorパターンです。オブジェクトの集合があり、このオブジェクトの集合がどのように相互作用するかを記述するためにカプセル化されたオブジェクトを定義する必要があるときに使用することができます。また、分離も可能で、オブジェクトの相互作用を独立に変化させることができます。

どんなものにも利点と落とし穴があるのと同じように、Mediatorデザインパターンにも次のような落とし穴があります。

パターンの役割

Mediatorパターンの識別として述べたように、このパターンは、各オブジェクトを明示的に記述することなく、オブジェクト間の相互作用メソッドを設定するのに役立つと言えます。そのため、オブジェクト間を分離するのに役立ちます。また、ルーターとして使用されることもあり、通信管理にも使用されます。

Mediatorパターンの構造を見るための図を以下に示します。

Mediator

Mediator(2)

前のパターン構造図でわかるように、このパターンには次のような参加者がいます。

デザインパターンが解決する問題

これまで理解してきたことを通して、このパターンは次のような場合に使用できます。

よって、このパターンについて、次を言うことができます。

MQL5での使用方法

下の手順でこのパターンをMQL5で使用します。

interfaceキーワードを使用してColleagueインターフェイスを作成します。

interface Colleague
  {
   void Send(string message);
  };
interfaceキーワードを使ってMediatorインターフェイスを作成します。
interface Mediator
  {
   void Send(string message,Colleague& colleague);
  };

ConcreteColleague1クラスを宣言します。

class ConcreteColleague1:public Colleague
  {
protected:
   Mediator*         m_mediator;
public:
                     ConcreteColleague1(Mediator& mediator);
   void              Notify(string message);
   void              Send(string message);
  };
ConcreteColleague1::ConcreteColleague1(Mediator& meditor):
   m_mediator(&meditor)
  {
  }
void ConcreteColleague1::Notify(string message)
  {
  }
void ConcreteColleague1::Send(string message)
  {
   m_mediator.Send(message,this);
  }

ConcreteColleague2クラスを宣言します。

class ConcreteColleague2:public Colleague
  {
protected:
   Mediator*         m_mediator;
public:
                     ConcreteColleague2(Mediator& mediator);
   void              Notify(string message);
   void              Send(string message);
  };
ConcreteColleague2::ConcreteColleague2(Mediator& mediator):
   m_mediator(&mediator)
  {
  }
void ConcreteColleague2::Notify(string message)
  {
  }
void ConcreteColleague2::Send(string message)
  {
   m_mediator.Send(message,this);
  }

ConcreteMediatorクラスを宣言します。

class ConcreteMediator:public Mediator
  {
public:
   ConcreteColleague1*  colleague_1;
   ConcreteColleague2*  colleague_2;
   void              Send(string message,Colleague& colleague);
  };
void ConcreteMediator::Send(string message,Colleague& colleague)
  {
   if(colleague_1==&colleague)
      colleague_2.Notify(message);
   else
      colleague_1.Notify(message);
  }

MQL5でMediatorデザインパターンを使用するための完全なコードは次のように1つのブロックになります。

//+------------------------------------------------------------------+
//|                                                     Mediator.mqh |
//+------------------------------------------------------------------+
interface Colleague
  {
   void Send(string message);
  };
interface Mediator
  {
   void Send(string message,Colleague& colleague);
  };
class ConcreteColleague1:public Colleague
  {
protected:
   Mediator*         m_mediator;
public:
                     ConcreteColleague1(Mediator& mediator);
   void              Notify(string message);
   void              Send(string message);
  };
ConcreteColleague1::ConcreteColleague1(Mediator& meditor):
   m_mediator(&meditor)
  {
  }
void ConcreteColleague1::Notify(string message)
  {
  }
void ConcreteColleague1::Send(string message)
  {
   m_mediator.Send(message,this);
  }
class ConcreteColleague2:public Colleague
  {
protected:
   Mediator*         m_mediator;
public:
                     ConcreteColleague2(Mediator& mediator);
   void              Notify(string message);
   void              Send(string message);
  };
ConcreteColleague2::ConcreteColleague2(Mediator& mediator):
   m_mediator(&mediator)
  {
  }
void ConcreteColleague2::Notify(string message)
  {
  }
void ConcreteColleague2::Send(string message)
  {
   m_mediator.Send(message,this);
  }
class ConcreteMediator:public Mediator
  {
public:
   ConcreteColleague1*  colleague_1;
   ConcreteColleague2*  colleague_2;
   void              Send(string message,Colleague& colleague);
  };
void ConcreteMediator::Send(string message,Colleague& colleague)
  {
   if(colleague_1==&colleague)
      colleague_2.Notify(message);
   else
      colleague_1.Notify(message);
  }


結論

プログラミングやソフトウェア開発において最も重要なトピックの1つであるデザインパターンの3つ目のタイプについて説明しました。この記事では、いくつかの振る舞いデザインパターンを紹介し、それがどのようなもので、どのように再利用、拡張、保守可能で、テストされたソフトウェアを作成するのに役立つのかについて説明しました。各パターンで何ができるのか、各パターンを使用することで解決できる問題や課題、各パターンの利点と落とし穴、MQL5で各パターンを使用してMetaTrader 5用の効果的な取引システムを作成する方法について学びました。

振る舞いデザインパターンの中から以下のパターンをカバーしました。

デザインパターンについての記事を読むのがこれが初めての方は、「ソフトウェア開発とMQL5におけるデザインパターン(第1回):生成パターン」と「ソフトウェア開発とMQL5におけるデザインパターン(第2回):構造パターン」をお読みになることをお勧めします。他のタイプのデザインパターンについてもっと学ぶ必要がある場合、ぜひご参考ください。

効果的なソフトウェアを作成するのに役立つので、デザインパターンについてさらにお読みになることをお勧めします。

最も人気のあるテクニカル指標を使用したMetaTrader 5用の取引システムの作成に関する記事をもっとお読みになりたい場合は、私の出版物ページから他の記事をご確認ください。取引に役立つ洞察を得たり、結果を強化したり、開発者としての素養を高め、お取り組み中のプロジェクトを改善したりするために、これらの記事がお役に立つかと思います。