Implementing State Pattern

1.5k Views Asked by At

I am implementing a state pattern in Java for my application and need few clarifications.

The state machine has 5 states State 1 to State 5. The are a total of 5 events(Event1 to Event5) which causes the state transition. Not all events are applicable in all the states. If the event is not applicable in that particular state the application will throw exception.

When the state machine gets initialized it starts with state1.

Following is the interface and the context class.

/*
 Interface defining the possible events in each state.
 Each Implementer will handle event in a different manner. 
*/
public interface State {
 /*
  Handlers for each event. Each Implementer will handle the vent in a different manner.
 */
 public void handleEvent1(StateContext context);
 public void handleEvent2(StateContext context);
 public void handleEvent3(StateContext context);
 public void handleEvent4(StateContext context);
 public void handleEvent5(StateContext context);
 // Method to enter state and do some action.
 public void enter(StateContext context);
 // Method to exit state and do some clean-up activity on exit .
 public void exit(StateContext context);
}

/*
  Context class which will handle the state change and delegate event to appropriate event handler of current state
*/
Class StateContext {

   private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

   private State currentState = null;

   StateContext() {
        currentState = new State1();
   }

   //Handle event1 and pass it to the appropriate event handler for the current state.
   public void handleEvent1() {
      currentState.handleEvent1(); 
   }
       .
       .
       .
   //Handle event5 and pass it to the appropriate event handler for the current state.
   public void handleEvent5() {
      currentState.handleEvent5(); 
   }

   // Method to change the state. 
   // This method will be called by each state when it needs to transit to a new state.
   public void changeState(State newState) {
          accquireLock();
          currentState.exit();
          currentState = newState;
          currentState.enter();           
   }

   // Release read lock and accquire write lock
   public void accquireLock() {
        lock.readLock().unlock()
        lock.writeLock().lock();
   }

   // accquire readlock and release write lock
   public void releaseLock() {
        lock.readLock().lock()
        lock.writeLock().unlock();
   }
}

To make it simple, I have provided implementation for only one state.

public class State1 implements State {
       public void handleEvent1(StateContext context) {
          //Hand1e Event 1
       }
              .
              .
              .
      public void handleEvent5(StateContext context) {
          //Handle Event 5
       }


       public void enter(StateContext context) {
           //Release the lock here
           context.releaseLock();
           /*Here is my question. Is it a  good java practice to expose accquire and release lock in Context object. And use the exposed method here to release lock. 
           */

           // Do some action on entering the state. This may take few seconds to finish

       }  
}

I want to release the lock only after entering state. Also I don't want to hold the lock till enter() finishes. If I hold the lock till enter finishes I cannot handle other events and it may get timed-out.For some events (which don't really change the state) we need to read the state and based on the state we can process them or ignore them. If I don't release lock they cannot be processed.Also in some other cases if an event comes to shutdown(this event changes the state) the state machine while enter() is in progress I cannot handle it. I have to shutdown the state machine immediately since it is not appropriate in continuing the enter() after shutdown event has come.

My Question: Is it good java programming practice to expose the accquireLock and releaseLock as an API in Context class and use them in each state class.

Thanks, Arun

1

There are 1 best solutions below

3
On

To answer the question, if the lock is held by the state "manager" class, then that class should be the one to control the lock access, not any of the actual state classes.

With regards to your statement about holding the lock until enter finishes, that is precisely the point of a lock: you don't want other methods getting involved or interrupting. Instead, you should develop some sort of event queue to catch all events and wait to distribute them if the receiving object is busy. Either that, or make a specific exception for whichever events you know are going to interrupt, so that you can bypass the lock when the specified event(s) are fired.

If you choose to use a method of interrupting, you're going to have to implement a number of checks in each State class's enter method to check if the shutdown event has been triggered. The only other way I see it working is to make each State extend Thread so that you may interrupt/stop it at will, though that does not sound like a valid option in this instance.