1, Definition
The status mode is defined as follows:
Allow an object to alter its behavior when its internal state changes. the object will appear to change its class. (when an object's internal state changes, it is allowed to change its behavior, and the object looks like it has changed its class)
The general class diagram of the state pattern is shown above. As can be seen from the figure, there are three roles in the status mode:
- State - abstract state role: an interface or abstract class, which is responsible for defining the object state and encapsulating the environment role to achieve state switching.
- ConcreteSate - specific status role: each specific status has two responsibilities: behavior management of this status and processing of trend status. Generally speaking, it refers to what to do in this state and how to transition from this state to other states.
- Context - environment role: defines the interface required by the client and is responsible for switching the specific state.
The core of state pattern: encapsulation. The change of state causes the change of behavior. From the outside, it looks as if the class corresponding to this object has changed.
2, General code
1. Abstract state role
public abstract class State{ //Define an environment role to improve subclass access protected Context context; //Setting environment roles public void setContext(Context _context) { this.context = _context; } //Behavior 1 public abstract void handle1(); //Behavior 2 public abstract void handle2(); }
2. Specific status role
public class ConcreteState1 extends State { @Override public void handle1() { //Logic that must be processed in this state } @Override public void handle2() { //Set the current status to state2 super.context.setCurrentState(Context.STATE2); //Transition to state2 state, implemented by Context super.context.handle2(); } } public class ConcreteState2 extends State { @Override public void handle1() { //Set the current status to state1 super.context.setCurrentState(Context.STATE1); //Transition to state1 state, implemented by Context super.context.handle1(); } @Override public void handle2() { //Logic that must be processed in this state } }
Each specific state has two responsibilities: behavior management of this state and trend state processing.
3. Environmental role
public class Context { //Define status public final static State STATE1 = new ConcreteState1(); public final static State STATE2 = new ConcreteState2(); //current state private State currentState; public State getCurrentState() { return this.currentState; } public void setCurrentState(State currentState) { this.currentState= currentState; //Switching state this.currentState.setContext(this); } //Behavior entrustment public void handle1() { this.currentState.handle1(); } @Override public void handle2() { this.currentState.handle2(); } }
Environmental roles have two unwritten constraints:
- State objects are declared as static constants, and several state objects have several static constants.
- The environment role has all behaviors defined by the abstract role, and the specific implementation uses the delegation method.
4. Customers
public class Client { public static void main(String[] args) { //Define environmental roles Context context = new Context(); //Initialization status context.setCurrentState(new ConcreteState1()); //Behavior execution context.handle1(); context.handle2(); } }
3, Practical examples
Because this mode is very important, but it is a little difficult to understand, so it is interesting to use a specific example to show it and run the code yourself. This example is the state change during elevator operation.
1. Abstract state role
public abstract class LiftState { //Define an environment role, that is, the function switching caused by the change of packaging state protected Context context; public void setContext(Context _context) { this.context = _context; } //First, the elevator door opens public abstract void open(); //Elevator door closed public abstract void close(); //Elevator operation public abstract void run(); //Elevator stop public abstract void stop(); }
2. Specific status role
public class OpeningState extends LiftState{ @Override public void open() { System.out.println("Elevator door open..."); } @Override public void close() { //Status modification super.context.setLiftState(Context.closingState); //The action is delegated to CloseSate for execution super.context.getLiftState().close(); } @Override public void run() { //The door won't work } @Override public void stop() { //It was a stop } } public class ClosingState extends LiftState{ @Override public void open() { super.context.setLiftState(Context.openingState); super.context.getLiftState().open(); } @Override public void close() { System.out.println("Elevator door closed..."); } @Override public void run() { super.context.setLiftState(Context.runningState); super.context.getLiftState().run(); } @Override public void stop() { //Not by floor super.context.setLiftState(Context.stoppingState); super.context.getLiftState().stop(); } } public class RunningState extends LiftState{ @Override public void open() { //In the running state, it cannot be turned on! } @Override public void close() { //It was closed } @Override public void run() { System.out.println("Elevator operation..."); } @Override public void stop() { super.context.setLiftState(Context.stoppingState); super.context.getLiftState().stop(); } } public class StoppingState extends LiftState{ @Override public void open() { super.context.setLiftState(Context.openingState); super.context.getLiftState().open(); } @Override public void close() { //It was closed } @Override public void run() { super.context.setLiftState(Context.runningState); super.context.getLiftState().run(); } @Override public void stop() { System.out.println("Elevator stop..."); } }
3. Environmental role
public class Context { //Define all elevator states public final static OpeningState openingState = new OpeningState(); public final static ClosingState closingState = new ClosingState(); public final static RunningState runningState = new RunningState(); public final static StoppingState stoppingState = new StoppingState(); //Define a current elevator state private LiftState liftState; public LiftState getLiftState() { return this.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(); } }
4. Customers
public class Client { public static void main(String[] args) { Context context = new Context(); context.setLiftState(new ClosingState()); context.open(); context.close(); context.run(); context.stop(); } }
The results are as follows:
Elevator door open... Elevator door closed... Elevator operation... Elevator stop...
4, Application of state mode
1. Advantages and disadvantages of state mode
Advantages: clear structure, avoiding too many if statements; Follow design principles, including opening and closing principle and single responsibility principle; Good encapsulation,
Disadvantages: it is a kind of expansion problem, so the state is not easy to be too large, preferably no more than 5.
2. Usage scenario of status mode
- Scenarios in which the behavior changes with the change of state: for example, the status of permission designers is different, and the results of executing the same behavior are different.
- Substitutes for conditional and branch judgment statements
3. Best practices
When you need to reassemble several existing states in a certain order, you can use the construction mode + state mode.