Download MetaTrader 5

State design pattern in MQL4?

To add comments, please log in or register
Amir Yacoby
1256
Amir Yacoby  

Hello,

The OO state design pattern takes a context class which has a reference to a state base class, which is subclassed by the state classes that implement the state behaviour of the context class.
At the same time the state subclasses have a refference to the context class which they get as parameter to their methods in order to call the SetState method of the context class.

Does this mean that this pattern can't be implemented in MQL4 (because of the 2 objects that have references one for another)?

 

Here's a brief on the state design pattern:
http://www.tutorialspoint.com/design_pattern/state_pattern.htm 

JD4
1101
JD4  
MQL4 (recently) was reworked to be more like MQL5.  I do not know if it had the OO properties added to it that MQL5 supposedly has from being similar to C++.  Me personally, from what I remember of my Java course, you want to keep interdependency between objects down to a minimum, unless they are in a parent/child relationship, and even then, you only want the object itself to determine what methods are public, so it has ultimate control over it's own state.  As far as changing the behavior based on it's state, I do not remember if that is supposed to be happening in a properly built (follows the OO implementation) object, or if it does, it is very controlled by the object that is changing state.  I would think that minor changes would be allowable.  Like the simple example of a car, if someone steps on the gas pedal, the car goes faster.
Amir Yacoby
1256
Amir Yacoby  

I think I was misunderstood. 

Maybe this C# example will make things more clear.

 

http://www.dofactory.com/net/state-design-pattern 

JD4
1101
JD4  
I understood your first example. As I said, I took Java.  My problem sometimes is in clearly explaining what my mind is seeing and making my hands output it properly.  My point was I am not sure about what of the OO capabilities that were put in MQL5 (because of it being based on C++) are possible, if any, in MQL4.
Amir Yacoby
1256
Amir Yacoby  
I think they are the same in that manner, MQL4 and MQL5. I tried both and seems that you cant make 2 objects have reference to each other.
In terms of OO capabilities they are almost the same thing.
JD4
1101
JD4  
Oh, I think I get it now.  One object can call another object's methods, and possibly have that called object change it's state, and you can have more than one reference refer to a single object.  But you can't have a single reference refer to more than one object.  Again, hopefully my fingers are explaining correctly what my mind is thinking here, but does that look closer to what you were asking?
ydrol
593
ydrol  

@OP Do you mean forward references to a sub class ?

class Context;

class Base {

    void doSomething(Context *refContext ) {
        refContext.setState();
    }

};

class Context : Base {

    Base *refBase;
    public:
    void setState() {
    }

};

Amir Yacoby
1256
Amir Yacoby  

ydrol, I don't know of forward reference in MQL, maybe it can be used (BTW, there's MQL doc on this?).

What I mean is suppose you have an object, Context - which has many states. Lets say some of them are Open,Close,etc... And you want to manage the transitions between the states.
So there is a ready made OO solution for this kind of problem, called State pattern which says the following: 

class Context {
   State           *m_state;            // keeps the current object state - base class of state
   StateOpen *m_state_open;   // subclass of state - a specific state
   StateClose *m_state_close;  // subclass of state - a specific state
   void Context() {
        m_state=new StateOpen(); // initial state is open
        m_state_open=new StateOpen();  // used to transition Context to this state from specific Close Subclass
        m_state_close=new StateClose(); // used to transition Context to this state from specific Open Subclass
   }
   SetState (State *state){       // used to transition the state of the object from within each state subclass
     m_state=state;
   } 
   void Close(){
      m_state.Close(this);       // pass context to the specific state object in order for it to change state to Close.
   }
   void Open(){
      m_state.Open(this);       // pass context to the specific state object in order for it to change state to Open
   }

} 

Class State{  // state of object - base class - abstract class
   virtual void Close(Context *context);
   virtual void Open(Context *context);
}

Class StateOpen : State {   // class to deal operations of Context when Context is in Open mode
  StateOpen() {Print("Object in open mode created");}
  void Close(Context *context){
     Print("moving Context to Close state");
     context.SetState(context.GetCloseState());  // GetClosedState is a getter of m_closed_state in Context
  }
  void Open(Context *context){
    Print("context is already open, cant open");
  }
}

Class StateClose : State {   // class to deal operations of Context when Context is in Close mode
  StateClose() {Print("Object is in Close mode now");}
  void Open(Context *context){
     Print("moving Context to Open state");
     context.SetState(context.GetOpenState());  // GetOpenState is a getter of m_open_state in Context
  }
  void Close(Context *context){
    Print("context is already closed, cant close");
  }
}
Amir Yacoby
1256
Amir Yacoby  
This model can add as many states as you want though, but I cant make it in MQL because StateOpen has reference to Context and Context has reference to StateOpen (and StateClose as well).

as you see, each state of Context is dealt in a different subclass of State. And each state subclass deal differently with the same methods, depending on the state.
When a state subclass needs to change the state of object, it simply uses the Context object to set the new state - like in StateOpen subclass for Close() method - it changes the state to Close.
Amir Yacoby
1256
Amir Yacoby  
Ydrol, it worked with forward reference, thx
Fab
5
Fab  

People, it is a bit sad to leave such interesting conversations unfinished and with non-working code, even after so long time. Especially because some interested guys, like me, can come here looking for reference even after some years. So, if you allow, even as a "blast from the past", here is a working solution for a state design pattern in mql4/5.

It is a silly example, it makes nothing really useful, but perhaps can serve as a template for something significant.

Just as a brief introduction (on the web there are thousand of free pages explaining the usefulness of the gang of four STATE DESIGN PATTERN for avoiding "spaghetti code" nested if cycles or switch statements), this design is useful every time you have to code "states" of things that can be summarized in "state diagrams". For instance you want to code an EA that must perform quite well-isolated and distinguished things when, for instance, trades are running versus when orders are pending, or during some market hours versus other market sessions, or while orders are pending versus when orders are filled, and so on. In such situations, in a "procedural way" you start filling it code with a series of if{ if{ if{ if{ if{... chains, that make everything difficult to follow up and mainly to maintain. Just imagine to encapsulate everything into sort of "subsystems" (actually... objects) that you can treat as such... like "sub-systems" (sub-EA's?) that you can modify and evolve almost independently... and (your life will change???).

Here the silly but working example. It is a "mql-like-library" (= an include) and a "mql-like-client" (a dumb EA).

Include: (StateTemplate.mqh)

//////////////////////DECLARATIONS
//forward declaration, otherwise the compiler doesn't know how to manage it within the
//Breakout declaration itself
class State; 
/**
* This class provides the "cotext" or "controller" class of all the
* state design
*/
class Context {
 public:
                Context();  //Constructor
  virtual void  ~Context(); //Destructor
  //The methods which will be implemented in the various states, which
  //correspond to the transitions into a state diagram
  virtual void  GetDownTick();
  virtual void  GetUpTick();
  //Common method SetCurrent will set the current state
  void  SetCurrent(State* state);
  //The various states are class-wide instances of the State objects
  State* bullish_state_;
  State* bearish_state_;
  //And the state_ one will hold the current state
  State* state_;
};

/**
* This is the generic interface defining how a state should look like
*/
class State {
 public:
  //it contains the methods-transitions, defined with a pointer to the
  //context class
  virtual void GetDownTick(Context* my_ctxt) = 0;
  virtual void GetUpTick(Context* my_ctxt) = 0;
};

/**
* Then we need to define the concrete implementation of the defined states,
* in this case BullishState and BearishState
*/
class BullishState : public State {
 public:
                BullishState();
  virtual       ~BullishState();
  virtual void  GetDownTick(Context* my_ctxt);
  virtual void  GetUpTick(Context* my_ctxt);
};

class BearishState : public State {
 public:
                BearishState();
  virtual       ~BearishState();
  virtual void  GetDownTick(Context* my_ctxt);
  virtual void  GetUpTick(Context* my_ctxt);
};

/////////////////// end of DECLARATIONS

/////////////////// DEFINITIONS

/**
* Let's start constructing the context. The constructor prints out
* its performance for demonstration purposes and instantiates its objects
*/
Context::Context(void) {
  Print("Constructing the context");
  // instantiating the state objects
  bearish_state_ = new BearishState();
  bullish_state_ = new BullishState();
  state_ = bullish_state_; //you must start from somewhere....
}

/**
* Also the destructor is verbose and performs memory clean-up
*/
Context::~Context(void) {
  Print("I am destroying the Context");
  delete  bearish_state_;
  delete  bullish_state_;
  bearish_state_  = NULL;
  bullish_state_  = NULL;
}


/**
* Now we define the method-transitions. In the context, they simply delegate to
* the concrete state's  implementations. Easy!
* The "GetPointer(this)" construct is a horrible mql4 syntactic sugar which is
* absurdely mimicking what is in every other language is the obvious use of the
* keyword "this" (what else could "this" mean if not returning the pointer of
* "this"????). But we can live with inelegance, as long as it works!!!!!
*/
void Context::GetDownTick(void) {
  Print(__FUNCTION__ " was called and transfers action to the concrete state");
  state_.GetDownTick(GetPointer(this));
}

void Context::GetUpTick(void) {
  Print(__FUNCTION__ " was called and transfers action to the concrete state");
  state_.GetUpTick(GetPointer(this));
}

/**
* Finally we define the SetState method, which provides for State transitions,
* of course
* \param *state the pointer to the relevant State object instance
*/
void Context::SetCurrent(State *state) {
  Print(__FUNCTION__ " was called and changes state to ", state);
  state_ = state;
}

/**
* Now we provide the implementation of the concrete states and we are done!
* Constructors and destructors here will be pretty silly and will be just
* made "loud" to speak out that they have been called
*/
BullishState::BullishState(void) {
  Print("BullishState constructed!");
}

BullishState::~BullishState(void) {
  Print("BullishState destroyed!");
}

/**
* And here the implementation of the concrete actions that must be performed,
* conditionally, when we are in the different states. Here, for instance,
* what we do when we get a down tick and we are in the bullish state: we
* print a nice message and we switch to the bearish state (i.e. we instruct
* the context that we need to switch state)
* \param *my_ctxt the pointer to the context that must control the whole machine
*/
void BullishState::GetDownTick(Context *my_ctxt) {
  Print(__FUNCTION__, " called with a down tick");
  Print("I was so glad to be in a bullish state!");
  Print("I have to notify the context that we have to change state");
  my_ctxt.SetCurrent(my_ctxt.bearish_state_);
}

/**
* Here, we do when we get an up tick and we are in the bullish state: we
* print a nice message and we remain into same state
* \param *my_ctxt the pointer to the context that must control the whole machine
*/
void BullishState::GetUpTick(Context *my_ctxt) {
  Print(__FUNCTION__, " called with an up tick");
  Print("I am happy that we remain bullish, and we have nothing else to do!");
  return;
}

/**
* Constructor and destructor have merely demonstration purposes
*/
BearishState::BearishState(void) {
  Print("Constructing BearishState!");
}

BearishState::~BearishState(void) {
  Print("Destroying BearhisState!");
}

/**
* If we come with a down tick when we are in a bearish status the situation is
* reversed: we are happy and we remain in the same status!
* \param *my_ctxt the pointer to the context that must control the whole machine
*/
void BearishState::GetDownTick(Context *my_ctxt) {
  Print(__FUNCTION__, " called with a down tick");
  Print("I am happy that we remain bearish, and we have nothing else to do!");
  return;
}

/**
* If we come with an up tick when we are in a bearish state, we notify the situation
* and we instruct the contexts that we have to change state to bullish state and the
* cycle will go on over and over again...
*/
void BearishState::GetUpTick(Context *my_ctxt) {
  Print(__FUNCTION__, " called with an up tick");
  Print("I was so glad to be in a bearish state!");
  Print("I have to notify the context that we have to change state");
  my_ctxt.SetCurrent(my_ctxt.bullish_state_);
}

The client (Modfy it to #include the previous "include" file with the correct path, wherever you have saved it, please!)

#property version   "1.00"
#property strict

#include <Sandbox/StateTemplate.mqh>
Context* my_sentiment = NULL;
//+---------------------------------------------------------------------------+
//| Expert initialization function                                            |
//+---------------------------------------------------------------------------+
int OnInit() {
  my_sentiment = new Context();
  return(INIT_SUCCEEDED);
}
//+---------------------------------------------------------------------------+
//| Expert deinitialization function                                          |
//+---------------------------------------------------------------------------+
void OnDeinit(const int reason) {
  delete my_sentiment;
  my_sentiment = NULL;
}
//+---------------------------------------------------------------------------+
//| Expert tick function                                                      |
//+---------------------------------------------------------------------------+
void OnTick() {
  if (Close[0] > Close[1]) {
    my_sentiment.GetUpTick();
  } else {
    my_sentiment.GetDownTick();
  }
  return;
}

Attach it on a random chart, let it run for a while, looking at the output in the Expert tab of the terminal. When you are satisfied, remove the Expert from the chart, and open up the logfile with a text editor. This is an extract of the output:

0    08:33:04.669    Expert Sandbox\StateClient USDJPY,M15: loaded successfully
0    08:33:09.639    StateClient USDJPY,M15: Constructing the context
0    08:33:09.639    StateClient USDJPY,M15: Constructing BearishState!
0    08:33:09.640    StateClient USDJPY,M15: BullishState constructed!
0    08:33:09.640    StateClient USDJPY,M15: initialized
0    08:33:11.469    StateClient USDJPY,M15: Context::GetUpTick was called and transfers action to the concrete state
0    08:33:11.469    StateClient USDJPY,M15: BullishState::GetUpTick called with an up tick
0    08:33:11.469    StateClient USDJPY,M15: I am happy that we remain bullish, and we have nothing else to do!
0    08:33:12.156    StateClient USDJPY,M15: Context::GetUpTick was called and transfers action to the concrete state
0    08:33:12.156    StateClient USDJPY,M15: BullishState::GetUpTick called with an up tick
0    08:33:12.156    StateClient USDJPY,M15: I am happy that we remain bullish, and we have nothing else to do!
0    08:33:13.071    StateClient USDJPY,M15: Context::GetUpTick was called and transfers action to the concrete state
0    08:33:13.071    StateClient USDJPY,M15: BullishState::GetUpTick called with an up tick
0    08:33:13.071    StateClient USDJPY,M15: I am happy that we remain bullish, and we have nothing else to do!

....


0    08:33:33.421    StateClient USDJPY,M15: Context::GetDownTick was called and transfers action to the concrete state
0    08:33:33.421    StateClient USDJPY,M15: BullishState::GetDownTick called with a down tick
0    08:33:33.421    StateClient USDJPY,M15: I was so glad to be in a bullish state!
0    08:33:33.421    StateClient USDJPY,M15: I have to notify the context that we have to change state
0    08:33:33.421    StateClient USDJPY,M15: Context::SetCurrent was called and changes state to 2
0    08:33:33.600    StateClient USDJPY,M15: Context::GetUpTick was called and transfers action to the concrete state
0    08:33:33.600    StateClient USDJPY,M15: BearishState::GetUpTick called with an up tick
0    08:33:33.600    StateClient USDJPY,M15: I was so glad to be in a bearish state!
0    08:33:33.600    StateClient USDJPY,M15: I have to notify the context that we have to change state
0    08:33:33.600    StateClient USDJPY,M15: Context::SetCurrent was called and changes state to 3
0    08:33:34.411    StateClient USDJPY,M15: Context::GetDownTick was called and transfers action to the concrete state
0    08:33:34.411    StateClient USDJPY,M15: BullishState::GetDownTick called with a down tick
0    08:33:34.411    StateClient USDJPY,M15: I was so glad to be in a bullish state!
0    08:33:34.411    StateClient USDJPY,M15: I have to notify the context that we have to change state
0    08:33:34.411    StateClient USDJPY,M15: Context::SetCurrent was called and changes state to 2
0    08:33:35.005    StateClient USDJPY,M15: Context::GetDownTick was called and transfers action to the concrete state
0    08:33:35.005    StateClient USDJPY,M15: BearishState::GetDownTick called with a down tick
0    08:33:35.005    StateClient USDJPY,M15: I am happy that we remain bearish, and we have nothing else to do!
0    08:33:35.099    StateClient USDJPY,M15: Context::GetDownTick was called and transfers action to the concrete state
0    08:33:35.099    StateClient USDJPY,M15: BearishState::GetDownTick called with a down tick
0    08:33:35.099    StateClient USDJPY,M15: I am happy that we remain bearish, and we have nothing else to do!
0    08:33:36.935    StateClient USDJPY,M15: Context::GetUpTick was called and transfers action to the concrete state
0    08:33:36.935    StateClient USDJPY,M15: BearishState::GetUpTick called with an up tick
0    08:33:36.935    StateClient USDJPY,M15: I was so glad to be in a bearish state!
0    08:33:36.935    StateClient USDJPY,M15: I have to notify the context that we have to change state
0    08:33:36.935    StateClient USDJPY,M15: Context::SetCurrent was called and changes state to 3
0    08:33:38.606    StateClient USDJPY,M15: uninit reason 1
0    08:33:38.606    StateClient USDJPY,M15: I am destroying the Context
0    08:33:38.606    StateClient USDJPY,M15: Destroying BearhisState!
0    08:33:38.606    StateClient USDJPY,M15: BullishState destroyed!
0    08:33:38.611    Expert StateClient USDJPY,M15: removed

So, that's it!

It might be that my code can be improved, that there could be errors, that I was overlooking many things and whatsoever. I am not a professional programmer, and I am just a self-thaught amateur, so profis are welcome to teach me better. What I have understood so far is that the magic here is that everything happens just because the context passes the pointer of the "right" object over and over as the "handle" to be used in the correct situation at every right point in time. This is elegant and brilliant! And should be also very efficient in terms of performance and memory usage. And it is a little cumbersome to write the first time, but then a dream in terms of maintenance and cleanlyness for complex designs.

Happy if this can help someone sometimes now or in the future!

Bye

To add comments, please log in or register