Tuesday, May 4, 2010

State Pattern

The state pattern is a behavioral object design pattern. The idea behind the state pattern is for an object to change its behavior depending on its state. In the state pattern, we have a Context class, and this class has a State reference to a Concrete State instance. The State interface declares particular methods that represent the behaviors of a particular state. Concrete States implement these behaviors. By changing a Context's Concrete State, we change its behavior. In essence, in the state pattern, a class (the Context) is supposed to behave like different classes depending on its state. The state pattern avoids the use of switch and if statements to change behavior.
Let's look at an example of the state pattern. First off, we'll define the EmotionalState interface. It declares two methods, sayHello() and sayGoodbye().

EmotionalState.java

package com.cakes;

// State
public interface EmotionalState {

 public String sayHello();

 public String sayGoodbye();

}
The HappyState class is a Concrete State that implements sayHello() and sayGoodbye() of EmotionalState. These messages are cheerful (representing a happy state).

HappyState.java

package com.cakes;

// Concrete State
public class HappyState implements EmotionalState {

 @Override
 public String sayGoodbye() {
  return "Bye, friend!";
 }

 @Override
 public String sayHello() {
  return "Hello, friend!";
 }

}
The SadState class also implements the EmotionalState interface. The messages are sad (representing a sad state).

SadState.java

package com.cakes;

//Concrete State
public class SadState implements EmotionalState {

 @Override
 public String sayGoodbye() {
  return "Bye. Sniff, sniff.";
 }

 @Override
 public String sayHello() {
  return "Hello. Sniff, sniff.";
 }

}
The Person class is the Context class. It contains an EmotionalState reference to a concrete state. In this example, we have Person implement the EmotionalState reference, and we pass the calls to Person's sayHello() and sayGoodbye() methods on to the corresponding methods on the emotionalState reference. As a result of this, a Person object behaves differently depending on the state of Person (ie, the current EmotionalState reference).

Person.java

package com.cakes;

// Context
public class Person implements EmotionalState {

 EmotionalState emotionalState;

 public Person(EmotionalState emotionalState) {
  this.emotionalState = emotionalState;
 }

 public void setEmotionalState(EmotionalState emotionalState) {
  this.emotionalState = emotionalState;
 }

 @Override
 public String sayGoodbye() {
  return emotionalState.sayGoodbye();
 }

 @Override
 public String sayHello() {
  return emotionalState.sayHello();
 }

}
The Demo class demonstrates the state pattern. First, it creates a Person object with a HappyState object. We display the results of sayHello() and sayGoodbyte() when the person object is in the happy state. Next, we change the person object's state with a SadState object. We display the results of sayHello() and sayGoodbyte(), and we see that in the sad state, the person object's behavior is different.

Demo.java

package com.cakes; public class Demo { public static void main(String[] args) { Person person = new Person(new HappyState()); System.out.println("Hello in happy state: " + person.sayHello()); System.out.println("Goodbye in happy state: " + person.sayGoodbye()); person.setEmotionalState(new SadState()); System.out.println("Hello in sad state: " + person.sayHello()); System.out.println("Goodbye in sad state: " + person.sayGoodbye()); } } The console output of executing Demo is shown here.

Console Output

Hello in happy state: Hello, friend! Goodbye in happy state: Bye, friend! Hello in sad state: Hello. Sniff, sniff. Goodbye in sad state: Bye. Sniff, sniff. Note that we don't necessarily need to have the Context (ie, Person) implement the EmotionalState interface. The behavioral changes could have been internal to the Context rather than exposing EmotionalState's methods to the outside. However, having the Context class implement the State interface allows us to directly access the different behaviors that result from the different states of the Context.

No comments:

Post a Comment