I think I was misunderstood.
Maybe this C# example will make things more clear.
In terms of OO capabilities they are almost the same thing.
@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() { } };
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"); } }
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.
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)
//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 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
![MQL5 - Language of trade strategies built-in the MetaTrader 5 client terminal](https://c.mql5.com/i/registerlandings/logo-2.png)
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
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