2

I've been reading up on State Machines since its likely I need to use for my next project. Most examples I find online show how to go from StateA to StateB. But what if your next desired state is not an adjacent state? Are there any common patterns/practices to achieve this? Ideally in Java, but I can read other programming languages just as well.

# Example States
WakeUp->Get Dressed->Get Car Keys->Get in Car->Drive to Work->Work

Current State: Get in Car

Problems to solve

# Scenario 1: Desired State == Work
Forgot car keys, so you have to return to previous state and then move forward in states again.

# Scenario 2: Desired State == Work
Have car keys, so move forward in states to get to Desired State.

It's very likely that State Machine may not solve this problem elegantly and I just need to hand-craft the logic, I don't mind, but thought I'd follow a common design pattern to help others understand it.

From the example above, I do not need to worry about 'internal' states, which is also true for the project I'm tackling; just in case that makes a difference in possible solutions.

Community
  • 1
  • 1
Jose Leon
  • 1,615
  • 3
  • 22
  • 30

1 Answers1

6

Here is a simple way to define a state machine.

Define in an enum all the states that you want.

enum StateType {
   WAKE_UP, GET_DRESSED, GET_CAR_KEYS, GET_IN_CAR, DRIVE_TO_WORK, WORK
}

Have a statemachine which controls states, and a state interface which performs an action on the statemachine. The state then returns the next state to go to.

interface State {
    StateType next(StateMachine sm);
}

Implement this state for multiple types

class GetInCarState implements State {
    @Override
    public StateType next(StateMachine  sm) {
        if (sm.hasKeys()) {
            return StateType.DRIVE_TO_WORK;
        }
        return StateType.GET_CAR_KEYS;
    }
}

Now define the State Machine

class StateMachine {
    private Map<StateType, State> states = new HashMap<StateType, State>() {{
        put(StateType.WAKE_UP, new WakeUpState()); 
        put(StateType.GET_DRESSED, new GetDressedState()); 
        put(StateType.GET_CAR_KEYS, new GetCarKeysState()); 
        put(StateType.GET_IN_CAR, new GetInCarState()); 
        put(StateType.DRIVE_TO_WORK, new DriveToWorkState()); 
        put(StateType.WORK, new WorkState()); 
    }};

    private StateType currentState = StateType.WAKE_UP;

    private boolean hasCarKeys;

    public boolean hasKeys() {
        return hasCarKeys;
    }

    public void setHasKeys(boolean hasKeys) {
        hasCarKeys = hasKeys;
    }

    public void update() {
        currentState = states.get(currentState).next(this);
    }
}
flakes
  • 21,558
  • 8
  • 41
  • 88
  • Nice one. Couple of suggestions: (1) StateType.DriveToWork - you've likely meant DRIVE_TO_WORK; (2) you could impove your example by combining `class GetInCarState` and `StateType.DRIVE_TO_WORK` by moving the method `next` inside the enum type - it will eliminate any need of a Map by StateType as well. – bashnesnos Sep 29 '16 at 15:32
  • Thanks for the catch on (1) :) For number two; I was also thinking of having the enum define the `next` method but decided against it. By not coupling the implementation to the enum it's possible to have more than one representation for each state. This could be helpful in creating mock states for testing, and creating similar state machines later without having to create an entirely new enum. – flakes Sep 29 '16 at 15:43
  • 1
    Yeah, that's the big question of all OOP time - to make things abstract or not to make :-) It seems you prefer to abstract first :-) – bashnesnos Sep 29 '16 at 15:49
  • 1
    @bashnesnos yeah. It gets me into trouble sometimes haha – flakes Sep 29 '16 at 16:00