Laboratory: State Pattern
CSC 207 Algorithms and Object Oriented Design Spring 2011

Summary: In this lab, you will get a concrete look at a brief motivating example of the state pattern.

Background

The state pattern is useful for modeling, well, the state of something. Namely, we need an easy way to switch the behavior of an object based on something we're calling "state."

For example, say we want to design a multilingual application. There will need to be all manner of messages that we'll need to print as output, and each of these translated in to the appropriate language. The particular message that's printed will depend on a language setting (the state).

The following code gives a short example using two message types in four different languages.

MultiLingualMessages.java
/** A class that prints out messages of various types in several
 * languages.
 *
 * @author Jerod Weinman
 */
public class MultiLingualMessages {

  /**
   * An enumerated type explicitly listing the possible languages.
   */
  public enum Language {English, French, Spanish, Chef};

  /** Object member representing the language */
  private Language lang;

  /** Return the language of this object.
   *
   * @return the language of this object
   */
  public Language getLanguage() { return lang; }

  /** Set the language of this object. */
  public void setLanguage(Language lang) {
    this.lang = lang;
  }

  /** Print a question in the appropriate language */
  public void printQuestion() {

    String msg = "";

    switch(lang) {
    case English: msg = "What?"; break;
    case Spanish:
    case French: msg = "Que?"; break;
    case Chef: msg = "Bork?"; break;
    default: throw new IllegalStateException("Unknown current language.");
    }

    System.out.println(msg);
  }

  /** Print an exclamation in the appropriate language */
  public void printExclamation() {
    
    String msg = "";

    switch(lang) {
    case English: msg = "Whoah!"; break;
    case Spanish: msg = "íCaramba!"; break;
    case French: msg = "Par bleu!"; break;
    case Chef: msg = "Bork!"; break;
    default: throw new IllegalStateException("Unknown current language.");
    }

    System.out.println(msg);
  }
}

Exercises

Exercise 1

Do you see why this is generally a painful design to work with?
  1. If you had to print 100 messages instead of just two, what code would you have to repeat over and over?
  2. Let's say you wanted to add a language after you'd added those extra 98 messages. How many methods would you have to change?
  3. Hopefully you see that this is a design that's not fun for you because of the amount of code you have to write. What risks are there when you have to make modifications (in many places) to existing code?
Be prepared to discuss your answers as a class.

Exercise 2

Let's see if we can begin to solve this problem. The state pattern we will use features the following elements: We'll clarify what these mean in the next few exercises.
  1. Create an interface called Messenger that has two operations, one for fetching an exclamation String and another for fetching the question String. (Note that your return types should not be void.)
  2. Now, create two classes that implement this interface for two languages of your choice. For instance, you might call them FrenchMessenger and ChefMessenger. You only need to write two, one-line methods for each, and you need no object or class variables.

Exercise 3

So what's this context thing all about? Well, this is where we finally make use of the language messengers we pulled out above. Instead of a enum Language variable representing our state, as it does in MultiLingualMessages above, we can use a Messenger object. Thus, the class below provides a "context" for a particular messenger, replacing our original class MultiLingalMessage.
MessengerContext.java
/** A class that prints out messages of various types in several
 *  languages using the state pattern. 
 *
 * @author Jerod Weinman
 * @author YOUR NAME HERE
*/
public class MessengerContext 
{

  private Messenger messenger;


  /**
   * Set the messenger for this context to the specified value.
   *
   * @param msgr the specified value to use for this context.
   */
  public void setMessenger(Messenger msgr) 
  {
    messenger = msgr;
  }


  /** Print a question using the messenger state of this context. */
  public void printQuestion() 
  {
    // BODY HERE
  }

  /** Print an exclamation using the current messenger state of this context. */
  public void printExclamation() 
  {
    // BODY HERE
  }
}
  1. Fill in the body of the two print methods, based on your Messenger interface.
  2. You have only implemented two languages. How hard is it now to add two more compared to the original class, MultiLingualMessages?
  3. How many methods do you have to change to add a language? How does this compare to the number that will remain unchanged?
Be prepared to discuss your answers as a class.

For those with extra time

Build a chain of responsibility.
Created: Jerod Weinman, 6 January 2009
Modified: Jerod Weinman, 17 January 2011
Copyright © 2009-2011 Jerod Weinman.
CC-BY-NC-SA
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License .