English Русский Español Deutsch 日本語 Português
preview
软件开发和 MQL5 中的设计范式(第 3 部分):行为范式 1

软件开发和 MQL5 中的设计范式(第 3 部分):行为范式 1

MetaTrader 5交易 | 17 七月 2024, 12:41
140 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

概述

在本文中,我们将继续有关软件领域中设计范式主题的系列文章。在本系列的前两篇文章中,我们辨别了这些范式的两种类型,即创建性范式和结构性范式,在本文中,我们将辨别行为设计范式,这是第三种类型;在辨别和理解了行为范式是什么样的,以及它们在创建、构建或开发我们的软件时有何作用之后,我们将学习如何在 MetaTrader 5 中利用 MQL5 创建软件,从而创建一个可靠的、可维护、可重用、经过充分测试、并可扩展的软件。

以下主题是关于我们将提到的内容,涵盖了该范式的重要类型:

如果这是您阅读本系列的第一篇文章,那么我希望您先去阅读有关创建和结构范式的其它文章,以便全面了解软件开发中最重要的主题之一,即设计范式,因为它对您创建软件的方式非常实用。

免责声明:所有信息仅按“如样”提供,仅用于教学目的,并非为交易目的或建议而准备。该信息不保证任何类型的结果。如果您选择在任何交易账户上使用这些素材,您将自行承担风险,并且您将是唯一的责任人。

行为(Behavioral)范式

在谈论设计范式的上下文中,以及在谈论创造性范式和结构性范式之后,我们将讨论这些设计范式的最后一种类型,即行为范式。我们已领教到,创建性范式是通过创建、组合和表示对象来帮助创建独立的软件或系统的范式。此外还领教到,结构性范式是通过所创建的对象和类来构建更大结构的范式。

在本文中,我们提供的行为范式,与分配和设置对象之间的责任有关。它们还辨别对象如何相互通信或交互,并且该类型下有许多范式,如下所示:

  • 责任(responsibility)链
  • 命令(Command)
  • 解释器(Interpreter)
  • 迭代器(Iterator)
  • 调解器(Mediator)
  • 备忘录(Memento)
  • 观察者(Observer)
  • 状态(State)
  • 策略(Strategy)
  • 模板(Template)方法
  • 游客(Visitor)

由于范式太多,无法在一篇文章中涵盖,故在这篇文章中我们的重点将放在前五种范式上,如下:

  • 责任链:它在处理来自发送方的请求时,把机会分散给多个对象,这有助于应用发送方与其接收方的解耦。它还有助于链接接收对象,并沿此链传递请求,从而完成对象的处理。
  • 命令:授予对不同请求、队列或日志请求的客户端设置参数的权限,并支持撤消已封装请求对象的操作。
  • 解释器:它定义了给定语言的语法表述,以及解释器,可用这个已定义的表述来解释语言中需要的句子。
  • 迭代器:如果我们需要在不公开其底层表述的情况下对聚合对象的元素顺序访问,则该范式有助于为我们提供一种方法来做到这一点。
  • 调节器:它定义了一组对象如何通过封装对象进行交互,并可通过我们独立地改变对象的交互来促进解耦。

如果您阅读了前两篇关于设计范式的文章,您就熟悉我们将涵盖每个范式的方式,它与如下相同:

  • 范式有什么作用?
  • 设计范式解决了什么问题?
  • 我们如何在 MQL5 中运用它?


责任(responsibility)链

在这一部分中,我们将了解责任链是什么、可以做什么、解决什么,以及如何在 MQL5 中运用它。当我们需要处理来自客户端的请求时,如果我们有很多对象,可基于每一个的责任来处理客户端的请求,我们就可以用这种范式来处理这种情况。

尽管使用这种范式具有优势,但我们可能会面临以下陷阱:

  • 长链情况下的效率问题。
  • 由于请求并未指定接收方,因此无法保证请求处理,故可将此请求传递给链中的对象而无需处理。除此之外,我们还需要知道,如果我们没有正确配置的链,则无法处理请求。
范式有什么作用?

该范式在处理请求时把机会分散给许多对象,故在解耦任何请求的发送方和该请求的接收方时非常实用。这是通过遍历接收对象链条,并将请求传递给每一个对象,检查哪个对象可处理请求。

以下是该范式的结构图:

责任链

正如我们在上图中所见,我们有以下参与者:

  • Client:客户端正在发起请求,并由链条中的对象处理。
  • Handler:它定义了处理请求的接口。它还实现了后继者链接。
  • ConcreteHandler:它是对象,基于其职责处理请求,它有权访问后继者,当它无法处理请求时,则可以传递给后继者。

设计范式解决了什么问题?

如果满足以下条件,就可以使用该范式:

  • 我们有许多对象可以处理请求。
  • 我们需要将发送方和接收方解耦。
  • 我们需要生成一条请求,在不提及接收者的情况下由众多对象之一接收处理。
  • 我们有一组动态指定的对象来处理请求。

故此,该范式可以解决以下问题:

  • 降低耦合,因为它有助于解耦发送方和接收方,这意味着它有助于应用的独立修改。
  • 它为分配对象和派发责任授予了灵活性。

我们如何在 MQL5 中运用它?

在这一部分中,我们将学习如何在 MQL5 中运用该范式来创建有效的 MetaTrader5 软件,那么以下就是在 MQL5 中为责任链编码的步骤:

使用 namespace 关键字声明 Chain_Of_Responsibility 区域,包含范式的函数和变量

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.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)

在这一部分中,我们将辨别另一种行为范式,即命令范式,也称为动作(Action)和业务(Transaction)。该范式有助于将请求封装在对象之中,这令我们能够在不更改发送方或接收方的情况下为不同的请求设置参数,这意味着在发送方、处理器、和接收方之间,正在进行解耦。当我们在类中含有庞大函数时,这非常实用。该范式还支持撤消操作。

与大多数事情一样,使用该范式时也存在一些陷阱,它们如下所示:

  • 它通常与其它范式结合使用。
  • 它会产生大量的类和对象,以便处理所有不同的命令或请求。
  • 为每个命令创建一个对象是违背面向对象设计原则的。
范式有什么作用?

简单地说,它创建一个封装的调用程序,接收命令,并将其发送至接收方。

以下是命令范式的图形:

命令

正如我们在上一张命令范式结构图中所见,我们有以下参与者:

  • Command:该命令可以声明执行操作的接口。
  • ConcreteCommand:该 ConcreteCommand 在接收方和操作或命令之间创建一个链接,此外,它还通过调用接收方上的相应操作来实现执行。
  • Client:这可以应用两件事:创建 ConcreteCommand,和设置其接收方。
  • Invoker:它接收命令去执行请求。
  • Receiver:它标识与请求执行关联的操作方法,它可以是任何类。

设计范式解决了什么问题?

  • 当我们发现以下适用情况时,就可用该范式:
  • 我们需要依据动作为对象设置参数,以便执行。
  • 我们需要在不同时间指定、排队、并执行一些操作。
  • 我们需要一些可以用来支持撤消操作的东西。
  • 如果系统崩溃,我们需要支持在这种情况下重新应用日志记录更改。
  • 我们需要在基础级之上建立高级操作当作系统结构。

我们如何在 MQL5 中运用它?

在这一部分中,我们将查看一种可在 MQL5 中运用命令范式的方法,以下步骤即为实做:

以 namespace 关键字声明我们的命令空间,指定我们的函数、变量、类......等等

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

声明 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();
  }

声明 Client 类为参与者,创建具体命令,设置为接收器,并运行它

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

故此,我们可以找到以下代码,这是在一个代码模块中的全部代码

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

另一种行为范式,可帮助我们依据给定的语言设置对象之间的交互,并依据解释器定义规则或语法的表述,并可在以后用该表述来解释和翻译该语言的内容。

使用这种范式类型时存在陷阱,如下所示:

  • 规则或语法的复杂性,以及复杂度较高时,这意味着难以维护。
  • 它可在特定状况下使用。
范式有什么作用?

该范式帮助我们描述如何定义语言的语法,表述该语言的内容,并获得对此内容的解释。

如果我们需要查看解释器范式的图形,它与如下相同:

解释器(Interpreter)

正如我们在上图中所见,在该范式中,我们有以下参与者:

  • AbstractExpression:它可以声明抽象解释(上下文)的操作。
  • TerminalExpression:它可以实现语法中与终端品种关联的解释操作,为内容中的每个终端品种创建一个实例作为需求。
  • NonterminalExression:对于语法中的每个规则,都需要一个类,它为每个规则维护 AbstractExpression 的实例变量。它实现了语法中非终端品种的解释操作。
  • Context:它包含解释器的全局信息。
  • Client:它构建抽象语法树,表述语言中的内容,我们需要按语法来定义它,并调用解释的操作。

设计范式解决了什么问题?

正如我们所辨别的,当我们有一种需要解释的语言,且我们可以定义或表述该语言的内容时,就可用该范式。

故此,以下是我们可以使用该范式的最佳情况:

  • 我们有一套简单的语言语法,因为如果语法很复杂,类的层次结构就会变得很大,这可能导致一个不受控的状态。
  • 如果解释器的效率因素不是那么紧要。

故此,通过使用该范式,我们可以获得以下益处:

  • 使用该范式可以轻松、流畅地使用继承来更新或扩展语法。
  • 它也令语法实现变得更容易。
  • 它令添加解释表达式的新方法变得更加容易。

我们如何在 MQL5 中运用它?

在本文的这一部分中,我们将呈现一种编码或利用这种范式的简单方法。以下是在 MQL5 中使用此解释器的步骤:

声明 Interpreter 区域,我们将用它来定义并声明我们的函数、变量和类,与我们已知使用 namespace 关键字相同

namespace Interpreter

声明 Context 类为参与者

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

声明 TerminalExpression 类为参与者,实现 interpret 方法

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

声明 Client 为参与者

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.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)

我们将辨别迭代器设计范式,它是设置对象之间交互、或通信方法的行为范式之一。这种范式有助于聚合对象(如列表)的存在,或者为我们提供了一种以顺序方式访问元素的方法,而无需公开其表述、或内部结构的底层细节。它也被称为光标(Cursor)。

尽管使用迭代器范式时我们可以获得益处,但它存在如下陷阱:

  • 如果我们有一个集合,则无法访问它的索引。
  • 例如,无方向状态集合,在某些情况下若我们需要定向到前一个状态时索引无效,但在某些语言中也有解决方案。
  • 在某些情况下,如果我们创建一个索引,并循环遍历它,它会比使用范式更快。
范式有什么作用?

该范式能以不同方式支持遍历复杂聚合的变体,因为除了为迭代器定义子类,以便支持更新的遍历之外,还可以通过替换迭代器的实例来轻松更新遍历算法。它令聚合的接口变得简单。持续跟踪迭代器的游弋状态,如此我们就能一次性处理多个遍历。

我们可以找到该范式的图形,如下图所示:

迭代器

基于上一张迭代器结构图,我们可以找到以下参与者:

  • Iterator:它定义了可用于访问和遍历元素的接口。
  • ConcreteIterator:它允许实现迭代器的接口,持续跟踪聚合遍历。
  • Aggregate:它有助于识别创建对象迭代器的接口。
  • ConcreteAggregate:它有助于实现迭代器接口,以便获取合适的 ConcreteIterator 实例作为返回。

设计范式解决了什么问题?

当我们拥有以下适用条件时,可用该迭代器范式:

  • 如果我们需要访问聚合对象的内容,但不需要公开它的内部表述。
  • 如果我们需要支持很多聚合对象的遍历。
  • 如果我们有不同的聚合结构,但我们需要提供一个统一的界面来遍历它们。

我们如何在 MQL5 中运用它?

在这一部分中,我们将通过以下步骤学习如何在 MQL5 中使用该范式:

使用预处理器 #define 定义 ERRITERAOR-UT-OF-BOUNDS

#define ERR_ITERATOR_OUT_OF_BOUNDS 1

我们将使用 template 关键字,并在定义的迭代器接口中将 T 声明为 CurrentItem

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

实现迭代器接口,并在声明 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];
  }

实现迭代器创建接口,以便获取合适的具体迭代器的实例作为返回值

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 中运用迭代器范式的完整代码

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

另一种行为设计范式,设置对象如何彼此交互。这种范式是调解器(Mediator)范式,当我们有一组对象,且我们需要定义一个封装对象来描述这组对象如何交互时,就可以使用它。它允许或应用解耦,这可能很实用,令我们能独立地改变对象的交互。

与任何可能具有优点和缺陷的东西一样,以下是调解器设计范式的陷阱:

  • 创建一个调解器应对一切。
  • 它与其它范式一起使用。
范式有什么作用?

根据我们提到的调解器范式的标识,我们可以说它有助于设置对象之间的交互方法,而无需以显式方式提及每个对象。故此,它有助于在对象之间应用解耦。它也可以当作路由器,用于通信管理。

下面是调解器范式的图形,查看其结构:

调解器(Mediator)

调解器(2)

正如我们在上图中看到的范式结构,我们得到以下调解器范式的参与者:

  • Mediator:它标识同事对象之间的通信接口。
  • ConcreteMediator:通过协调同事对象,实现合作行为。它的同事是已知的,可以由 ConcreteMediator 维护。
  • Colleague 类:对象的调解器会由每个同事类知晓。除此之外,每个同事都可以在需要这种通信的任何时候与其调解器沟通,或者与另一位同事沟通。

设计范式解决了什么问题?

通读至此,我们理解了该范式,或者当我们遇到以下问题时,就可用该范式:

  • 我们有一组对象,在定义了某种途径后可以彼此通信,但这种通信的方式很复杂。
  • 我们很难重用一个对象,因为它要与其它不同的对象进行通信。
  • 我们需要定制分布在类之间的行为,而无需创建太多子类。

故此,我们可以这样说:

  • 它有助于限制子类化。
  • 它有助于将同事解耦。
  • 它令对象协议变得简单。
  • 对象之间的合作是抽象的。
  • 控制是集中制。

我们如何在 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.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);
  }


结束语

现在,假设您已经获知有关三种设计范式的信息,这是编程和软件开发中最重要的主题之一。我们在这篇文章中提到了一些行为设计范式,并辨别它们是什么,以及它们如何有助于创建可重用、可扩展、可维护和测试的软件,了解每种范式可以做什么,使用每种范式可以解决的问题或障碍,每种范式的益处和陷阱,以及我们如何利用 MQL5 中的每种范式为 MetaTrader 5 创建有效的交易系统。

我们从行为设计范式中提到了以下范式:

  • 责任(responsibilities)链
  • 命令(Command)
  • 解释器(Interpreter)
  • 迭代器(Iterator)
  • 调解器(Mediator)

如果这是您阅读的第一篇关于设计范式的文章,我建议您阅读我关于《软件开发及 MQL5 中的设计范式(第 1 部分):创造范式,以及《软件开发及 MQL5 中的设计范式(第 2 部分):结构范式》等其它文章,如果您需要了解更多关于其它类型的设计范式的信息,我希望您觉得它们很有用。

我还建议阅读有关设计范式主题的更多信息,因为它将帮助您创建有效的软件,以下是有关该主题的一些有用资源:

  • 《设计范式 — 可重用面向对象软件的元素》,作者:Eric Gamma、Richard Helm、Ralph Johnson 和 John Vlissides
  • 《傻瓜设计范式》,史蒂夫·霍尔兹纳(Steve Holzner)
  • 《头先(Head First)设计范式》,埃里克·弗里曼(Eric Freeman)、伊丽莎白·罗布森(Elisabeth Robson)、伯特·贝茨(Bert Bates)、和凯西·塞拉(Kathy Sierra)

如果您想阅读更多有关使用最流行的技术指标为 MetaTrader 5 创建交易系统的文章,您可以通过我的发布页面查看我的其它文章,我希望您发现它们对您的交易有用,获得有用的见解,并增强您的结果,或拓展您作为开发人员的背景,以便改进您从事的项目。

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/13796

附加的文件 |
Command.mqh (4.04 KB)
Interpreter.mqh (4.83 KB)
Iterator.mqh (2.31 KB)
Mediator.mqh (1.72 KB)
神经网络变得简单(第 66 部分):离线学习中的探索问题 神经网络变得简单(第 66 部分):离线学习中的探索问题
使用准备好的训练数据集中的数据对模型进行离线训练,这种方法虽然有一定的优势,但其不利的一面是,环境信息被大大压缩到训练数据集的大小。这反过来又限制了探索的可能性。在本文中,我们将探讨一种方法,这种方法可以用尽可能多样化的数据来填充训练数据集。
MQL5 简介(第 1 部分):算法交易新手指南 MQL5 简介(第 1 部分):算法交易新手指南
通过我们的 MQL5 编程新手指南,进入算法交易的迷人领域。在揭开自动化交易世界的神秘面纱之际,让我们探索支持MetaTrader 5 的语言 MQL5 的精髓。从了解基础知识到迈出编码的第一步,本文是您即使没有编程背景也能释放算法交易潜力的关键。加入我们的旅程,在令人兴奋的 MQL5 世界里,体验简单与复杂的结合吧。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
Python、ONNX 和 MetaTrader 5:利用 RobustScaler 和 PolynomialFeatures 数据预处理创建 RandomForest 模型 Python、ONNX 和 MetaTrader 5:利用 RobustScaler 和 PolynomialFeatures 数据预处理创建 RandomForest 模型
在本文中,我们将用 Python 创建一个随机森林(random forest)模型,训练该模型,并将其保存为带有数据预处理功能的 ONNX 管道。之后,我们将在 MetaTrader 5 终端中使用该模型。