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