3

I have a state machine like code, which has state A,B and C, with a function changeState(char i) which can change the state of a system, and each state can also call changeState(char i) to switch the system to another state:

struct State{
    State(char aName){
        this->name=aName;
    }
    char name;
};

void changeState(char i);

struct A : public State{
    A():State('A'){
        changeState('B');
    }
};

struct B : public State{
    B():State('B'){
    changeState('C');
    }
};

struct C : public State{
    C():State('C'){
    }
};

State* state;
void changeState(char i){
    switch(i){
        case 'A':
            state=new A();
            break;
        case 'B':
            state=new B();
            break;
        case 'C':
            state=new C();
            break;
    }
}

Now this code has at least 2 problems:

  1. if I do not set the state as global, I need to handle the memory management of each states, which increases the complexity of the code

  2. if recursive state change occurs, the final state is not as I expected:

Suppose I want to trigger state change, start from A to C (to simplify the code now the state change occurs in constructor only, but in real code it is in other method which calls after constructor):

int main(){
    changeState('A');
    printf("%c\n",state->name);
    return 0;
}

the flow is : changeState('A')->new A()->changeState('B')->new B()->changeState(C)->new C(), I want the last state be C, but now is A.

My question is, what is the alternative software architecture or design pattern if my state machine may change state recursively? or, if possible, how to modify current code to overcome the problem?

  • 6
    It sounds like what you have is simply not a finite state machine at all. State transitions are only caused by new input, not by other state transitions. If transition X always causes transition Y as well, then what you really have is one transition that includes the effects of both X and Y. Also, because memory management sucks, avoid using raw pointers/new whenever possible. In this case, I don't see why you wouldn't simply use an enum for the current state. – Ixrec Dec 22 '15 at 2:24
  • The state changes can be triggered both by user input and by state, I want some states have the ability to change to another state because at some situations the state can be automatically redirected to other state. – ggrr Dec 22 '15 at 2:33
2

First of all, I'd always avoid global state unless it is absolutely necessary. It makes extended your code hard, testing your code hard, and debugging your code hard. For the expense of having a state machine object that is passed to each state when it's created? Definitely not.

But I don't think it's necessary anyway. Your code has another antipattern that you haven't highlighted: your State objects cause side-effects in their constructors, which is a seriously bad idea. I'd fix both problems with a single change: have a class StateMachine with a method:

void changeState(State * requested)
{
    State * alternative;
    while (alternative = requested-> getAlternative ())
        requested = alternative;
    currentState = requested;
}

Where getAlternative is a virtual method in State that returns null, and can be overridden by each subclass to return a different state if necessary.

Once this is done, you can allocate a single instance of each State subclass and use those rather than calling "new" all the time, which will save the possibility of memory leaks (which look like they could have been a problem in your design).

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.