This is the sixth day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

An overview of the

State Pattern :(State Pattern) allows an object to change its behavior when its internal State changes. Object appears to have modified its class

The state pattern mainly deals with situations where the conditional expressions governing the state of an object are too complex. Complex decision logic can be simplified by moving the decision logic of states into a series of classes that represent different states.

When to use:

  • State patterns are generally used to implement state machines, which are commonly used in game, workflow engine and other system development.

  • The code contains a large number of conditional statements related to object state.

What is a state machine?

  • A State machine consists of three parts: states, events, and Actions. An event is also called a Transition Condition. Events trigger transitions of state and execution of actions. However, the action is not required, and it is possible to just transfer the state without performing any action.

UML class diagram:

Role Composition:

  1. Context: This defines the interface required by the client and maintains an instance of a Concrete State role, delegating state-related operations to the current Concrete State object.

  2. Abstract State: Defines an interface to encapsulate a specific state-related behavior that uses the context.

  3. Concrete State: Interface to implement abstract State definitions.

Generic code

Context.java

public class Context {
    private State state;

    public Context(a) {
        this.state = new ConcreteStateA();  // Set the initial state
    }

    public State getState(a) {
        return state;
    }

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

    public void request(a) {
        this.state.handle(this); }}Copy the code

State.java

public abstract class State {

    public abstract void handle(Context context);

}
Copy the code

ConcreteStateA. Java, ConcreteStateB. Java

public class ConcreteStateA extends State {

    @Override
    public void handle(Context context) {
        System.out.println("Handle method for ConcreteStateA state");
        context.setState(this); }}public class ConcreteStateB extends State {

    @Override
    public void handle(Context context) {
        System.out.println("Handle method for ConcreteStateB state");
        context.setState(this); }}Copy the code

Testing:

public class Test {
    public static void main(String[] args) {

        Context context = new Context();
        context.request();
        System.out.println("Current status is:" + context.getState());

        context.setState(new ConcreteStateB());
        context.request();
        System.out.println("Current status is:"+ context.getState()); }}Copy the code

Results:

The handle method in the ConcreteStateA state is currently in the general template state. ConcreteStateA@1b6d3586 ConcreteStateB The handle method in the ConcreteStateB state is currently in the general template state. ConcreteStateB@4554617c

Specific instance

I believe many people have played king of Glory this mobile game, in the game, king of Glory inside the hero suffered different skills or buff will have different status, such as vertigo, acceleration, deceleration and so on. Without state mode, our hero class would be very complex and difficult to maintain. In this case, using state mode is a better choice, the following code simulation implementation.

RunState.java

public interface RunState {

    void run(Hero hero);

}
Copy the code

Commonstate. Java, speedupstate. Java, speedDownState. Java, swimState. Java

public class CommonState implements RunState {
    @Override
    public void run(Hero hero) {
        // Run normally}}public class SpeedUpState implements RunState {
    @Override
    public void run(Hero hero) {
        System.out.println("-------------- Run faster ---------------");
        try {
            Thread.sleep(4000);// Assume the acceleration lasts for 4 seconds
        } catch (InterruptedException e) {}
        hero.setState(Hero.COMMON);
        System.out.println("------ Acceleration stops and becomes normal ------"); }}public class SpeedDownState implements RunState{
    @Override
    public void run(Hero hero) {
        System.out.println("-------------- slow down run ---------------");
        try {
            Thread.sleep(4000);// Assume the deceleration lasts for 4 seconds
        } catch (InterruptedException e) {}
        hero.setState(Hero.COMMON);
        System.out.println("------ Deceleration stops and becomes normal ------"); }}public class SwimState implements RunState{
    @Override
    public void run(Hero hero) {
        System.out.println("-------------- can't run ---------------");
        try {
            Thread.sleep(2000);// Assume the vertigo lasts for 2 seconds
        } catch (InterruptedException e) {}
        hero.setState(Hero.COMMON);
        System.out.println("------ Vertigo is over and back to normal ------"); }}Copy the code

Hero.java

public class Hero {

    public static final RunState COMMON = new CommonState();// Normal status

    public static final RunState SPEED_UP = new SpeedUpState();// Acceleration state

    public static final RunState SPEED_DOWN = new SpeedDownState();// Decelerate state

    public static final RunState SWIM = new SwimState();// Get dizzy

    private RunState state = COMMON;// It is normal by default

    private Thread runThread;// Run the thread

    // Set the state
    public void setState(RunState state) {
        this.state = state;
    }

    // Stop running
    public void stopRun(a) {
        if (isRunning()) {
            runThread.interrupt();
        }
        System.out.println("-------------- stop running ---------------");
    }

    // Start running
    public void startRun(a) {
        if (isRunning()) {
            return;
        }
        final Hero hero = this;
        runThread = new Thread(new Runnable() {
            @Override
            public void run(a) {
                while(! runThread.isInterrupted()) { state.run(hero); }}}); System.out.println("-------------- start running ---------------");
        runThread.start();
    }

    private boolean isRunning(a) {
        returnrunThread ! =null&&! runThread.isInterrupted(); }}Copy the code

Testing:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Hero hero = new Hero();
        hero.startRun();
        hero.setState(Hero.SPEED_UP);
        Thread.sleep(5000);
        hero.setState(Hero.SPEED_DOWN);
        Thread.sleep(5000);
        hero.setState(Hero.SWIM);
        Thread.sleep(5000); hero.stopRun(); }}Copy the code

Results:

— — — — — — — — — — — — — — started running — — — — — — — — — — — — — — — — — — — — — — — — — — — — — accelerating movement — — — — — — — — — — — — — — — — — — — — – end of accelerating state, Become a normal state — — — — — — — — — — — — — — — — — — — — slow movement — — — — — — — — — — — — — — — — — — — — – end of the deceleration state, have become a normal state — — — — — — — — — — — — — — — — — — — — not running — — — — — — — — — — — — — — — — — — — — – stunning end, Return to normal —— ————– Stop running —————


conclusion

State patterns differ from Policy Patterns State patterns are the same as policy patterns in UML, but solve different problems and focus.

  1. The state mode focuses on switching between states to do different things, while the policy mode focuses on choosing policies according to specific situations and does not involve switching.

  2. The state mode does different things in different states, while the strategic mode does the same thing. For example, the aggregation payment platforms, such as Alipay, wechat Pay and UnionPay, have different strategies, but they all do payment in the end, that is to say, they are interchangeable. In contrast to the state pattern, the same method in each state does different things and is not interchangeable.

  3. The state pattern encapsulates the state of an object, while the policy pattern encapsulates an algorithm or policy. Because state is object dependent, it cannot be reused; By isolating policies or algorithms from the Context, we can reuse them.

  4. In the state pattern, each state holds a reference to the Context to implement state transitions. But each policy does not hold references to the Context, they are just used by the Context.