Saturday, May 16, 2009

A simple example of the State Design Pattern

Here's the formal definition of the state design pattern :

The State Pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.


The UML diagram :


If you don't understand this obscure definition nor the UML diagram, don't worry. I made a simple example for you. The state pattern is just a clean way for an object to partially change its type at runtime.

Let's take a pizza store. A pizza store is cooking pizza, baking it and delivering it to their clients. Our pizza is the context object with a state.

So here's how you do that the old fashion way :


Pizza.class

public class Pizza {

public final static int COOKED = 0;
public final static int BAKED = 1;
public final static int DELIVERED = 2;

private String name;

int state = COOKED;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getState() {
return state;
}

public void setState(int state) {
this.state = state;
}

public void bake() throws Exception {

if(state == COOKED) {
System.out.print("Baking the pizza...");
state = BAKED;
}
else if(state == BAKED) {
throw new Exception("Can't bake a pizza already baked");
}
else if(state == DELIVERED) {
throw new Exception("Can't bake a pizza already delivered");
}
}

public void deliver() throws Exception {

if(state == COOKED) {
throw new Exception("Can't deliver a pizza not baked yet");
}
else if(state == BAKED) {
System.out.print("Delivering the pizza...");
state = DELIVERED;
}
else if(state == DELIVERED) {
throw new Exception("Can't deliver a pizza already delivered");
}
}
}

The problem with this implementation is that everything is going messy when you have a lot of state. Moreover, the add of a new state is not that simple.

Let's see the re-factored example using the State Design Pattern.

Firstable, we have to write the state interface. This interface will describe the different transitions.


PizzaState.class

public interface PizzaState {

void bake() throws Exception;

void deliver() throws Exception;
}

Then, we refactor our Pizza object with our new state interface.


Pizza.class

public class Pizza {

PizzaState cookedState;
PizzaState bakedState;
PizzaState deliveredState;

private String name;

//State initialization
private PizzaState state = cookedState;

public Pizza() {
cookedState = new CookedPizzaState(this);
bakedState = new BakedPizzaState(this);
deliveredState = new DeliveredPizzaState(this);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public PizzaState getState() {
return state;
}

public void setState(PizzaState state) {
this.state = state;
}

public void bake() throws Exception {
this.state.bake();
}

public void deliver() throws Exception {
this.state.deliver();
}

public PizzaState getCookedState() {
return createdState;
}

public PizzaState getBakedState() {
return bakedState;
}

public PizzaState getDeliveredState() {
return deliveredState;
}
}

And last but not least, we write the state implementations.


CookedPizzaState.class

public class CookedPizzaState implements PizzaState {

private Pizza pizza;

public CookedPizzaState(Pizza pizza) {
this.pizza = pizza;
}

public void bake() throws Exception {
System.out.print("Baking the pizza...");
pizza.setState(pizza.getBakedState());
}

public void deliver() throws Exception {
throw new Exception("Can't deliver a pizza not baked yet");
}

}

You still have to write BakedPizzaState.class and the DeliveredPizzaState.class.

The state design pattern is one of those you need to know and master. It can help you in complex situations.

15 comments:

  1. Do you work in Dominos pizza by any chance? I need their number in NJ.

    ReplyDelete
    Replies
    1. RU KIDDIN ME!!! here U GO ---> 911 <-- Call this number they would let u know....

      Delete
  2. Perhaps it would be better for the context class to decide the state transitions, so the states did not have to know about each other. In theory then, only the context class would have to change when the state transition logic needed to change.

    ReplyDelete
  3. Nice post. Thanks
    I also like design patterns, and blog on it - Java Jazzle - Design Pattern

    ReplyDelete
  4. It's a helpful template, but it didn't seem to work for me - got "java.lang.NullPointerException" when it tried to change state, resolved this by moving the state initialization to inside the "public Pizza() {...}" constructor (I'm a novice, not sure if that's the ideal way to resolve or not).

    Also, I think the getCookedState() method returning createdState is a typo (should return cookedState)?

    ReplyDelete
  5. Nice post. I recently posted about State pattern using C# and Personal loan as an example. Due to similarities between C# and Java it might be helpful for people to get another example. http://www.nileshgule.com/2012/07/state-design-pattern.html

    ReplyDelete
  6. The null pointer is logical since you assign state cookedState which is still null because the assignment is above the constructor.

    public Pizza() {
    cookedState = new CookedPizzaState(this);
    bakedState = new BakedPizzaState(this);
    deliveredState = new DeliveredPizzaState(this);
    state = cookedState;
    }

    This will remove the nullpointer.

    ReplyDelete
  7. Nice example of the pattern. Thanks.

    ReplyDelete
  8. Very nice example and implementation. As ClarkKent above remarked, moving the state transition higher in the hierarchy would be better. Also, the new Java Enums in JDK 1.6+ would offer more optimal implementation.

    - Josef

    ReplyDelete
  9. How can you declare a PizzaState variable if you don't even implement PizzaState?

    ReplyDelete
  10. Thank you very much for this article. It is useful.

    ReplyDelete
  11. State design pattern works on the concept of state change. Entire process life-cycle can be divided in multiple phases.With completion of each phase process exits from one state and enters in to another state.

    For example In JSF framework entire web request response lifecycle is divided in six phases:

    After completion of every phase process exits from a state and enters into another state. For example after RestoreValuePhase we can say ViewRestored as exit state and RequestApply as entering state .

    So to implement State design pattern It is required to divide entire process in such a way that It can be processed in multiple phases with every phase exit defining a state change.

    Now let's understand this with below code.


    Any project lifecycle can be divided in multiple phases like


    requirementAssessment
    Design
    Development
    QualityAssessment
    Deploy
    Closure



    So these are the phases used in below example

    Rules :

    1. We need to define a class where we can store the current state of the process. NextPhase class in below code is doing that.

    2. We need to define an Interface wherein we can provide the contact method that would be implemented in each phase.In below code ProjectPhase is doing that.


    Learn more about State design pattern here -- State Design Pattern

    http://efectivejava.blogspot.in/2013/09/java-state-design-patten-oops-state.html?utm_source=BP_recent

    ReplyDelete
  12. Your pizza example is very accurate and when explaining association, aggregation and composition even using example like these will give a better understanding to the user about best UML concept in a UML tutorial .

    ReplyDelete