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

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 what we're calling state.

Say for example we want to design a multilingual application. There will need to be all manner of messages that will need to be printed 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 */
public class MultiLingualMessages {

  public enum Language {English, French, Spanish, Chef};

  private Language lang;

  public Language getLanguage() { return lang; }

  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;
    }

    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!";
    }

    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 you have to make modifications (in many places) to existing code?

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.
MessengerContext.java
/** A class that prints out messages of various types in several
 *  languages using the state pattern. */
public class MessengerContext {

  private Messenger messenger;

  public void setMessenger(Messenger msgr) {
    messenger = msgr;
  }

  public void printQuestion() {
    // BODY HERE
  }

  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?
Jerod Weinman
Created: 6 January 2009