Search code examples
javadesign-patternsstate-pattern

State Pattern share common knowledge between state object efficient and extandable implementation


I am using the State Pattern, but the examples I found are for educational purpose and I wanted to know what are best practices in this pattern for sharing objects between states, i.e. a boolean, a List and also for one state object to change the state reference in the automata object.

I will lay down a simple model to be used as an example.

public abstract class State {

    Automata automata;

    public State( Automata automata){
         this.automata = automata;
    }

    public abstract void action();

}

public class State1 extends State {

    public State1(Automata automata){
         super(automata)
    }

    @Override
    public void action() {
        // GET  : Use obj1 
        // POST :Set in automata state to State2 
    }
} 

public class State2 extends State {

     public State2(Automata automata){
         super(automata)
    }

    @Override
    public void action() {
        // GET  :Use obj1 
        // POST :Set in automata state to State1 
    }
}  

public class Automata {

     private State state1 = new State1(this);
     private State state2 = new State2(this);
     private State state = state1;

     public void takeAction() {
         state.action()
     }
}

I have tried the following solutions:

  1. For GET store obj1 in Automata and use getters and setters. For POST store states in Automata and use getters and setters. This approach will make the code unnecessarily long by using the getters and becomes hard to maintain with a changing list of obj1's and states.
  2. Use private inner classes. Declare State, State1 and State2 as private inner classes and access directly obj1 and states. Hard to maintain and update simply because of length of file. Can not be shared with another Automata class.
  3. Make fields public. I do not want to expose all of this fields.
  4. Use a singleton/static class approach for sharing obj1's

I am not very found of package private access.

In my design I am combining this pattern with the Template Method Pattern as a side information.

I know a one-fits-all approach does not exist, but what are common best practices in working with this pattern?


Solution

  • I would do something like this:

    public class Automata {
      List<String> someList;
      boolean someBoolean;
      private State currentState;
    
      public void performAction() {
        currentState = currentState.action(this);
      }
    
      interface State {
        State action(Automata context);
      }
    
      enum States implements State {
        IDLE {
          @Override
          public State action(Automata context) {
            if (context.someBoolean) {
              return WORKING;
            }
            return this;
          }
        },
        WORKING {
          @Override
          public State action(Automata context) {
            if (context.someList.size() > 7) {
              return IDLE;
            }
            return this;
          }
        }
      }
    }
    

    The setting of the state is now done in the performAction() of Automata, no need to do this from within each state. I used enum as the implementations of the state because they are great when you want to implement the state as a pure function. However, if your states themselves are not stateless you may want to implement the states as static inner classes.