Java programming language logo

State Design Pattern

The State pattern lets an object to change its behavior at run-time by changing its internal state.

The State pattern defines objects, which represent different states. It also defines an object that uses these states and changes behavior by swapping the state objects.

When the State pattern is used, several classes have to be created. This is a drawback of this pattern.

State is a behavioral pattern.

Example

In the next Java example, we are going to use an imaginary gun, which have different states like magazine empty or removed and safety can be on or off. When its trigger is pulled, obviously it has different behavior depending whether it is loaded or not and so on.

The State interface defines the methods those can be used for operating the gun.


package com.programcodex.designpatterns.state;

public interface State {

    void ejectMagazine();

    void pullTrigger();

    void switchSafetyOn();

    void switchSafetyOff();
    
}

Next, we are creating four states. The first is the MagazineRemovedState, and it implements the State interface. It defines how the gun works in this state. What will happen when we try to remove the magazine when it has already been removed? Or what will happen when we pull the trigger without any magazine?

Every state class has a Gun field. In this state, it does nothing, but it will be used in other states.


package com.programcodex.designpatterns.state;

public class MagazineRemovedState implements State {

    private final Gun gun;

    public MagazineRemovedState(Gun gun) {
        this.gun = gun;
    }

    @Override
    public void ejectMagazine() {
        System.out.println("Magazine cannot be ejected because it has already been removed.");
    }

    @Override
    public void pullTrigger() {
        System.out.println("The trigger was pulled, but there is no magazine.");
    }

    @Override
    public void switchSafetyOn() {
        System.out.println("The safety was switched on, but there is no magazine.");
    }

    @Override
    public void switchSafetyOff() {
        System.out.println("The safety was switched off, but there is no magazine.");
    }
}

The gun gets into the SafetyOnState when it gets loaded. So in this state, the magazine can be ejected by the ejectMagazine method. When this happens, the gun gets into MagazineRemovedState also by this method.

The switchSafetyOff method prepares the gun for shooting and sets it into switchSafetyOff state.

Of course, when safety is on and the trigger is pulled, nothing happens. Also the safety cannot be activated again from this state.


package com.programcodex.designpatterns.state;

public class SafetyOnState implements State {

    private final Gun gun;

    public SafetyOnState(Gun gun) {
        this.gun = gun;
    }

    @Override
    public void ejectMagazine() {
        System.out.println("Magazine is ejected.");
        gun.setState(gun.getMagazineRemovedState());
    }

    @Override
    public void pullTrigger() {
        System.out.println("The trigger was pulled,"
                + " but nothing happens while the safety is on.");
    }

    @Override
    public void switchSafetyOn() {
        System.out.println("Safety was already on, so nothing happens.");
    }

    @Override
    public void switchSafetyOff() {
        System.out.println("Safety is off now.");
        gun.setState(gun.getSafetyOffState());
    }
}

From the safety on state, our imaginary gun can get into the safety off state. In this state, the pullTrigger method has the most interesting behavior because this time the gun actually fires. When a bullet leaves, the bullet counter is decremented. If the gun runs out of bullets, its state becomes MagazineEmptyState.


package com.programcodex.designpatterns.state;

public class SafetyOffState implements State {

    private final Gun gun;

    public SafetyOffState(Gun gun) {
        this.gun = gun;
    }

    @Override
    public void ejectMagazine() {
        System.out.println("Magazine cannot be ejected while the safty is off.");
    }

    @Override
    public void pullTrigger() {
        gun.decrementBulletCounter();
        System.out.format("FIRE! %d bullet%s left.%n", gun.getBulletCounter(),
                gun.getBulletCounter() > 1 ? "s" : "");
        if (gun.getBulletCounter() == 0) {
            gun.setState(gun.getMagazineEmptyState());
        }
    }

    @Override
    public void switchSafetyOn() {
        System.out.println("Safety is on now.");
        gun.setState(gun.getSafetyOnState());
    }

    @Override
    public void switchSafetyOff() {
        System.out.println("Safety is already off, so nothing happens.");
    }
}

When the gun runs out of bullets, its state becomes MagazineEmptyState. In this state, the only thing what we can do is ejecting the magazine. The ejectMagazine method also sets the gun's state to MagazineRemovedState.

In this state, the other methods do nothing useful.


package com.programcodex.designpatterns.state;

public class MagazineEmptyState implements State {

    private final Gun gun;

    public MagazineEmptyState(Gun gun) {
        this.gun = gun;
    }

    @Override
    public void ejectMagazine() {
        System.out.println("The magazine is ejected.");
        gun.setState(gun.getMagazineRemovedState());
    }

    @Override
    public void pullTrigger() {
        System.out.println("The trigger was pulled, but there is no bullet.");
    }

    @Override
    public void switchSafetyOn() {
        System.out.println("The safety was switched on, but there is no bullet.");
    }

    @Override
    public void switchSafetyOff() {
        System.out.println("The safety was switched off, but there is no bullet.");
    }    
}

All of the possible states are implemented. It’s time to create the Gun class.

This class contains instances from each state. These are stored in the first four fields.

The state field contains the current state. At the beginning, it holds the magazineRemovedState. Every time when the gun’s state is changing, one of the four state gets assigned to this field.

The bulletCounter represents the bullets in the magazine.

The Gun class has all the methods those found in the State interface, but the gun is not a state, therefore it does not implement that. In a more complex example, these method names and their numbers not necessarily meet. These methods delegate the operations to the current state object, so the gun works according to its internal state.

After those, we can found get methods, which are called from the state objects.

The last methods deals with the bullets. The insertMagazineWithBullets loads the bullets into the gun and sets the state into SafetyOnState.


package com.programcodex.designpatterns.state;

public class Gun {

    private final State magazineRemovedState = new MagazineRemovedState(this);
    private final State magazineEmptyState = new MagazineEmptyState(this);
    private final State safetyOnState = new SafetyOnState(this);
    private final State safetyOffState = new SafetyOffState(this);

    private State state = magazineRemovedState;

    private int bulletCounter;

    public void ejectMagazine() {
        state.ejectMagazine();
        this.bulletCounter = 0;
    }

    public void pullTrigger() {
        state.pullTrigger();
    }

    public void switchSafetyOn() {
        state.switchSafetyOn();
    }

    public void switchSafetyOff() {
        state.switchSafetyOff();
    }    

    public void setState(State state) {
        this.state = state;
    }

    public State getMagazineRemovedState() {
        return magazineRemovedState;
    }

    public State getMagazineEmptyState() {
        return magazineEmptyState;
    }

    public State getSafetyOnState() {
        return safetyOnState;
    }

    public State getSafetyOffState() {
        return safetyOffState;
    }

    public void insertMagazineWithBullets(int bullets) {
        System.out.format("Magazine is inserted with %d bullets.%n",
                bullets);
        this.bulletCounter = bullets;
        setState(getSafetyOnState());
    }

    public int getBulletCounter() {
        return bulletCounter;
    }

    public void decrementBulletCounter() {
        if (bulletCounter > 0) {
            bulletCounter--;
        }
    }
}

And we are done. It’s time to run these things.

First of all, an instance is created from the Gun class that can have different states. It is used by the useGun method in each state. Using the gun means pulling its trigger while the safety is on, then switching the safety and pulling the trigger three times, finally switching the safety back.

At first, the gun is used without any magazine. After that, a magazine with two bullets is inserted and used again. In the third try, the magazine is ejected. In the last case, a magazine with ten bullets is inserted.

The gun behaves differently depending on its internal state.


package com.programcodex.designpatterns.state;

public class TestState {

    public static void main(String[] args) {
        Gun gun = new Gun();

        useGun(gun);
        gun.insertMagazineWithBullets(2);
        useGun(gun);        
        gun.ejectMagazine();
        useGun(gun);        
        gun.insertMagazineWithBullets(10);
        useGun(gun);
    }

    private static void useGun(Gun gun) {
        System.out.println();
        gun.pullTrigger();
        gun.switchSafetyOff();
        gun.pullTrigger();
        gun.pullTrigger();
        gun.pullTrigger();
        gun.switchSafetyOn();
        System.out.println();
    }
}

The output of the above test class:

The State Design Pattern