Search code examples
design-patternsstate-pattern

Best way to construct complex states with state pattern?


I have a menu that can be in different states so I decided to use the state pattern. Reading online about the pattern seems like the usual way is to construct states from another state but this will not work for me because I have more complex states which need other dependencies. I could pre-create all states in a context object and then switch states from the context object, but I also need some info from the previous state so it becomes messy too.

     class State1 {
         public State1(Context context, /*some complex constructor*/) {
         }
         public void Action() {
            //Something happend and we want to transition to other state
               
            //Usual way 
            //this will not work because state1 doesnt have 
            //dependencies to construct state2
            //context->SetState(new State2());
            

            //My current way
            //This is better because it works, but you will have to create
            //such functions for every state and context object needs to be 
            //modified if new states are added which is not ideal
            context->SetState2(/*something produced by action in state1*/);
         }
     }   

    class State2 {
         public State2(Context context, /*some complex constructor*/) {
         }
     }   
     
     
    class Context {
      //has current state 
    }

Is this a sign of a bad design?
Is there a clean solution to this or I thinking too much about this?


Solution

  • An important detail missing from the code in the OP is that states in the State design pattern are polymorphic (there is a shared API across all states).

    class State1 implements State {}
    class State2 implements State {}
    class Context { private volatile State currentState; }
    

    In the absence of polymorphic states, there is no State design pattern. Instantiating the states should be handled in the same manner as instantiating any other business objects in an OO application, i.e. according to the Dependency Inversion Principle. With those things in mind... there are different ways to transition between states in the State pattern.

    One way is to manage transitions within the Context. In this scenario, States are decoupled from each other: a State does not know that other States exist. The Context initiates a transition (if necessary) after the handle() (or action()) method returns. This does not necessarily mean the Context is aware of concrete State implementations. The Context may simply have a List of State objects, and transition from one to the next.

    Another way is to manage transitions by linking each State to its successor: each State takes another (abstract) State in its constructor. When it wants to transition, it sets the successor on the Context. In this scenario, the Context is unaware of the transition logic, and does not know how many States are available.