The State Pattern

Elegantly manage state transitions without large switches or numerous if statements.

We’re all familiar—even nonprogrammers, implicitly—with the notion of a state machine. The machine is anything that can be in one of several discrete states with defined transitions. A simple coffee machine can be only on or off. The Start button turns it on. Another button, or a sensor of some kind, turns it off. It cannot be 72% on, nor can it be in a state of making popcorn or playing music. A media player, whether implemented in hardware or software, can be in any one of several states: stopped, paused, playing, or rewinding. The Play button transitions from the stopped state to the playing state. The Pause button transitions from the playing state to the paused state. The Stop button transitions from either the playing or paused state to the stopped state.

The behavior of the buttons (or Java methods) is contextually dependent upon what state the machine is in. Pressing Stop when the machine isn’t doing anything is ignored. Pressing Start when the machine is running also will usually be ignored.

It’s common to diagram these transitions by using state diagrams similar to the one in Figure 1.

A typical state diagram

Figure 1. A typical state diagram

Figure 1 is for a stateful device such as a media player, but you can imagine the diagram would be similar for, say, 3D printer firmware and many other kinds of devices. And the same general idea would apply for a graphics application, which might have edit and preview modes/states, or for a computer game with player-play, computer-play, and won/lost states.

The notion of a state machine gave rise to—and gave its name to—the State pattern. In this pattern, an object can be in exactly one of a fixed number of states, and transition between them causes changes in the object’s behavior.

State in an Application

You may have noticed that Java documentation refers to “the state of an object,” meaning the contents of the instance fields in an object. This is a related meaning of the word state, and in fact, the two meanings overlap in large applications. The app needs to keep track of the state it’s in, and that information is typically stored in the state of objects. But there is a very specific meaning of the term State pattern, as defined in the “Gang of Four” book Design Patterns: Elements of Reusable Object-Oriented Software, where it was first popularized. The pattern will “allow an object to alter its behavior when its internal state changes. The object will appear to change its class.”

Before exploring this definition of the State pattern and its implementation, though, let’s look at a less-well-structured way to manage the state of a media player. In Figure 1, which is a simplified representation of the solution I’ll implement, notice that the transitions are labeled start, stop, pause, and rewind. I could implement those as methods. I’d also need a field to keep track of what state the object is actually in, using a Java enum. (In older code, you might see a bunch of final static ints or chars defined to track the state.)


enum StateName { STOPPED, PLAYING, PAUSED, REWINDING };
StateName currentStateName;

Then, I might define each of the four methods, using logic like the following code. Because lots of things work only in one or another state, I need to check what state the object is in. So, I end up with a lot of if statements or a giant switch statement in each method.


public void start() {
    if (currentStateName == StateName.STOPPED) {
        currentStateName = StateName.PLAYING;
        startPlay();
    } else if (currentStateName == StateName.PAUSED) {
        currentStateName = StateName.PLAYING;
        resumePlay();
    } else if (currentStateName == StateName.PLAYING) {
        System.out.println("Already playing!");
    } else if (currentStateName == StateName.REWINDING) {
        System.out.println("Wait a while, OK?");
    }
}

I also need the same amount of conditional code in each of the four methods. This becomes a serious maintenance issue when you need to add or change functionality. And seriously, who’s ever worked on a project for a few months and not had to add a feature? If you’re not getting feature-add requests, you probably have no users!

The State pattern suggests a cleaner way to organize the code. To refactor the previous approach using the State pattern, I’ll start by creating an interface called State, and make four instances of it, one for each state the player can be in. First, here’s the interface:


interface State {
    void stop();
    void start();
    void pause();
    void rewind();
    default enterState() {
        // Only some states will need this
    }
}

In Figure 2, I show a generic class diagram for this. The process() method corresponds to the various processing methods such as stop(), start(), and so on. It is common, but not required, to provide a method such as enterState() to be called upon entry into the state, to configure things appropriately.

A typical class diagram in the State pattern

Figure 2. A typical class diagram in the State pattern

The class using the State classes is called the Context class, and the State classes are frequently (but not necessarily) written as inner classes inside the Context class. Note that the term Context class is an important concept in the following explanations.

(By the way, all the code for this article is in my GitHub repository. The file PlayerStateDemo.java is the one I’m discussing here.)

With the State defined, I now define the four states by subclassing. Because the app needs only one instance of each State class, the classes are instantiated as anonymous classes and saved as fields.

However, if your program changes state only rarely, you could create the State subclasses as named classes, configure them in the constructor, and lazily instantiate the State subclass only when switching into it.

Here is the code for the “Stopped” State subclass, which defines how the various operations will be performed when invoked while the player is in the stopped state. In this example, stop() is some low-level hardware control method that might stop a motor moving on a DVD player. The commented-out setIcon() call symbolizes some update to the GUI.


State stoppedState = new State() {
    @Override
    public void enterState() {
        stop();
        // setIcon(Icon.stopped);
    }

    @Override
    public void stop() {
        // Do nothing - already stopped
    }

    @Override
    public void start() {
        currentState = playingState;
        currentState.enterState();
    }

    @Override
    public void pause() {
        // Do nothing - already stopped
    }

        @Override
        public void rewind() {
            currentState = rewindingState;
            currentState.enterState();
        }
};

The last two statements in rewind() show how an object of the Context class can “appear to change its class.” Let’s look at the main program’s methods, such as stop() and start():


    PlayerStateDemo context = new PlayerStateDemo();
    System.out.println("Initial state: " + context.getState());
    // User presses the Start button
    context.start();
    System.out.println("Current state: " + context.getState());
    // User presses the Stop button
    context.stop();
System.out.println("Current state: " + context.getState());

These methods invoke or “delegate to” the current State object’s methods. Just by having a new value assigned to the currentState variable, the program appears to change its class, because different code will be invoked from the same method call based on whichever State object is current. For example, if you call stop() while playing, the player will stop, but if you call the very same method a second time, it will do nothing, because the first invocation invokes the stop() method in playingState while the second calls the stop() method in stopState.

In this example, invalid but harmless operations are silently ignored, because that’s how real media players work. Obviously, there will be some cases where it makes sense to signal invalid usage—for example, by logging or throwing an exception.

You need to ensure that the variable containing the state can never be null; in my example, I use a field initialization to ensure that this is set from the beginning:


State currentState = stoppedState;

While this way of doing things may seem like more code than the maze of if statements shown earlier (in fact it is slightly longer, by line count), it is worthwhile in terms of more-maintainable code. You can more clearly see what goes where, and—just as important—the compiler will probably tell you if you forgot to write a required method for a particular state, due to the interface requirement that all methods be implemented.

The State pattern sorts out the state-specific behavior, making it easier to read and maintain the code and easier to add new states.

There are several possible variations to how you implement the pattern. Depending on the scope of the application, it might be useful to move the interface to be a noninner class of the Context, and have the Context class also implement the interface, so the compiler will check that all the delegation methods are present with the correct arguments and return types. The State classes themselves don’t have to be inner classes at all. If it made sense, they could be separate, package-level visibility classes; the Context class could create them. Here, the Context would typically pass a reference to itself into the constructor, for example:


State shuttingDownState = new ShuttingDownState(this);

This is because the State classes are likely to need access to nonpublic methods and fields in the Context class.

Client code using the Context class does not need access to the actual State object, and typically it should not have access, so as to enforce encapsulation. If necessary, the Context class could provide generic information methods such as isPlaying(), isStopped(), and so on to export information, where it makes sense, without exporting implementation details. But if you don’t need or want encapsulation at this boundary, you could have a method such as this in the Context class:


public State getState() { return currentState; }

For an in-between version (a sort of mild encapsulation), you could use this:


public String getState() { return currentState.getClass().getSimpleName(); }

However, if you have used anonymous classes, note that Class.getSimpleName() returns the empty string on at least the standard JVM, so use getName().

In this example, I’ve used the State code to perform the transitions. The Context class could as easily handle the transitions itself. As with all design patterns, it is just a pattern. Therefore, how you implement it is up to you, as long as you follow the general guidelines.

A Bit of History

The State pattern, like most good patterns, is language-agnostic. I came close to figuring out this pattern a long time ago in a job far, far away, before there were pattern catalogs. Remember that patterns are not designed a priori, but are extracted from working code. One weekend at that long-ago job, I started writing a text-based adventure game in C. I made up a giant C struct—a data type that foreshadowed Java’s class mechanism—and simply assigned a new one whenever the player changed locations. Assigning the struct controlled the outcome of operations such as enter, exit, take, and so on. It had both strings and pointers to functions to handle behaviors; there was even a preprocessor that took a simple text file (this was long before JSON) and generated the C files containing the states. The point here is that the State pattern can be implemented in C or in most modern procedural languages; it’s certainly not limited to C++ and Java.

I’ve partly re-created this in the file GameStateDemo.java (in the same GitHub repository), although it doesn’t have the actual room descriptions. The program is just a demo; the game is playable, but the average time to boredom is on the order of 4.2 seconds. Here is the State class, which covers actions regarding rooms:


abstract class State {
    public abstract void lookAround();
    public abstract void goInside();
    public abstract void goOutside();
    public void quitGame() {
        display("Goodbye!");
        System.exit(0);
    }
}

This simple version of State does not have an activation method, but it does have quitGame(). In a trivial game, it makes sense to allow the user to exit from any state, so I allow that in the default quitGame() method. States could override this, too—for example, by prompting users if they’re holding any valuables or if they want to save the state of the game.

The states in this game are inRoom, inHallway, and so on. Here is the inHallway state:


public State inHallwayState = new State() {
    public void lookAround() {
        display("You are in a hallway. There is a door here");
    }
    public void goInside() {
        display("You are in a room");
        state = inRoomState;
    }
    public void goOutside() {
        display("You are already in the hallway");
    }
};

To see the other states, check out the online codebase.

Conclusion

The State pattern is a good one. It sorts out the state-specific behavior, making it easier to read and maintain than having a long conditional statement in each method. It’s also easier to add new states. Finally, it makes the state transitions explicit inside the Context class and the States—and they can be completely invisible to the outside. Transitions are also atomic, because only a single variable in the Context class, typically with a name like currentState, is changed on a transition.

Try the State pattern the next time you find yourself writing parallel if statements in several methods, or any time an object’s behavior has to change significantly based on what state it’s in. You’ll like the improvement in readability and maintainability that it brings.

This article was originally published in the July/August 2018 issue of Java Magazine.

Ian Darwin

Ian Darwin (@Ian_Darwin) is a Java Champion who has done all kinds of development, from mainframe applications and desktop publishing applications for UNIX and Windows, to a desktop database application in Java, to healthcare apps in Java for Android. He’s the author of Java Cookbook and Android Cookbook (both from O’Reilly). He has also written a few courses and taught many at Learning Tree International.

Share this Page