This is the 10th day of my participation in Gwen Challenge

State pattern we can also often come into contact with, in the actual development state pattern will be an object encapsulates different behavior in different conditions in each state class, by setting the different state of the object can be let environmental objects have different behaviors, and the details of the state transition is transparent to the client, convenient for the client to use. In practical development, state mode has a high frequency of use, in workflow logic, in order state flow logic has a wide range of use.

Role division

The state pattern structure diagram contains the following roles:

  1. Context: An environment class, also known as a Context class, is an object that has multiple states. Because the states of environment classes are diverse and the behavior of objects in different states is different, the states are separated to form separate state classes.

  2. State (Abstract State class) : It is used to define an interface to encapsulate related to the environment of a particular state behavior, in abstract classes declared in the different state corresponding method, and in its subclass implementation class these methods, the behavior of the object may be different due to different conditions, thus the realization of the method in different subclasses may be different, the same method can be written in the abstract state class.

  3. ConcreteState: this is a subclass of abstract state classes. Each subclass implements a behavior related to a state of the environment class. Each ConcreteState class corresponds to a ConcreteState of the environment.

Usage scenarios

We can simply divide the scenarios using state patterns into two cases:

  1. The behavior of an object depends on its state, and it must change its behavior based on state at run time.
  2. The code contains a large number of conditional statements related to the state of the object, for example, an operation with a large branch statement (if-else/switch-case) that depends on the state of the object.

The advantages and disadvantages

  • Clear structure, avoiding too many switches… The case or the if… Use of else statements.
  • Good embodiment of the open closed principle and the single responsibility principle, want to increase the state to add a subclass, want to modify the state to modify the subclass.
  • Encapsulation is very good. State changes are implemented inside the class, and external calls do not need to know how to implement state and behavior changes inside the class.
  • A common problem with design patterns is that classes multiply and maintenance costs increase.

Code sample

We use the program to realize a simple driving and parking process class, driving and parking have the following actions: open the door, close the door, driving the car, stop the car. Let’s start with a picture:

Let’s write it in the simplest code:

  1. interface
public interface ICar {
    void open(a);
    void close(a);
    void run(a);
    void stop(a);
}

Copy the code
  1. implementation
public class Car1 implements ICar {
    @Override
    public void open(a) {
        System.out.println("The door...");
    }

    @Override
    public void close(a) {
        System.out.println("Close the door...");
    }

    @Override
    public void run(a) {
        System.out.println("Car form...");
    }

    @Override
    public void stop(a) {
        System.out.println("Stop..."); }}Copy the code
  1. call
public class Client {
    public static void main(String[] args) {
        ICar lift = newCar1(); lift.open(); lift.close(); lift.run(); lift.stop(); }}Copy the code

The above code is very simple and requires little explanation. Based on the steps above, we added a precondition, that is, under what conditions can you open the door, under what conditions can you drive, and so on.As shown in the figure above, if it is in the open state, the next state can only be closed, not drive, that is, open state can not drive. In other cases, you can follow the figure so you can understand. If you follow this logic and write the simple form code above, there will be a lot of if-else judgments. Let me write it briefly:

public class Car2 implements ICar{
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    / / close
    public void close(a) {
        // What other states can be performed when the car is closed
        switch(this.state) {
            case "The door":
                System.out.println("Open the door.");
                break;
            case "Closed":
                System.out.println("Do not close the door.");
                break;
            case "Driving":
                System.out.println("Ready to drive");
                break;
            case "Stop":
                System.out.println("You can park.");
                break; }}// The car opens
    public void open(a) {
        // In what state can the elevator be opened
        switch(this.state){
        }
    }
    // Car driving
    public void run(a) {
        switch(this.state){
        }
    }
    // The bus stops
    public void stop(a) {
        switch(this.state){
        }
    }

}
Copy the code

As the project progresses, this problem will become bigger and bigger. It is difficult to predict all possible states and transitions during the design phase. As a result, this class can become bloated over time.

Next we modify it with the state pattern: we implement the code according to the roles we described above.

  1. Define an abstract state class
public abstract class ACar {
    protected Context context;
    public void setContext(Context _context){
        this.context = _context;
    }
    public abstract void open(a);
    public abstract void close(a);
    public abstract void run(a);
    public abstract void stop(a);

}
Copy the code
  1. There are four state implementations
public class OpenningState extends ACar {
    @Override
    public void open(a) {
        System.out.println("Open the door.");
    }

    @Override
    public void close(a) {
        System.out.println("Can close the door.");

        // Set the environment as closed after closing. You can either drive or park
        super.context.setState(Context.closeingState);

        super.context.getState().close();// Close the door

    }

    @Override
    public void run(a) {
        System.out.println("Can't drive");
    }

    @Override
    public void stop(a) {
        System.out.println("No parking."); }}Copy the code
public class RunningState extends ACar {
    @Override
    public void open(a) {
        System.out.println("Don't open the door.");
    }

    @Override
    public void close(a) {
        System.out.println("Can't close the door.");
    }

    @Override
    public void run(a) {
        System.out.println("Already driving.");
    }

    @Override
    public void stop(a) {
        System.out.println("You can park."); }}Copy the code
public class ClosingState extends ACar {
    @Override
    public void open(a) {
        System.out.println("Open the door.");
    }

    @Override
    public void close(a) {
        System.out.println("Closed.");
    }

    @Override
    public void run(a) {
        System.out.println("Ready to drive.");
    }

    @Override
    public void stop(a) {
        System.out.println("Parking is available."); }}Copy the code
public class StopState extends ACar {
    @Override
    public void open(a) {
        System.out.println("Can open the door.");
    }

    @Override
    public void close(a) {
        System.out.println("Can't close the door.");
    }

    @Override
    public void run(a) {
        System.out.println("Ready to drive");
    }

    @Override
    public void stop(a) {
        System.out.println("Stopped"); }}Copy the code
  1. Define an environment class
public class Context {
    public final static OpenningState openningState = new OpenningState();
    public final static ClosingState closeingState = new ClosingState();
    public final static RunningState runningState = new RunningState();
    public final static StopState stoppingState = new StopState();
    // Define a current vehicle state
    private ACar state;
    public ACar getState(a) {
        return state;
    }
    public void setState(ACar state) {
        this.state = state;
        // Notify each implementation class of the current environment
        this.state.setContext(this);
    }
    public void open(a){
        this.state.open();
    }
    public void close(a){
        this.state.close();
    }
    public void run(a){
        this.state.run();
    }
    public void stop(a){
        this.state.stop(); }}Copy the code
  1. Client testing
public class Client {
    public static void main(String[] args) {

        Context context = new Context();
        context.setState(newOpenningState()); context.open(); context.close(); context.run(); context.stop(); }}Copy the code
  1. The output

It’s open and it’s closed and it’s closed and it’s driving and it’s parking

If the state mode is not used, the common method is to use the switch statement in each method to determine the current state for processing, while using the state mode, through each subclass to achieve, to avoid the switch statement judgment, making the code looks not so jumbled. So what if I wanted to add a state like fuel state? In fact, it is very simple, just add a subclass, and add to the existing class rather than modify, in accordance with the open closed principle; In this case, our states are separate classes, and the class will change only if the factors related to the state change, complying with Demeter’s law.