Extending State Pattern - Example Code
This is an example toy implementation of state pattern extended with secondary transition stimuli. We dicussed in the last post that a context is more than a wrapper here, and thus it provides an interface to states to raise secondary events. As handling of the secondary events would vary from concrete context to concrete context, state is provided with an interface of an abstract context, as shown below:
class AbstractContext
{
public:
typedef std::map
InstanceTable_t;
virtual ~AbstractContext();
virtual void
raiseSecondaryEvent(
const SecondaryEvent::SecondaryEvent_t&
) = 0;
void activateState(const AbstractState::State_t& stateType);
void onEvent_1();
void onEvent_2();
void onEvent_3();
protected:
AbstractContext();
AbstractState* getStateInstance(
const AbstractState::State_t& type);
virtual AbstractState*
createStateInstance(
const AbstractState::State_t& type
)const = 0;
AbstractState::State_t getCurrentStateType() const;
private:
AbstractState* state_;
InstanceTable_t instances_;
};
A concrete context will have an implementation of the transition table mapping (current state, secondary event) -> next state; naturally a simple representation of the table would be an stl::map. Notice that states were instantiated with a factory method createStateInstance(), which is defined in concreteContext and used from AbstractContext::getStateInstance(); to use this on demand instantiation pattern properly we always have to use this accessor function getStateInstance() to get any state instance.
#include <map>
#include "AbstractContext.hpp"
class ConcreteContext : public AbstractContext
{
public:
typedef
std::map,
AbstractState::State_t> TransitionTable_t;
ConcreteContext();
virtual void
raiseSecondaryEvent(
const SecondaryEvent::SecondaryEvent_t& event
);
protected:
void populateTransitions();
AbstractState* createStateInstance
(const AbstractState::State_t& type) const;
private:
TransitionTable_t transitionTable;
};
Abstract state interface is typical. See concreteState classes to check how we are raising secondary events.
class AbstractContext;
class AbstractState
{
public:
enum State_t
{
State_1 = 1,
State_2 = 2
};
virtual ~AbstractState(){}
virtual void onEvent_1(AbstractContext* context) = 0;
virtual void onEvent_2(AbstractContext* context) = 0;
virtual void onEvent_3(AbstractContext* context) = 0;
virtual State_t stateType() const = 0;
protected:
AbstractState(){}
};
class ConcreteState_1 : public AbstractState
{
public:
ConcreteState_1(){}
virtual void onEvent_1(AbstractContext* context)
{
std::cout << "Process primary event-1 in ConcreteState-1";
std::cout << std::endl;
}
virtual void onEvent_2(AbstractContext* context)
{
std::cout << "Process primary event-2 in ConcreteState-1";
std::cout << std::endl;
}
virtual void onEvent_3(AbstractContext* context)
{
std::cout << "Process primary event-3 in ConcreteState-1";
std::cout << std::endl;
context->raiseSecondaryEvent(SecondaryEvent::Trigger_1);
}
virtual AbstractState::State_t stateType() const
{
return AbstractState::State_1;
}
};
class ConcreteState_2 : public AbstractState
{
public:
ConcreteState_2(){}
virtual void onEvent_1(AbstractContext* context)
{
std::cout << "Process primary event-1 in ConcreteState-2";
std::cout << std::endl;
}
virtual void onEvent_2(AbstractContext* context)
{
std::cout << "Process primary event-2 in ConcreteState-2";
std::cout << std::endl;
}
virtual void onEvent_3(AbstractContext* context)
{
std::cout << "Process primary event-3 in ConcreteState-2";
std::cout << std::endl;
context->raiseSecondaryEvent(SecondaryEvent::Trigger_2);
}
virtual AbstractState::State_t stateType() const
{
return AbstractState::State_2;
}
};
Now the cpp definitions for AbstractContext and its concrete derived class -
AbstractContext.cpp :
AbstractContext::AbstractContext() : state_(NULL)
{
}
AbstractContext::~AbstractContext()
{
InstanceTable_t::iterator it = instances_.begin();
InstanceTable_t::iterator itEnd = instances_.end();
while(it != itEnd)
{
delete it->second;
++it;
}
instances_.clear();
}
void AbstractContext::onEvent_1()
{
state_->onEvent_1(this);
}
void AbstractContext::onEvent_2()
{
state_->onEvent_2(this);
}
void AbstractContext::onEvent_3()
{
state_->onEvent_3(this);
}
void
AbstractContext::activateState(
const AbstractState::State_t& stateType)
{
state_ = getStateInstance(stateType);
assert(state_ != NULL);
}
AbstractState*
AbstractContext::getStateInstance(const AbstractState::State_t& type)
{
AbstractState* stateInstance = NULL;
InstanceTable_t::const_iterator it =
instances_.find(type);
if(it != instances_.end())
{
stateInstance = it->second;
}
else
{
stateInstance = createStateInstance(type);
instances_[type] = stateInstance;
}
return stateInstance;
}
AbstractState::State_t AbstractContext::getCurrentStateType() const
{
assert(state_ != NULL);
return state_->stateType();
}
ConcreteContext.cpp :
ConcreteContext::ConcreteContext()
{
populateTransitions();
}
void ConcreteContext::raiseSecondaryEvent(
const SecondaryEvent::SecondaryEvent_t& event)
{
TransitionTable_t::iterator it =
transitionTable.find(
std::make_pair(getCurrentStateType(), event));
if(it != transitionTable.end())
{
activateState(it->second);
}
}
void ConcreteContext::populateTransitions()
{
transitionTable[std::make_pair(AbstractState::State_1,
SecondaryEvent::Trigger_1)] = AbstractState::State_2;
transitionTable[std::make_pair(AbstractState::State_2,
SecondaryEvent::Trigger_2)] = AbstractState::State_1;
}
AbstractState*
ConcreteContext::createStateInstance(
const AbstractState::State_t& type) const
{
AbstractState* state = NULL;
switch(type)
{
case AbstractState::State_1:
state = new ConcreteState_1();
break;
case AbstractState::State_2:
state = new ConcreteState_2();
break;
default:
assert(false);
break;
}
return state;
}
Now client code would just create a ConcreteContext, activate initial state and expose itself to primary events - that’s it.
No Comments »
RSS feed for comments on this post.
| TrackBack URI
You can also bookmark
this on del.icio.us or check the cosmos