
软件开发和 MQL5 中的设计范式(第 4 部分):行为范式 2
概述
在本文中,我们将终结行为设计范式,并终结软件设计范式的主题,以及如何在 MQL5 中运用它们。在之前的文章中,我们已经通过《软件开发中的设计范式和 MQL5(第一部分):创建模式》一文辨别了所有创建范式,并通过《软件开发中的设计范式和 MQL5(第 2 部分):结构范式》一文辩证过所有结构范式。然后,我们在上一篇文章《软件开发中的设计范式和 MQL5(第 3 部分):行为范式 1》中辩证过一些行为范式,它们如下:责任链、命令、解释器、迭代器、和调解器。我们还辩证过什么是设计范式,其可定义和管理对象之间的通信方法。
在本文中,我们将通过以下主题完成行为范式中剩余的内容:
- Memento:它可用于恢复对象以前捕获并存储的状态,并将对象的内部状态外部化,而不会违反封装。
- Observer:它可用于定义对象之间的一对多依赖关系,以便当一个对象更改其状态时,其所有依赖关系都会收到通知,并自动更新。
- State:它可用于对象内部状态发生变化时改变其行为。该对象似乎更改了其对应类。
- Strategy:它可用于辩证算法家族,封装它们,并令它们可互换。该策略允许算法依据其客户端独立变化而选用之。
- Template 方法:它可用于辩证操作中算法的基础,保留一些步骤给子类,并允许子类在不改变算法结构的情况下重新辩证它们。
- Visitor:它可用于辩证新操作,而不会对执行该操作的元素类产生任何影响。
- 结束语
我们之前已经辨别过,行为范式是与对象之间责任的分配,并辨别有关的行为范式。它们还辩证对象如何相互通信或交互。它们刻画一条在运行时难以遵循的复杂控制流。它们允许您专注于对象的连接方式,从而将您的注意力从控制流上移开。
如果您已经阅读了我在本系列中的前几篇文章,您将熟知我们会通过以下几点来呈现每种范式的方式:
- 范式有什么作用?
- 该模式解决了什么问题?
- 我们如何在 MQL5 中运用它?
重点要提的是,如果您理解 OOP(面向对象编程)主题,这将有助于很好地理解设计范式主题,如果您想阅读或学习该主题的一些内容,您还可以阅读我之前的 《解读 MQL5 面向对象编程(OOP)》 这篇文章。我希望您发现我的文章对您提高编程技能的旅程很实用,因为它确定了最重要的主题之一,即设计模式来编写干净、可扩展、可重用和经过充分测试的代码。
备忘录(Memento)
在本章节中,我们将辩证另一种行为设计范式,即备忘录范式。备忘录范式可将对象的状态外部化,以便提供回滚功能,它也称为令牌。
范式有什么作用?
当我们需要存储对象的状态快照,以便稍后恢复对象时,以及当获取状态的直接接口会暴露执行的细节,并破坏对象的封装时,我们可以使用备忘录范式。故此,该范式将捕获稍后要恢复的对象状态,并将其外部化,以下是该范式的结构图,展示其工作原理:
正如我们在上图中所见,我们有以下参与者:
- Memento:它根据需要存储状态,由存储其状态的发起者对象自行决定它不授予对象的访问权限,但发起者除外。它也许会根据发起者的自由裁量权,存储尽可能多、或尽可能少的发起者内部状态。它有两个窄或宽的界面,具体取决于看守者或发起者的观点。
- Originator:创建包含当前内部状态快照的备忘录,并可用该备忘录恢复内部状态。
- Caretaker:它负责保存备忘录,且不检查备忘录的内容。
使用这种类型的范式存在一些陷阱,它们与下面列出的相同:
- 如果有大型副本,它可能会很昂贵。
- 由于快照的卡槽数量有限,因此会丢失历史记录。
- 除备忘录外,它不会暴露任何信息。
设计范式解决了什么问题?
通过使用该范式,可以解决以下问题:
- 它保留了封装边界。
- 它标识窄接口和宽接口。
- 它可用于简化发起者。
我们如何在 MQL5 中运用它?
在这一部分中,我们将尝试在 MQL5 中运用备忘录范式,这可按以下步骤来完成:
使用 class 关键字声明 Memento 类
class Memento { protected: string m_state; public: string GetState(void); void SetState(string); Memento(string); }; Memento::Memento(string state): m_state(state) { } string Memento::GetState(void) { return m_state; } void Memento::SetState(string state) { m_state=state; }
声明 Originator 类
class Originator { protected: string m_state; public: void SetMemento(Memento& memento); Memento* CreateMemento(void); string State(void); void State(string); }; void Originator::SetMemento(Memento& memento) { m_state=memento.GetState(); } Memento* Originator::CreateMemento(void) { return new Memento(m_state); } void Originator::State(string state) { m_state=state; } string Originator::State(void) { return m_state; }
声明 Caretaker 类
class Caretaker { public: Memento* memento; ~Caretaker(void); }; Caretaker::~Caretaker(void) { if(CheckPointer(memento)==1) { delete memento; } }
如此,以下是在 MQL5 中运用备忘录范式的完整代码:
//+------------------------------------------------------------------+ //| Memento.mqh | //+------------------------------------------------------------------+ class Memento { protected: string m_state; public: string GetState(void); void SetState(string); Memento(string); }; Memento::Memento(string state): m_state(state) { } string Memento::GetState(void) { return m_state; } void Memento::SetState(string state) { m_state=state; } class Originator { protected: string m_state; public: void SetMemento(Memento& memento); Memento* CreateMemento(void); string State(void); void State(string); }; void Originator::SetMemento(Memento& memento) { m_state=memento.GetState(); } Memento* Originator::CreateMemento(void) { return new Memento(m_state); } void Originator::State(string state) { m_state=state; } string Originator::State(void) { return m_state; } class Caretaker { public: Memento* memento; ~Caretaker(void); }; Caretaker::~Caretaker(void) { if(CheckPointer(memento)==1) { delete memento; } }
观察者(Observer)
在这一部分中,我们将辩证另一种行为范式,即观察者范式。它定义了对象之间的一对多依赖关系,如此当一个对象更改其状态时,其所有依赖关系都将收到通知、并自动更新。它也称为从属(Dependents)和推送订阅(Publish-Subscribe)。
范式有什么作用?
当我们需要在单独的对象中封装抽象的各个方面,从而能够独立地变化和重用它们时;当需要在更改一个对象时一并更改其它对象,并且我们不知道需要更改的对象数量时;以及当我们需要对象向其它对象发送通知,而这些对象之间不存在耦合时,我们就可以使用观察者范式。
基于上述内容,该范式可以如下图所示,以便更好地理解该范式的工作原理:
正如我们在上图中所见,我们可得到在这个范式中的参与者,如下所示:
- Subject:观察者审视的主题,以及该主题受到的观察者数量。为了附加和剥离观察者,主题为此提供了接口。
- Observer:它标识对象的更新接口,当主题变化时需要发出通知。
- ConcreteSubject:它将状态保存或存储到 ConcreteObserver 对象,当状态发生变化时,它会向观察者发送通知。
- ConcreteObserver:扮演的角色针对以下三个意向:
- 维护对 ConcreteSubject 对象的引用。
- 保存状态。
- 执行观察者接口更新。
使用观察者范式时存在缺陷,它们如下所示:
- 可观察对象不会标识哪个对象更新了其状态。
- 大型更新。
- 调试可能很困难。
设计范式解决了什么问题?
根据我们对 Observer 范式工作方式的理解,我们可以说它能够解决、或者在运用它时会得到以下益处:
- 它允许主题和观察者之间的抽象耦合。
- 它为广播交互或通信提供支持。
我们如何在 MQL5 中运用它?
现在,是时候看看在 MQL5 中运用观察者范式的方法了,它将与以下内容相同:
使用 interface 关键字声明 Observer 区域,以便判定我们需要的类和函数
interface Observer { void Update(string state); };
使用 class 关键字声明 Subject
class Subject { public: Subject(void); ~Subject(void); void Attach(Observer* observer); void Detach(Observer& observer); void Notify(void); void State(string state); string State(void) {return m_state;} protected: string m_state; Observer* m_observers[]; int Find(Observer& observer); }; Subject::Subject(void): m_state(NULL) { } Subject::~Subject(void) { int itotal=ArraySize(m_observers); for(int i=0; i<itotal; i++) { Observer* item=m_observers[i]; if(CheckPointer(item)==1) { delete item; } } } void Subject::State(string state) { m_state=state; } void Subject::Notify(void) { int itotal=ArraySize(m_observers); for(int i=0; i<itotal; i++) { m_observers[i].Update(m_state); } } void Subject::Attach(Observer *observer) { int size=ArraySize(m_observers); ArrayResize(m_observers,size+1); m_observers[size]=observer; } void Subject::Detach(Observer &observer) { int find=Find(observer); if(find==-1) return; Observer* item=m_observers[find]; if(CheckPointer(item)==1) delete item; ArrayRemove(m_observers,find,1); } int Subject::Find(Observer &observer) { int itotal=ArraySize(m_observers); for(int i=0; i<itotal; i++) { Observer* item=m_observers[i]; if(item==&observer) return i; } return -1; }
使用 class 关键字声明 ConcreteSubject
class ConcreteSubject:public Subject { public: void State(string state); string State(void) {return m_state;} }; void ConcreteSubject::State(string state) { m_state=state; }
使用 class 关键字声明 ConcreteObserver
class ConcreteObserver:public Observer { public: void Update(string state); ConcreteObserver(ConcreteSubject& subject); protected: string m_observer_state; ConcreteSubject* m_subject; }; ConcreteObserver::ConcreteObserver(ConcreteSubject& subject): m_subject(&subject) { } void ConcreteObserver::Update(string state) { m_observer_state=state; }
如此,以下是在 MQL5 中运用观察者行为设计范式的完整代码:
//+------------------------------------------------------------------+ //| Observer.mqh | //+------------------------------------------------------------------+ interface Observer { void Update(string state); }; class Subject { public: Subject(void); ~Subject(void); void Attach(Observer* observer); void Detach(Observer& observer); void Notify(void); void State(string state); string State(void) {return m_state;} protected: string m_state; Observer* m_observers[]; int Find(Observer& observer); }; Subject::Subject(void): m_state(NULL) { } Subject::~Subject(void) { int itotal=ArraySize(m_observers); for(int i=0; i<itotal; i++) { Observer* item=m_observers[i]; if(CheckPointer(item)==1) { delete item; } } } void Subject::State(string state) { m_state=state; } void Subject::Notify(void) { int itotal=ArraySize(m_observers); for(int i=0; i<itotal; i++) { m_observers[i].Update(m_state); } } void Subject::Attach(Observer *observer) { int size=ArraySize(m_observers); ArrayResize(m_observers,size+1); m_observers[size]=observer; } void Subject::Detach(Observer &observer) { int find=Find(observer); if(find==-1) return; Observer* item=m_observers[find]; if(CheckPointer(item)==1) delete item; ArrayRemove(m_observers,find,1); } int Subject::Find(Observer &observer) { int itotal=ArraySize(m_observers); for(int i=0; i<itotal; i++) { Observer* item=m_observers[i]; if(item==&observer) return i; } return -1; } class ConcreteSubject:public Subject { public: void State(string state); string State(void) {return m_state;} }; void ConcreteSubject::State(string state) { m_state=state; } class ConcreteObserver:public Observer { public: void Update(string state); ConcreteObserver(ConcreteSubject& subject); protected: string m_observer_state; ConcreteSubject* m_subject; }; ConcreteObserver::ConcreteObserver(ConcreteSubject& subject): m_subject(&subject) { } void ConcreteObserver::Update(string state) { m_observer_state=state; }
状态(State)
在这一部分中,我们将辩证状态范式,该法式允许对象在其内部状态发生变化的情况下改变其行为,且对象将出现更改其类。它也被称为状态对象。当对象的行为取决于其状态时,我们就可用它基于运行时状态更改对象的行为。当我们的操作需配合大型条件语句时,也可以使用它,所有这些都取决于对象的状态。
范式有什么作用?
如果我们需要了解状态行为范式是如何工作的,我们可以看看如下图形:
正如我们在上图中所见,我们有以下参与者:
Context:它标识了客户感兴趣的接口。它还维护标识当前状态的 ConcreteState 实例的子类。
State:它标识接口,以便封装上下文特定状态的行为。
ConcreteState 子类:子类执行上下文状态的行为,这是针对每个子类。
根据我们所说的状态范式,当我们有一个对象的行为取决于其当前状态时,就可以使用它。但是使用这种范式有一个陷阱,那就是我们将有更多的类,这意味着更多的代码。
设计范式解决了什么问题?
尽管我们在使用状态范式时存在相同的陷阱,但它也有一些优点,可用它来解决所研究的问题。这些优点与如下相同:
- 它有助于特定于每个状态的行为本地化,并基于每个状态的特征来剥离行为。
- 它显式定义了不同状态之间的转换。
- 它有助于共享状态对象。
我们如何在 MQL5 中运用它?
以下步骤是关于如何在 MQL5 中运用状态范式的方法:
声明 Context 类
class Context;
声明 State 接口
interface State { void Handle(Context& context); };
声明 m_state 对象
State* m_state;
声明 Context 类
class Context { public: Context(State& state); ~Context(void); State* State(void) {return m_state;} void State(State& state); void Request(void); }; Context::~Context(void) { if(CheckPointer(m_state)==1) delete m_state; } void Context::State(State& state) { delete m_state; m_state=&state; } void Context::Request(void) { m_state.Handle(this); }
声明 ConcreteStateA 类
class ConcreteStateA:public State { public: void Handle(Context& context); }; void ConcreteStateA::Handle(Context& context) { context.State(new ConcreteStateB); }
声明 ConcreteStateB 类
class ConcreteStateB:public State { public: void Handle(Context& context); }; void ConcreteStateB::Handle(Context& context) { context.State(new ConcreteStateA); }
如此,以下是在 MQL5 中使用状态设计范式的一个代码模块:
//+------------------------------------------------------------------+ //| State.mqh | //+------------------------------------------------------------------+ class Context; interface State { void Handle(Context& context); }; State* m_state; class Context { public: Context(State& state); ~Context(void); State* State(void) {return m_state;} void State(State& state); void Request(void); }; Context::~Context(void) { if(CheckPointer(m_state)==1) delete m_state; } void Context::State(State& state) { delete m_state; m_state=&state; } void Context::Request(void) { m_state.Handle(this); } class ConcreteStateA:public State { public: void Handle(Context& context); }; void ConcreteStateA::Handle(Context& context) { context.State(new ConcreteStateB); } class ConcreteStateB:public State { public: void Handle(Context& context); }; void ConcreteStateB::Handle(Context& context) { context.State(new ConcreteStateA); }
策略(Strategy)
这是另一种行为范式,即策略范式。它识别一组算法,封装它们,并令它们可互换。这允许算法独立于使用它的客户端而变化。因此,当我们需要在运行时启用算法时,以及当我们需要消除条件语句时,我们就可以使用它。它也被称为政策(Policy)。
如此,我们可以说,在以下情况下,我们可以使用策略范式:
- 许多相关类仅在行为方式上有所不同。策略提供了一种配置方法,可将类配置为遵照多种方式之一运行。
- 我们需要使用同一算法的不同变体。并且我们需要能够在运行时选择算法。
- 算法是数据的运用,且客户不应直接访问数据。
- 类定义许多行为,在其操作中显示为多个条件语句。它可以位于单独的策略类中,比列举许多相关的条件分支更好。
- 条件语句消除。
范式有什么作用?
如果我们打算了解这种策略范式是如何工作的,我们可以通过下图形来实现:
正如我们在上图中所见,策略范式的参与者如下:
- Strategy:它声明上下文所用的所有受支持算法的通用接口,以便能够依据 ConcreteStrategy 标识调用算法。
- ConcreteStrategy:通过使用 Strategy 的接口,上下文实现算法。
- Context:它由 ConcreteStrategy 对象构造,它维护对策略对象的引用,并且为了能让策略访问其数据,它可以识别该接口。
尽管使用该策略范式可得到诸多益处,但也存在陷阱,它们与以下内容相同:
- 客户必须了解策略。
- 类的数量增加。
设计范式解决了什么问题?
以下是使用策略范式的后果或好处:
- 它有助于应用可重用性,因为它有助于识别上下文可重用的相关算法组。
- 它可以被视为支持各种行为的另一种方式,取代子类化。
- 它有助于消除条件语句的策略。
- 它允许人们在相同行为的不同实现之间进行选择。
我们如何在 MQL5 中运用它?
现在,我们将辩证一种使用策略范式的方法,该方法与以下步骤相同:
声明 Strategy 接口,以便其内部的类和函数:
interface Strategy { void AlgorithmInterface(void); };
声明 Context 类
class Context { public: Context(Strategy& strategy); ~Context(void); void ContextInterface(void); protected: Strategy* m_strategy; }; Context::Context(Strategy& strategy) { m_strategy=&strategy; } Context::~Context(void) { if(CheckPointer(m_strategy)==1) delete m_strategy; } void Context::ContextInterface(void) { m_strategy.AlgorithmInterface(); }
声明 ConcreteStrategyA 类
class ConcreteStrategyA : public Strategy { public: void AlgorithmInterface(void); }; void ConcreteStrategyA::AlgorithmInterface(void) { }
声明 ConcreteStrategyB 类
class ConcreteStrategyB : public Strategy { public: void AlgorithmInterface(void); }; void ConcreteStrategyB::AlgorithmInterface(void) { }
声明 ConcreteStrategyC 类
class ConcreteStrategyC : public Strategy { public: void AlgorithmInterface(void); }; void ConcreteStrategyC::AlgorithmInterface(void) { }
故此,以下代码是一个模块中的完整代码,用于在 MQL5 中运用策略范式:
//+------------------------------------------------------------------+ //| Strategy.mqh | //+------------------------------------------------------------------+ interface Strategy { void AlgorithmInterface(void); }; class Context { public: Context(Strategy& strategy); ~Context(void); void ContextInterface(void); protected: Strategy* m_strategy; }; Context::Context(Strategy& strategy) { m_strategy=&strategy; } Context::~Context(void) { if(CheckPointer(m_strategy)==1) delete m_strategy; } void Context::ContextInterface(void) { m_strategy.AlgorithmInterface(); } class ConcreteStrategyA : public Strategy { public: void AlgorithmInterface(void); }; void ConcreteStrategyA::AlgorithmInterface(void) { } class ConcreteStrategyB : public Strategy { public: void AlgorithmInterface(void); }; void ConcreteStrategyB::AlgorithmInterface(void) { } class ConcreteStrategyC : public Strategy { public: void AlgorithmInterface(void); }; void ConcreteStrategyC::AlgorithmInterface(void) { }
模板(Template)方法
模板方法是一种行为设计范式,在软件开发中很实用。它可用于识别操作中算法的基本组件,并将某些步骤委托给子类。这允许子类在不改变算法的整体结构的情况下重新定义这些步骤。
故此,我们可以说在以下情况下运用它:
- 我们有一个算法,我们只需要在不影响该算法的整体结构的情况下更改它的一些步骤。
- 我们只需要一次性实现算法不变层面,并将可变行为的实现责任委托给子类。
- 我们还要避免代码重复,对于子类的共同行为,应当在公共类中加以考虑,并本地化。
- 我们需要控制子类扩展。
范式有什么作用?
如果我们需要了解该范式是如何工作的,我们可以借助以下图表来做到这一点,这是该范式的表述:
正如我们在上图中所见,我们有以下参与者:
- AbstractClass:它可指定抽象的基元操作,这些操作必须由具体的子类定义,以实现算法的不同步骤。它还可用于执行算法框架概括的模板方法。该模板方法调用基元操作,和 AbstractClass 中指定的或属于其它对象的操作。
- ConcreteClass:它可用于执行特定于子类的算法步骤所需的基本操作。
设计范式解决了什么问题?
使用模板方法行为范式时,会有一些益处,并解决一些问题,这些益处和问题如下:
- 使用模板方法允许我们创建可重用的代码,因为这些方法是代码重用的基本技术,尤其是在类库中。
- 它可以让我们在不改变算法结构的情况下改变算法的步骤,正如我们提到的。
尽管有这些好处,但在使用这种范式时也存在一个陷阱,即所有类都必须无一例外地遵循该算法。
我们如何在 MQL5 中运用它?
如果我们需要在 MQL5 中使用模板方法范式,我们可以通过以下步骤,作为实现其作用的方法:
使用 class 关键字声明 AbstractClass
class AbstractClass { public: virtual void PrimitiveOperation1(void)=0; virtual void PrimitiveOperation2(void)=0; virtual void TemplateMethod(void); }; void AbstractClass::TemplateMethod(void) { PrimitiveOperation1(); PrimitiveOperation2(); }
使用 class 关键字声明 ConcreteClass
class ConcreteClass : public AbstractClass { public: void PrimitiveOperation1(void); void PrimitiveOperation2(void); }; void ConcreteClass::PrimitiveOperation1(void) { } void ConcreteClass::PrimitiveOperation2(void) { }
如此,以下代码模块是在 MQL5 中运用模板方法范式的完整代码:
//+------------------------------------------------------------------+ //| Template_Method.mqh | //+------------------------------------------------------------------+ class AbstractClass { public: virtual void PrimitiveOperation1(void)=0; virtual void PrimitiveOperation2(void)=0; virtual void TemplateMethod(void); }; void AbstractClass::TemplateMethod(void) { PrimitiveOperation1(); PrimitiveOperation2(); } class ConcreteClass : public AbstractClass { public: void PrimitiveOperation1(void); void PrimitiveOperation2(void); }; void ConcreteClass::PrimitiveOperation1(void) { } void ConcreteClass::PrimitiveOperation2(void) { }
访客(Visitor)
这是最后一种行为范式,即访客范式。这样可以识别新操作,而不会影响执行该操作的元素类。当我们有一个对象结构,其中包含许多不同的对象和接口类,并且我们需要针对这些对象执行操作时,我们就可以使用这种范式,我们有许多不同的、不相关的操作需要执行在相应对象结构上,并且希望避免这些操作对其类的“污染”, 对象类的结构我们很少改变定义,但您确实有在结构上定义新操作的需求。
范式有什么作用?
在下图中,我们可以看到访客范式是如何通过其结构工作的:
正如我们在上图中所见,我们有以下参与者:
- Visitor:对于对象结构中的每个 ConcreteElement 类,都会声明一个 “Visit” 操作。操作的名称和签名能唯一标识某个类,其发送请求给访问的访客。这种设计允许通过其特定接口直接访问元素,以便访客可以判定正在访问的具体元素类别。
- ConcreteVisitor:它执行访客声明的每个操作的实现。每个操作都实现算法的片段,由结构中对象的相应类来定义。具体访客为算法提供上下文,并存储其局部状态。这种状态通常是在结构迭代过程中的结果累积。
- Element:指定接受操作,取一位访客作为参数。
- ConcreteElement:它执行一个接受操作,取访客作为参数。
- ObjectStructure:它能够列出其元素,它也许会提供一个高级接口,令访客能够访问其元素,诸如列表或集合,它既可以是综合型、亦或集合型。
设计范式解决了什么问题?
根据我们提到的访客范式,我们可以发现在应用、或使用它时,可以解决或具有以下好处:
- 它令添加新操作变得如此容易。
- 它允许我们区分相关和不相关的操作,因为它收集了相关、及独立的不相关操作。
我们如何在 MQL5 中运用它?
如果我们需要在 MQL5 中使用访客范式,以下是执行此操作的方法步骤:
声明 Visitor 接口
interface Visitor;
声明 Element 类
class Element { protected: Visitor* m_visitor; public: ~Element(void); virtual void Accept(Visitor* visitor)=0; protected: void Switch(Visitor* visitor); }; Element::~Element(void) { if(CheckPointer(m_visitor)==1) delete m_visitor; } void Element::Switch(Visitor *visitor) { if(CheckPointer(m_visitor)==1) delete m_visitor; m_visitor=visitor; }
声明 ConcreteElementA 类
class ConcreteElementA : public Element { public: void Accept(Visitor*); void OperationA(void); }; void ConcreteElementA::OperationA(void) { } void ConcreteElementA::Accept(Visitor *visitor) { Switch(visitor); visitor.VisitElementA(&this); }
声明 ConcreteElementB 类
class ConcreteElementB : public Element { public: void Accept(Visitor* visitor); void OperationB(void); }; void ConcreteElementB::OperationB(void) { } void ConcreteElementB::Accept(Visitor *visitor) { Switch(visitor); visitor.VisitElementB(&this); }
使用 interface 关键字声明 Visitor,并在其中定义 VisitElementA 和 VisitElementB
interface Visitor { void VisitElementA(ConcreteElementA*); void VisitElementB(ConcreteElementB*); };
声明 ConcreteVisitor1 类
class ConcreteVisitor1 : public Visitor { public: void VisitElementA(ConcreteElementA* visitor); void VisitElementB(ConcreteElementB* visitor); }; void ConcreteVisitor1::VisitElementA(ConcreteElementA* visitor) { visitor.OperationA(); } void ConcreteVisitor1::VisitElementB(ConcreteElementB* visitor) { visitor.OperationB(); }
声明 ConcreteVisitor2 类
class ConcreteVisitor2 : public Visitor { public: void VisitElementA(ConcreteElementA*); void VisitElementB(ConcreteElementB*); }; void ConcreteVisitor2::VisitElementA(ConcreteElementA* visitor) { visitor.OperationA(); } void ConcreteVisitor2::VisitElementB(ConcreteElementB* visitor) { visitor.OperationB(); }
如此,以下代码模块是查看 MQL5 中访客范式的完整代码:
//+------------------------------------------------------------------+ //| Visitor.mqh | //+------------------------------------------------------------------+ interface Visitor; class Element { protected: Visitor* m_visitor; public: ~Element(void); virtual void Accept(Visitor* visitor)=0; protected: void Switch(Visitor* visitor); }; Element::~Element(void) { if(CheckPointer(m_visitor)==1) delete m_visitor; } void Element::Switch(Visitor *visitor) { if(CheckPointer(m_visitor)==1) delete m_visitor; m_visitor=visitor; } class ConcreteElementA : public Element { public: void Accept(Visitor*); void OperationA(void); }; void ConcreteElementA::OperationA(void) { } void ConcreteElementA::Accept(Visitor *visitor) { Switch(visitor); visitor.VisitElementA(&this); } class ConcreteElementB : public Element { public: void Accept(Visitor* visitor); void OperationB(void); }; void ConcreteElementB::OperationB(void) { } void ConcreteElementB::Accept(Visitor *visitor) { Switch(visitor); visitor.VisitElementB(&this); } interface Visitor { void VisitElementA(ConcreteElementA*); void VisitElementB(ConcreteElementB*); }; class ConcreteVisitor1 : public Visitor { public: void VisitElementA(ConcreteElementA* visitor); void VisitElementB(ConcreteElementB* visitor); }; void ConcreteVisitor1::VisitElementA(ConcreteElementA* visitor) { visitor.OperationA(); } void ConcreteVisitor1::VisitElementB(ConcreteElementB* visitor) { visitor.OperationB(); } class ConcreteVisitor2 : public Visitor { public: void VisitElementA(ConcreteElementA*); void VisitElementB(ConcreteElementB*); }; void ConcreteVisitor2::VisitElementA(ConcreteElementA* visitor) { visitor.OperationA(); } void ConcreteVisitor2::VisitElementB(ConcreteElementB* visitor) { visitor.OperationB(); }
结束语
阅读本文和本系列中的其它文章之后,您就能够识别所有类型的设计范式,即范式(创建、结构、和行为)。我们通过了解它如何工作,何时可以使用它,使用每种范式后可以解决哪些问题,以及如何在 MQL5 中使用这些范式来编写干净的代码,这意味着我们得到了可维护、可重用、并经过充分测试的代码。
我们已辩证范式,如下相同:
- 创建(Creational)范式
- 抽象工厂(Abstract Factory)
- 建造器(Builder)
- 工厂方法(Factory Method)
- 原型(Prototype)
- 单例(Singleton)
- 结构性范式
- 适配器(Adapter)
- 桥(Bridge)
- 复合(Composite)
- 装饰(Decorator)
- 外观(Facade)
- 蝇量级(Flyweight)
- 代理(Proxy)
- 行为(Behavioral)范式
- 责任(Responsibility)链
- 命令(Command)
- 解释器(Interpreter)
- 迭代器(Iterator)
- 调解器(Mediator)
- 备忘录(Memento)
- 观察者(Observer)
- 状态(State)
- 模板(Template)
- 访客(Visitor)
您可以通过单击以下链接阅读本系列中的其它文章:
我怎么强调理解设计范式主题的重要性都不为过,因为它在创建任何软件时都非常有用,正如我们所提到的,故我建议阅读有关此主题的更多信息,以及以下有关同一主题的一些参考资料:
- 《设计范式 — 可重用面向对象软件的元素》,作者:Eric Gamma、Richard Helm、Ralph Johnson 和 John Vlissides
- 《傻瓜设计范式》,史蒂夫·霍尔兹纳(Steve Holzner)
- 《头脑先行(Head First)设计范式》,埃里克·弗里曼(Eric Freeman)、伊丽莎白·罗布森(Elisabeth Robson)、伯特·贝茨(Bert Bates)、和凯西·塞拉(Kathy Sierra)
我希望本篇和系列文章对您提高编程技能,特别是 MQL5 的旅程有所帮助。如果您需要阅读更多有关 MQL5 编程的文章,以及如何根据最流行的技术指标(例如,移动平均线、RSI、布林带、和 MACD)创建交易系统,您可以查看我的出版物页面,我希望您发现它们对改善您的交易和编程背景也很实用。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/13876



