Thursday, March 3, 2011

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
What does it mean for an object to "appear to change its class?" Think about it from the perspective of a client: if an object you are using can completely change its behavior, then it appears to you that the object is actually instantiated from another class. In reality, however you know that we are using composition to give the appearance of a class change by simply referencing different state objects.
Motivation
  • State charts are often used to describe dynamic behavior
  • The "traditional" way to implement state transitions often involves complicated, nested if statements
      For example the behaviour of an object Room's isDark() is different when the values of property light changes.
public boolean isDark()
{
if(swichLight().equalsIgnoreCase("off"))
return true;
else
return false;
}


Let us look at detailed example:
Consider the life of a person, it has 4 states - childhood, boyhood, youth and old-age. So
now if you are youth, you do something different and when you are boy you do different.
So the logic uses nested ifs to find what the state is and then perform logic.
//With out using StateDesign Pattern



public class Life
{
final static int CHILD = 0;
final static int BOY = 1;
final static int YOUTH = 2;
final static int OLD = 3;

int state = CHILD;

public void children(){
if(state == CHILD){
System.out.println("Go and Play...");
state = BOY;
}
else if(state == BOY)
System.out.println("Don't behave like a kid");
else if(state == YOUTH)
System.out.println("Don't behave like a kid");
else if(state == OLD)
System.out.println("Don't behave like a kid");
}

public void schoolBoy(){
if(state == CHILD)
System.out.println("grow up...");
else if(state == BOY){
System.out.println("Its time to go to school");
state = YOUTH;
}
else if(state == YOUTH)
System.out.println("Don't behave like a boy");
else if(state == OLD)
System.out.println("Don't behave like a boy");
}

public void youngGeneration(){
if(state == CHILD)
System.out.println("grow up...");
else if(state == BOY)
System.out.println("grow up...");
else if(state == YOUTH){
System.out.println("Right time to achieve my goals...");
state = OLD;
}
else if(state == OLD)
System.out.println("You are no longer youth");
}

public void oldGeneration(){
if(state == CHILD)
System.out.println("grow up...");
else if(state == BOY)
System.out.println("grow up...");
else if(state == YOUTH){
System.out.println("grow up...");
state = OLD;
}
else if(state == OLD)
System.out.println("I can guide you all");
}

public String toString(){
return state+"";
}

// Other methods...
}

The State Pattern
  • Delegate the responsibility of handling state transitions to encapsulations of the states
  • Participants:
    • An abstract State  
    • Several concrete State classes
    • A Context
By encapsulating each state into a class, we localize any changes that will need to be made.
State transitions can be controlled by State Classes or by Context Classes.

Objects are often discussed in terms of having a "state" that describes their exact conditions in a given time, based upon the values of their properties. The particular values of the properties affect the object's behavior.

Now if we use State Design Pattern here, we can get rid of this messy if else code in Life Class and also make it flexible.

//With State Design Pattern


public interface State{
public void childhood();
public void boyhood();
public void youth();
public void old();
}




public class ChildHoodState implements State{

Life life;

public ChildHoodState(Life life){
this.life = life;
}

public void childhood(){
System.out.println("Go and Play...");
life.setState(life.boyHoodState);
}
public void boyhood(){
System.out.println("Don't behave like a kid");
}
public void youth(){
System.out.println("Don't behave like a kid");
}
public void old(){
System.out.println("Don't behave like a kid");
}
}




public class BoyHoodState implements State{

Life life;

public BoyHoodState(Life life){
this.life = life;
}

public void childhood(){
System.out.println("Grow up.. ");
}
public void boyhood(){
System.out.println("Its time to go to school");
life.setState(life.youthState);
}
public void youth(){
System.out.println("Don't behave like a boy");
}
public void old(){
System.out.println("Don't behave like a boy");
}
}




public class YouthState implements State{

Life life;

public YouthState(Life life){
this.life = life;
}

public void childhood(){
System.out.println("Grow up...");
}
public void boyhood(){
System.out.println("Grow up...");
}
public void youth(){
System.out.println("Right time to achieve my goals...");
life.setState(life.oldState);
}
public void old(){
System.out.println("You are no longer youth");
}
}




public class OldState implements State{

Life life;

public OldState(Life life){
this.life = life;
}

public void childhood(){
System.out.println("Grow up...");
}
public void boyhood(){
System.out.println("Grow up...");
}
public void youth(){
System.out.println("Grow up...");
}
public void old(){
System.out.println("I can guide you all");
}
}




public class Life{
// All the States
State childHoodState;
State boyHoodState;
State youthState;
State oldState;

// initialise them
State state = childHoodState;

public Life(){
childHoodState = new ChildHoodState(this);
boyHoodState = new BoyHoodState(this);
youthState = new YouthState(this);
oldState = new OldState(this);
}

// Delegate to current state

public void children(){
state.childhood();
}

public void schoolBoy(){
state.boyhood();
}

public void youngGeneration(){
state.youth();
}

public void oldGeneration(){
state.old();
}

// for transition of state
public void setState(State state){
this.state = state;
}

// Many other methods needed... including getters and setters of each State

}




public class TestLife{
public static void main(String[] args) {
Life life = new Life();
System.out.println(life);

life.children();
life.schoolBoy();
life.youngGeneration();
life.oldGeneration();

System.out.println(life);


}
}


What we are doing here is implementing the behaviours that are appropriate for the state we are in. In some cases this behaviour includes moving the Life forward to new state.
We will have similar kind of classes for 'YouthState' and 'OldState' also ...
From the above explanation we came to know that State Design Pattern is a fully encapsulated, self-modifying Strategy Design Pattern.

Advantage and Disadvantage

In general think of strategy pattern as a flexible alternative to subclassing; if you use inheritence to define the behaviour of a subclass then you are stuck with that behaviour even if you need to change it. With Strategy you can change the behaviour by composing with a different object.
Disadvantage: By encapsulating state behaviour into seperate classes, you'll always end up with more classes in your design. That's often the price you pay for flexibility.

2 comments:

  1. Your program is not working, can you help me?
    I needed to understand this program

    ReplyDelete

Chitika