三、行为型模式【状态模式】

状态模式

状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。

有限状态机

状态机有 3 个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也称为转移条件(Transition
Condition)。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。

马里奥形态的转变就是一个状态机。其中,马里奥的不同形态就是状态机中的“状态”,游戏情节(比如吃了蘑菇)就是状态机中的“事件”,加减积分就是状态机中的“动
作”。比如,吃蘑菇这个事件,会触发状态的转移:从小马里奥转移到超级马里奥,以及触发动作的执行(增加 100 积分)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2vhojDRd-1663490116683)(…/src/main/resources/pic/状态转移图.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);
    }
}

状态机实现方式一:分支逻辑法

利用 if-else 或者 switch-case 分支逻辑,参照状态转移图,将每一个状态转移原模原样地直译成代码。对于简单的状态机来说,这种实现方式最简单、最直接,是首选。

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;
    }
}

状态机实现方式二:查表法

对于状态很多、状态转移比较复杂的状态机来说,查表法比较合适。通过二维数组来表示状态转移图,能极大地提高代码的可读性和可维护性。在二维表中,第一维表示当前状态,第二维表示事件,值表示当前状态经过事件之后,转移到的新状态及其执行的动作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wZGZNNlR-1663490116685)(…/src/main/resources/pic/二维表.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;
    }
}

状态机实现方式三:状态模式

状态模式通过将事件触发的状态转移和动作执行,拆分到不同的状态类中,来避免分支判断逻辑。对于状态并不多、状态转移也比较简单,但事件触发执行的动作包含的业务逻辑可能比较复杂的状态机来说,我们首选这种实现方式 。

IMario 是状态的接口,定义了所有的事件。SmallMario、SuperMario、 CapeMario、FireMario 是 IMario 接口的实现类,分别对应状态机中的 4 个状态。原来所有的状态转移和动作执行的代码逻辑,都集中在 MarioStateMachine 类中,现在,这些代码逻辑被分散到了这 4 个状态类中。 MarioStateMachine 和各个状态类之 间是双向依赖关系。MarioStateMachine 依赖各个状态类是理所当然的,但是,反过来,各个状态类为什么要依赖 MarioStateMachine 呢?这是因为,各个状态类需要更新 MarioStateMachine 中的两个变量,score 和 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;
    }
    }

猜你喜欢

转载自blog.csdn.net/weixin_46488959/article/details/126919426