September 18, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Working With Design Patterns: State

  • June 19, 2008
  • By Jeff Langr
  • Send Email »
  • More Articles »

I refactor my code slowly, running tests continually. The first class I create is the HoldingState class (see Listing 4), an abstract class intended to provide null behavior for each of the possible events.

Listing 4: HoldingState.

// HoldingState
import java.util.*;

abstract class HoldingState {
   protected final Holding holding;
   HoldingState(Holding holding) {
      this.holding = holding;
   }
   void checkout(Date date, String patronId) {
   }
   void checkin(Date date) {
   }
   void placeHold(Date date, String patronId) {
   }
   void update(Date date) {
   }
}

I then create state derivatives, one for each of the three states. After I create the derivatives, I begin to move code over from the Holding class. For each event (checkout, checkin, placeHold, update), the job of the Holding object is simply to delegate the event to the current HoldingState object. For example, the checkout method in Holding ends up reading:

public void checkout(Date date, String patronId) {
   state.checkout(date, patronId);
}

Because these are simple delegations, I could consider using the Java proxy mechanism. Right now, I'm not concerned about a rapidly growing public interface on Holding, so I'll defer that enhancement.

States that need to do something with the checkout event call an action method and/or transition to another state. Other states may choose to ignore that event. One interesting result of implementing the state pattern, though, is that it may help point out a potential problem in the system by making ignored events apparent. For example, my CheckedOut state object provides no behavior for the checkout event. That unhandled event triggers me to think that I must determine what should happen if someone does try to check out a book twice.

The benefit from moving all the code into the state objects is that the conditionals begin to disappear. When in the CheckedInHeld state, I no longer have to check whether or not a book is held before I proceed:

class CheckedInHeld extends HoldingState {
   // ...
  @Override
   public void checkout(Date date, String patronId) {
      if (patronId != holding.holdPatron)
         throw new HoldException();
      holding.doCheckout(date, patronId);
      holding.state = new CheckedOut(holding);
   }
   // ...
}

Previously, the code in checkout in Holding had to ask whether or not the book was already held. Because this code is executing when the Holding references the CheckedInHeld state, that question no longer needs to be asked.

The state classes are shown in Listing 5. Each of the state classes is small, simple, and easily could be tested directly.

Listing 5: State derivatives.

// CheckedOut.java
import java.util.*;

class CheckedOut extends HoldingState {
   CheckedOut(Holding holding) {
      super(holding);
   }

   @Override
   public void checkin(Date date) {
      holding.doCheckin(date);
      holding.state = new CheckedIn(holding);
   }

   @Override
   public void placeHold(Date date, String patronId) {
      holding.doHold(date, patronId);
      holding.state = new CheckedOutHeld(holding);
   }
}

// CheckedIn.java
import java.util.*;

class CheckedIn extends HoldingState {
   CheckedIn(Holding holding) {
      super(holding);
   }

   @Override
   public void checkout(Date date, String patronId) {
      holding.doCheckout(date, patronId);
      holding.state = new CheckedOut(holding);
   }

   @Override
   public void placeHold(Date date, String patronId) {
      holding.doHold(date, patronId);
      holding.state = new CheckedInHeld(holding);
   }
}

// CheckedInHeld.java
import java.util.*;

public class CheckedInHeld extends CheckedIn {
   CheckedInHeld(Holding holding) {
      super(holding);
   }

   @Override
   public void checkout(Date date, String patronId) {
      if (patronId != holding.holdPatron)
         throw new HoldException();
      holding.doCheckout(date, patronId);
      holding.state = new CheckedOut(holding);
   }

   @Override
   public void placeHold(Date date, String patronId) {
      throw new HoldException();
   }

   @Override
   public void update(Date date) {
      holding.doReleaseOldHold(date);
   }
}

// CheckedOutHeld.java
import java.util.*;


public class CheckedOutHeld extends HoldingState {
   CheckedOutHeld(Holding holding) {
      super(holding);
   }

   @Override
   public void checkin(Date date) {
      holding.doCheckin(date);
      holding.state = new CheckedInHeld(holding);
   }

   @Override
   public void placeHold(Date date, String patronId) {
      throw new HoldException();
   }
}




Page 3 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel