3. Behavioral model [state model]

state mode

The state pattern is generally used to implement state machines, and state machines are often used in system development such as games and workflow engines.

Finite State Machine

The state machine has three components: State, Event, and Action. Among them, the event is also called a transition condition
. Events trigger state transitions and action execution. However, the action is not necessary, and it may only transfer the state without performing any action.

The transformation of Mario's form is a state machine.
Among them, Mario's different forms are the "states" in the state machine, the game plot (such as eating mushrooms) is the "event" in the state machine, and the addition and subtraction of points are the " actions" in the state machine. For example, the event of eating a mushroom will trigger a state transfer: from Little Mario to Super Mario, as well as trigger the execution of an action (increase 100 points).
[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-2vhojDRd-1663490116683) (…/src/main/resources/pic/status transfer diagram.png)]

public enum State {
    
    
    SMALL(0),
    SUPER(1),
    FIRE(2),
    CAPE(3);
    private int value;

    private State(int value) {
    
    
        this.value = value;
    }

    public int getValue() {
    
    
        return this.value;
    }
}

public class MarioStateMachine {
    
    
    private int score;
    private State currentState;

    public MarioStateMachine() {
    
    
        this.score = 0;
        this.currentState = State.SMALL;
    }

    public void obtainMushRoom() {
    
    
//TODO
    }

    public void obtainCape() {
    
    
//TODO
    }

    public void obtainFireFlower() {
    
    
//TODO
    }

    public void meetMonster() {
    
    
//TODO
    }

    public int getScore() {
    
    
        return this.score;
    }

    public State getCurrentState() {
    
    
        return this.currentState;
    }
}

public class ApplicationDemo {
    
    
    public static void main(String[] args) {
    
    
        MarioStateMachine mario = new MarioStateMachine();
        mario.obtainMushRoom();
        int score = mario.getScore();
        State state = mario.getCurrentState();
        System.out.println("mario score: " + score + "; state: " + state);
    }
}

State machine implementation method 1: branch logic method

Use if-else or switch-case branch logic, refer to the state transition diagram, and translate each state transition into code as it is. For simple state machines, this implementation method is the simplest and most direct, and is the first choice.

public class MarioStateMachine {
    
    
    private int score;
    private State currentState;

    public MarioStateMachine() {
    
    
        this.score = 0;
        this.currentState = State.SMALL;
    }

    public void obtainMushRoom() {
    
    
        if (currentState.equals(State.SMALL)) {
    
    
            this.currentState = State.SUPER;
            this.score += 100;
        }
    }

    public void obtainCape() {
    
    
        if (currentState.equals(State.SMALL) || currentState.equals(State.SUPER)) {
    
    
            this.currentState = State.CAPE;
            this.score += 200;
        }

    }

    public void obtainFireFlower() {
    
    
        if (currentState.equals(State.SMALL) || currentState.equals(State.SUPER)) {
    
    
            this.currentState = State.FIRE;
            this.score += 300;
        }
    }

    public void meetMonster() {
    
    
        if (currentState.equals(State.SUPER)) {
    
    
            this.currentState = State.SMALL;
            this.score -= 100;
            return;
        }
        if (currentState.equals(State.CAPE)) {
    
    
            this.currentState = State.SMALL;
            this.score -= 200;
            return;
        }
        if (currentState.equals(State.FIRE)) {
    
    
            this.currentState = State.SMALL;
            this.score -= 300;
            return;
        }
    }

    public int getScore() {
    
    
        return this.score;
    }

    public State getCurrentState() {
    
    
        return this.currentState;
    }
}

State machine implementation method 2: look-up table method

For state machines with many states and complex state transitions, the look-up table method is more suitable. Representing the state transition diagram through a two-dimensional array can greatly improve the readability and maintainability of the code. In a two-dimensional table, the first dimension represents the current state, the second dimension represents the event, and the value represents the new state that the current state is transferred to after the event and the action it performs.
[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-wZGZNNlR-1663490116685) (…/src/main/resources/pic/two-dimensional table.png)]

public enum Event {
    
    
    GOT_MUSHROOM(0),
    GOT_CAPE(1),
    GOT_FIRE(2),
    MET_MONSTER(3);
    private int value;

    private Event(int value) {
    
    
        this.value = value;
    }

    public int getValue() {
    
    
        return this.value;
    }
}

public class MarioStateMachine {
    
    
    private int score;
    private State currentState;
    private static final State[][] transitionTable = {
    
    
            {
    
    SUPER, CAPE, FIRE, SMALL},
            {
    
    SUPER, CAPE, FIRE, SMALL},
            {
    
    CAPE, CAPE, CAPE, SMALL},
            {
    
    FIRE, FIRE, FIRE, SMALL}
    };
    private static final int[][] actionTable = {
    
    
            {
    
    +100, +200, +300, +0},
            {
    
    +0, +200, +300, -100},
            {
    
    +0, +0, +0, -200},
            {
    
    +0, +0, +0, -300}
    };

    public MarioStateMachine() {
    
    
        this.score = 0;
        this.currentState = State.SMALL;
    }

    public void obtainMushRoom() {
    
    
        executeEvent(Event.GOT_MUSHROOM);
    }

    public void obtainCape() {
    
    
        executeEvent(Event.GOT_CAPE);
    }

    public void obtainFireFlower() {
    
    
        executeEvent(Event.GOT_FIRE);
    }

    public void meetMonster() {
    
    
        executeEvent(Event.MET_MONSTER);
    }

    private void executeEvent(Event event) {
    
    
        int stateValue = currentState.getValue();
        int eventValue = event.getValue();
        this.currentState = transitionTable[stateValue][eventValue];
        this.score = actionTable[stateValue][eventValue];
    }

    public int getScore() {
    
    
        return this.score;
    }

    public State getCurrentState() {
    
    
        return this.currentState;
    }
}

State machine implementation method three: state mode

The state mode avoids branch judgment logic by splitting the state transfer and action execution triggered by events into different state classes. We prefer this implementation method for state machines that do not have many states and whose state transitions are relatively simple, but the business logic contained in the actions triggered by events may be complex.

IMario is a state interface that defines all events. SmallMario, SuperMario, CapeMario, and FireMario are implementation classes of the IMario interface, corresponding to the 4 states in the state machine respectively. Originally, all the code logic for state transition and action execution was concentrated in the MarioStateMachine class. Now, these code logic are dispersed into these four state classes. There is a two-way dependency relationship between MarioStateMachine and each state class. It is natural that MarioStateMachine depends on each state class, but on the other hand, why do each state class depend on MarioStateMachine? This is because each state class needs to update two variables in MarioStateMachine, score and currentState.

public interface IMario {
    
     //所有状态类的接口
    State getName();

    //以下是定义的事件
    void obtainMushRoom();

    void obtainCape();

    void obtainFireFlower();

    void meetMonster();
}

public class SmallMario implements IMario {
    
    
    private MarioStateMachine stateMachine;

    public SmallMario(MarioStateMachine stateMachine) {
    
    
        this.stateMachine = stateMachine;
    }

    @Override
    public State getName() {
    
    
        return State.SMALL;
    }

    @Override
    public void obtainMushRoom() {
    
    
        stateMachine.setCurrentState(new SuperMario(stateMachine));
        stateMachine.setScore(stateMachine.getScore() + 100);
    }

    @Override
    public void obtainCape() {
    
    
        stateMachine.setCurrentState(new CapeMario(stateMachine));
        stateMachine.setScore(stateMachine.getScore() + 200);
    }

    @Override
    public void obtainFireFlower() {
    
    
        stateMachine.setCurrentState(new FireMario(stateMachine));
        stateMachine.setScore(stateMachine.getScore() + 300);
    }

    @Override
    public void meetMonster() {
    
    
// do nothing...
    }
}

public class SuperMario implements IMario {
    
    
    private MarioStateMachine stateMachine;

    public SuperMario(MarioStateMachine stateMachine) {
    
    
        this.stateMachine = stateMachine;
    }

    @Override
    public State getName() {
    
    
        return State.SUPER;
    }

    @Override
    public void obtainMushRoom() {
    
    
// do nothing...
    }

    @Override
    public void obtainCape() {
    
    
        stateMachine.setCurrentState(new CapeMario(stateMachine));
        stateMachine.setScore(stateMachine.getScore() + 200);
    }

    @Override
    public void obtainFireFlower() {
    
    
        stateMachine.setCurrentState(new FireMario(stateMachine));
        stateMachine.setScore(stateMachine.getScore() + 300);
    }

    @Override
    public void meetMonster() {
    
    
        stateMachine.setCurrentState(new SmallMario(stateMachine));
        stateMachine.setScore(stateMachine.getScore() - 100);
    }
}

// 省略CapeMario、FireMario类...
public class MarioStateMachine {
    
    
    private int score;
    private IMario currentState; // 不再使用枚举来表示状态

    public MarioStateMachine() {
    
    
        this.score = 0;
        this.currentState = new SmallMario(this);
    }

    public void obtainMushRoom() {
    
    
        this.currentState.obtainMushRoom();
    }

    public void obtainCape() {
    
    
        this.currentState.obtainCape();
    }

    public void obtainFireFlower() {
    
    
        this.currentState.obtainFireFlower();
    }

    public void meetMonster() {
    
    
        this.currentState.meetMonster();
    }

    public int getScore() {
    
    
        return this.score;
    }

    public State getCurrentState() {
    
    
        return this.currentState.getName();
    }

    public void setScore(int score) {
    
    
        this.score = score;
    }

    public void setCurrentState(IMario currentState) {
    
    
        this.currentState = currentState;
    }


    public int getScore() {
    
    
        return this.score;
    }

    public State getCurrentState() {
    
    
        return this.currentState.getName();
    }

    public void setScore(int score) {
    
    
        this.score = score;
    }

    public void setCurrentState(IMario currentState) {
    
    
        this.currentState = currentState;
    }
    }

Guess you like

Origin blog.csdn.net/weixin_46488959/article/details/126919426