Design mode -- State mode

1. Definitions

State mode is also a behavioral mode that allows an object to change its behavior when its internal state changes. Object appears to have modified its class.

With the development of the city, buildings are now high-rise buildings. For buildings, the most basic configuration must be elevators; The elevator can open the elevator door, close the elevator door, run up and down, and stop. The corresponding code is as follows.

public interface ILift {

    /**
     * Elevator door open
     */
    void open();

    /**
     * Elevator door closed
     */
    void close();

    /**
     * Elevator start
     */
    void run();

    /**
     * Elevator stop
     */
    void stop();
}
public class LiftImpl implements ILift{

    @Override
    public void open() {
        System.out.println("Elevator door open...");
    }

    @Override
    public void close() {
        System.out.println("Elevator door closed...");
    }

    @Override
    public void run() {
        System.out.println("Elevator start...");
    }

    @Override
    public void stop() {
        System.out.println("Elevator stop...");
    }
}
public class Test {
    public static void main(String[] args) {
        ILift lift = new LiftImpl();
        //Open the elevator door and people get on the elevator
        lift.open();
        //Elevator door closed
        lift.close();
        //Elevator running
        lift.run();
        //The elevator stops when it reaches the designated floor
        lift.stop();
    }
}

The running results are as follows. Is it very simple? In fact, the template method mode can be used for open, close, run, stop and other methods.

Elevator door open...
The elevator door is closed...
The elevator runs up and down...
The elevator stopped

Improved version of elevator operation

The above code is very simple, but there is a problem. Not all States of the elevator can do other things, such as not opening the door when the elevator is running. This is not taken into account, so the code is slightly modified as shown below.

public interface ILift {

    //Four states of elevator
    //Door open state
    public final static int OPENING_STATE = 1;
    //Door closed state
    public final static int CLOSING_STATE = 2;
    //running state 
    public final static int RUNNING_STATE = 3;
    //Stop status
    public final static int STOPPING_STATE = 4;

    /**
     * Set elevator status
     * @param state
     */
    void setState(int state);

    /**
     * Elevator door open
     */
    void open();

    /**
     * Elevator door closed
     */
    void close();

    /**
     * Elevator start
     */
    void run();

    /**
     * Elevator stop
     */
    void stop();
}

In the implementation of the following elevator, it specifies what can be done in what state. For example, the corresponding open method is to open the elevator door. Under what state can the elevator door be opened? Off and stop status. For the close method, under what circumstances can the elevator door be closed? Only open the door.

public class LiftImpl implements ILift{

    private int state;

    @Override
    public void setState(int state) {
        this.state = state;
    }

    @Override
    public void open() {
        //Under what condition can the elevator door be opened
        switch (this.state){
            //If it is on, it does not need to be processed
            case OPENING_STATE :
                break;
            //If it is closed, the door can be opened
            case CLOSING_STATE :
                openWithoutLogic();
                setState(CLOSING_STATE);
                break;
            //If it is in the running state, of course, it is not allowed to open the door in this case
            case RUNNING_STATE :
                break;
            //If it is in the stop state, the door can of course be opened at this time
            case STOPPING_STATE :
                openWithoutLogic();
                setState(OPENING_STATE);
                break;
        }
    }

    @Override
    public void close() {
        //Under what condition can the elevator door be closed
        switch (this.state){
            //If it is open, the elevator door can be closed
            case OPENING_STATE :
                setState(CLOSING_STATE);
                closeWithoutLogic();
                break;
            //If it is closed, it does not need to be closed again
            case CLOSING_STATE :
                break;
            //If it is in the running state, the door is closed during operation
            case RUNNING_STATE :
                break;
            //If it is stopped, the door is closed
            case STOPPING_STATE :
                break;
        }
    }

    @Override
    public void run() {
        //Under what condition can the elevator run
        switch (this.state){
            //If it is on, it is not allowed to run
            case OPENING_STATE :
                break;
            //If it is off, it can run
            case CLOSING_STATE :
                setState(RUNNING_STATE);
                runWithoutLogic();
                break;
            //If it is running, it is running and does not need to change the state
            case RUNNING_STATE :
                break;
            //If it is stopped, it can run when others use the elevator
            case STOPPING_STATE :
                setState(RUNNING_STATE);
                runWithoutLogic();
                break;
        }
    }

    @Override
    public void stop() {
        //Under what condition can the elevator stop
        switch (this.state){
            //If it is on, it cannot be stopped
            case OPENING_STATE :
                break;
            //If it is closed, the elevator can be stopped
            case CLOSING_STATE :
                setState(STOPPING_STATE);
                stopWithoutLogic();
                break;
            //If it is running and someone gets off the elevator, it can certainly stop
            case RUNNING_STATE :
                setState(STOPPING_STATE);
                stopWithoutLogic();
                break;
            //If it is a stop state, it is a stop, and there is no need to change the state
            case STOPPING_STATE :
                break;
        }
    }

    //Simply open the elevator door without considering other conditions
    public void openWithoutLogic(){
        System.out.println("The elevator door opens...");
    }

    //Only the elevator door is closed without considering other conditions
    public void closeWithoutLogic(){
        System.out.println("The elevator door is closed...");
    }

    //Pure operation, regardless of other conditions
    private void runWithoutLogic(){
        System.out.println("The elevator runs up and down...");
    }

    //Simply stop, regardless of other conditions
    private void stopWithoutLogic(){
        System.out.println("The elevator stopped...");
    }
}

Now the function of the elevator is relatively perfect, and it also supports that the state cannot be changed at will. The operation results are as follows, but there are still problems. If a maintenance state is to be added at this time, case judgment should be added in all methods, and switch should be added in the corresponding maintenance state for judgment, which is completely contrary to the opening and closing principle. At the same time, with the increase of States, a large number of switches and cases make the code abnormally complex.

public class Test {
    public static void main(String[] args) {
        ILift lift = new LiftImpl();
        lift.setState(ILift.STOPPING_STATE);
        //First, the elevator door opens and people go in
        lift.open();
        //Then the elevator door closes
        lift.close();
        //Then, the elevator runs up or down
        lift.run();
        //Finally, when we reached our destination, the elevator survived
        lift.stop();
    }
}
The elevator door opens...
The elevator door is closed...
The elevator runs up and down...
The elevator stopped...

2. Status mode implementation

Elevator operation ultimate

Now that we know the problem with the elevator running code, we try to change it using the state mode. The state pattern abstracts all States into corresponding classes, and then inherits them from LiftState, which ensures that all States are consistent. At the same time, use Context to reference different states, and then flow the states according to different situations. This implements what was said at the beginning of the article, allowing an object to change its behavior when its internal state changes. Object appears to have modified its class.

LiftState is used to constrain different states. All States inherit from LiftState to ensure consistent behavior between states.

public abstract class LiftState {

    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }

    //First, the elevator door opens
    public abstract void open();

    //If the elevator door is opened, of course it will be closed
    public abstract void close();

    //The elevator should be able to go up and down and run
    public abstract void run();

    //The elevator has to stop. If it doesn't stop, it's bullshit
    public abstract void stop();
}

Context is used to save different states. Each elevator state is an independent class. At the same time, the elevator can be triggered by other states through context.

public class Context {
    //Define all elevator states
    public final static OpenningState openningState = new OpenningState();
    public final static ClosingState closeingState = new ClosingState();
    public final static RunningState runningState = new RunningState();
    public final static StoppingState stoppingState = new StoppingState();

    //Set a current elevator status
    private LiftState liftState;

    public LiftState getLiftState() {
        return liftState;
    }

    public void setLiftState(LiftState liftState) {
        this.liftState = liftState;
        //Notify each implementation class of the current environment
        this.liftState.setContext(this);
    }

    public void open(){
        this.liftState.open();
    }

    public void close(){
        this.liftState.close();
    }

    public void run(){
        this.liftState.run();
    }

    public void stop(){
        this.liftState.stop();
    }
}

In the OpnningState state, the elevator door is open, so for the open method, only the elevator door is opened. In the open state, only the elevator door can be closed, while the stop and open states exist in the OpnningState state. Therefore, only the closed state can flow, and the flow is carried out through Context.

public class OpenningState extends LiftState{
    @Override
    public void open() {
        System.out.println("Elevator door open...");
    }

    @Override
    public void close() {
        //Status modification
        super.context.setLiftState(Context.closeingState);
        //The action is delegated to CloseState for execution
        super.context.getLiftState().close();
    }

    @Override
    public void run() {
        //The door cannot be opened during operation
    }

    @Override
    public void stop() {
        //The door must have stopped
    }
}

In the closing state, the elevator door is closed, so the close method only closes the elevator door. In the closed state, you can open the door, run and stop.

public class ClosingState extends LiftState{
    @Override
    public void open() {
        //Status modification
        super.context.setLiftState(Context.openningState);
        //The action is delegated to openningState for execution
        super.context.getLiftState().open();
    }

    @Override
    public void close() {
        System.out.println("The elevator door is closed...");
    }

    @Override
    public void run() {
        //It can operate when the door is closed
        //Status modification
        super.context.setLiftState(Context.runningState);
        //The action delegate is runningState to execute
        super.context.getLiftState().run();
    }

    @Override
    public void stop() {
        //Status modification. It can be stopped when the door is closed
        super.context.setLiftState(Context.stoppingState);
        //The action delegate is runningState to execute
        super.context.getLiftState().stop();
        //The stop state door is closed
    }
}

In the running state, the elevator is in the running state. Because the door cannot be opened in the running state, it can only be stopped (others get on or off the elevator).

public class RunningState extends LiftState{
    @Override
    public void open() {
        //The door cannot be opened and nothing is done in the running state
    }

    @Override
    public void close() {
        //The operation status door is closed and does nothing
    }

    @Override
    public void run() {
        System.out.println("The elevator runs up and down...");
    }

    @Override
    public void stop() {
        //It can run under the stop state door, and there are elevators for other floors
        super.context.setLiftState(Context.stoppingState);
        super.context.getLiftState().stop();
    }
}

In the StoppingState state, the elevator is in the stop state. In the stop state, it can open the door and run.

public class StoppingState extends LiftState{
    @Override
    public void open() {
        //The door can be opened in the stopped state
        super.context.setLiftState(Context.openningState);
        super.context.getLiftState().open();
    }

    @Override
    public void close() {
        //The stop state door is closed, so there is no need to do anything
    }

    @Override
    public void run() {
        //The door can be opened in the stopped state
        super.context.setLiftState(Context.runningState);
        super.context.getLiftState().run();
    }

    @Override
    public void stop() {
        System.out.println("The elevator stopped");
    }
}

Now you only need to store specific state classes through Context, and then the elevator state. Of course, these four states can actually write a template method, and then call the corresponding template.

public class Test {
    public static void main(String[] args) {
        Context context = new Context();
        context.setLiftState(Context.stoppingState);
        context.open();
        context.close();
        context.run();
        context.stop();
    }
}
The elevator door opens...
The elevator door is closed...
The elevator runs up and down...
The elevator stopped...

Roles in state mode

State, the role of state, defines different states, such as the abstraction of all States of an elevator. In this paper, LiftState plays this role

ConcreteState (concrete role), ConcreteState role, is the concrete implementation of the State role. In this paper, OpenningState, ClosingState, RunningState and StoppingState play this role.

Context (status and status context), the role of context, is the place to flow status and store status classes. In this paper, context plays this role.

Reference: Zen of design pattern

Code acquisition address: https://gitee.com/bughong/design-pattern

Posted by nicelad_uk on Wed, 17 Nov 2021 23:12:44 -0800