State pattern

 


State is a behavioral design pattern that lets an object alter its behavior when its internal state changes.

State machines

  1. Gather up states
    • Small Mario
    • Super Mario
    • Fire Mario
    • Cape Mario
  2. Define a state interface that contains a method for every possible action:
    • Got Mushroom 🍄
    • Got Fire Flower 🔥
    • Got Feather 🍃
  3. Implement a state class for every state. These classes will be responsible for the behaviour of the machine when it is in the corresponding state
  4. We don't need conditional code in the context class because the states will manage the behaviour

State interface

public interface State {
    void gotMushroom();
    void gotFireFlower();
    void gotFeather();
};

Concrete states

Example: Small Mario state class

public class SmallMario implements State {
    private Mario mario;

    public SmallMario(Mario mario) {
        this.mario = mario;
    }

    public void gotMushroom() {
        System.out.println("Got Mushroom!");
        mario.setState(new SuperMario(mario));
    }

    public void gotFireFlower() {
        System.out.println("Got FireFlower!");
        mario.setState(new FireMario(mario));
    }

    public void gotFeather() {
        System.out.println("Got Feather!");
        mario.setState(new CapeMario(mario));
    }
}

Context class

public class Mario {
    private State state;

    public Mario() {
        state = new SmallMario(this); //Begin state
    }

    //Delegate work to state
    public void gotMushroom() { state.gotMushroom(); }
    public void gotFireFlower() { state.gotFireFlower(); }
    public void gotFeather() { state.gotFeather(); }

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

    @Override
    public String toString() {
        return "State: " + state;
    }
}

Design principles (link):

  • (OK) Single Responsibility Principle - Organize the code related to particular states into separate classes.
  • (NOK) Introducing new states means we need to modify the existing states and context class to account for this new state, and the methods used to change to it. This violates the Open Closed Principle.
  • (OK) Dependency Inversion Principle - The context class and concrete states depend on the State interface.
  • (NOK) The state design pattern might violate the Liskov Substitution Principle, since most of the time different states have different behaviours in terms of usable methods and methods that throw exceptions.
  • (NOK) In the same way as LSP, the state pattern might also violate the Interface Segregation Principle, because sometimes there might be methods that are forcefully implemented in a state where they don't do anything or throw an exception. For example, in the SuperMario state, the method gotShroom doesn't do anything, it would be empty.

Relations with Other Patterns

  • In the State pattern, a set of behaviours are encapsulated in state objects, and at any time the context delegates to one of these states. The current state changes to reflect the internal state of the context, so its behaviour changes overtime. The client knows very little about the state objects. In Strategy pattern, a family of algorithms are encapsulated in order to make them interchangeable. The client usually specifies the strategy object that the context is composed with.

Comments

Popular Posts