vendredi 7 juillet 2017

Passing data into a state pattern

Assume following situation: I have several small actions, which each do one thing. They all derive from one action base class like this:

class Action
{
public:
     virtual ~Action() = default;
     virtual void doSomething() = 0;
     virtual std::unique_ptr<Action> switchState() = 0;
};

class specificAction
    : public Action
{
public:
    SpecificAction(/*parameter*/){/*...*/};
    void doSomething()
    {
        // do specific thing
    }

    std::unique_ptr<Action> switchState()
    {
        // Set pointer to next state
        return std::make_unique<NextAction>(/*parameters for this next state*/);
    }
};

Some of those actions are then combined to routines. There are different routines, which differ in the combination of actions (order, ammount etc.). I thought this would be ideal for a state pattern, where every routine has a member pointer to the current action. After one action is done, the switchState method sets the pointer to the next action, depending on the calling routine etc.:

class Routine
{
    // ...
public:
    void start()
    {
        while(routineNotComplete())
        {
            current->doSomething();
            current = current->switchState();
        }
    }
protected:
    std::unique_ptr<Action> current;
};

class SpecificRoutine
    : public Routine
{
public:
    SpecificRoutine(/*additional parameter for this specific routine*/)
    {
        // init data for routine
    }
private:
    // some important data for this routine
};

Now the problem: in most cases, the parameter, which every action needs are generated in the last action and everything is fine. However the state machine also needs data from "outside" (from the calling routine -> the "important data"). The problem occurs, if e.g. state nr. 10 needs some of those data, but state 1 to 9 do not. For me there are these possibilities:

  1. Give the data to the initial state and carry them through the states, until they are needed. -> very much useless overhead, many different constructors for actions, which are part of many different routines.

  2. Pass a reference or pointer to the calling routine to every action. Then those actions can access the necessary data from the calling routine directly. However to do this, the action needs to be friend of the routine and the generic Routine pointer has to be casted to the concrete derived type. Those two things do not look like a clean design for me?! Would look something like this:

    void setContext(Routine* caller); current->setContext(this);

  3. Include several virtual methods in the routine base class, which provide the specific data in a derived routine, if needed.-> many useless methods.

  4. put the needed data somewhere globally, for every action to access if needed.-> bad design, not very easy to follow the logic?!

Resulting from this thoughts, the actual question: Is there any better solution with the existing design? Or is the design overall too bad for this use case? If yes, which approach would be better?

Please Note: I tried to keep the example code as reduced as possible. I hope I did not miss anything.

Aucun commentaire:

Enregistrer un commentaire