状态模式--黑崎一护的嗑药历程

变身药丸

黑崎一护在和蓝染战斗中,使用了最后的月牙天冲重创蓝染,最终被被浦原喜助使用封印封锁。

一护立下大功,同时也丧失了死神的力量,无法使用斩魂刀变身了。

十二番队长涅茧利为了感谢一户,苦心研发了能够暂时获得死神能力的三种药丸:初解药丸,卍解药丸和虚化药丸。

这三种药丸服用之后能够变身成相应的状态,同时拥有对应的必杀技,战斗力大大增强。

黑崎一护的变身状态一览

正常状态

战斗力指数:100
在这里插入图片描述

始解状态

战斗力指数:200
在这里插入图片描述

卍解状态

战斗力指数:500
在这里插入图片描述

虚化状态

战斗力指数:1000
在这里插入图片描述
十二番队长涅茧利特意嘱咐了下用药规则,正常状态下只有吃始解药丸和卍解药丸才有作用,虚化药丸只有在卍解状态下吃才有作用,还有,同一种药丸重复吃是没有用的,不要浪费哦。

​另外,变身状态下,使用了必杀技之后,药效就会消失,立即恢复到正常状态,要注意哦,副队长涅音梦提醒道。
在这里插入图片描述
贴心的副队长涅音梦还画了幅状态转换图:
在这里插入图片描述

普通的实现方法

状态枚举类

public enum State {

    /**
     * 正常状态
     */
    NORMAL(0),
    /**
     * 始解状态
     */
    INITIAL(1),
    /**
     * 卍解状态
     */
    SWASTIKA(2),
    /**
     * 虚化状态
     */
    BLUR(3);

    private int value;

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

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

变身状态机类

public class TransformStateMachine {

    /**
     * 战斗力
     */
    private int fightingPower;
    /**
     * 当前状态
     */
    private State currentState;


    public int getFightingPower() {
        return this.fightingPower;
    }

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

    /**
     * 初始化
     */
    public TransformStateMachine() {
        this.fightingPower = 100;
        this.currentState = State.NORMAL;
    }

    /**
     * 吃下始解药丸
     */
    public void eateInitialPill() {
        System.out.println("吃下始解药丸");
        if(currentState.equals(State.NORMAL)) {
            this.currentState = State.INITIAL;
            this.fightingPower += 200;
            System.out.println("**变身始解状态**战斗力加200");
        } else {
            System.out.println("木有任何变化。。。");
        }
    }

    /**
     * 吃下卍解药丸
     */
    public void eateSwastikaPill() {
        System.out.println("吃下卍解药丸");
        if(currentState.equals(State.NORMAL) || currentState.equals(State.INITIAL)) {
            this.currentState = State.SWASTIKA;
            this.fightingPower += 500;
            System.out.println("**变身卍解状态**战斗力加500");
        } else {
            System.out.println("木有任何变化。。。");
        }
    }

    /**
     * 吃下虚化药丸
     */
    public void eateBlurPill() {
        System.out.println("吃下虚化药丸");
        if(currentState.equals(State.SWASTIKA)) {
            this.currentState = State.BLUR;
            this.fightingPower += 1000;
            System.out.println("**变身虚化状态**战斗力加1000");
        } else {
            System.out.println("木有任何变化。。。");
        }
    }

    /**
     * 使用必杀技
     */
    public void useUniqueSkill() {
        System.out.println("使用必杀技");
        if(currentState.equals(State.INITIAL) || currentState.equals(State.SWASTIKA) || currentState.equals(State.BLUR)) {
            System.out.println("月牙天冲--->");
            this.currentState = State.NORMAL;
            this.fightingPower = 100;
            System.out.println("**变回初始状态**战斗力变成100");
        } else {
            System.out.println("木有任何变化。。。");
        }
    }
}

测试类

public class Test {

    public static void main(String[] args) {
        TransformStateMachine transformStateMachine = new TransformStateMachine();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateInitialPill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateSwastikaPill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateBlurPill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.useUniqueSkill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.useUniqueSkill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
    }
}

运行结果

当前状态: NORMAL; 战斗力指数: 100

吃下始解药丸
**变身始解状态**战斗力加200
当前状态: INITIAL; 战斗力指数: 300

吃下卍解药丸
**变身卍解状态**战斗力加500
当前状态: SWASTIKA; 战斗力指数: 800

吃下虚化药丸
**变身虚化状态**战斗力加1000
当前状态: BLUR; 战斗力指数: 1800

使用必杀技
月牙天冲--->
**变回初始状态**战斗力变成100
当前状态: NORMAL; 战斗力指数: 100

使用必杀技
木有任何变化。。。
当前状态: NORMAL; 战斗力指数: 100

如果是简单的状态转换来说,这种方式是最简单直接的首选方法。

如果状态很多,这样编写的代码会包含大量的 if-else 或 switch-case 分支判断逻辑,可读性和可维护性都很差。

而且不符合开闭原则,如果修改了状态机中的某个状态转移,我们要在冗长的分支逻辑中找到对应的代码进行修改,很容易改错,引入 bug。

使用状态模式

我们要重构一下上面的代码,试着把每个状态的行为封装到一个类里,“封装了变化”,这样能让逻辑和结构更加清晰。

而且,我们针对某个状态的改变就不会影响其他的状态了,让修改和维护更容易。

这时候就要请状态模式出马了。

状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

在这里插入图片描述
本例的类图如下:
在这里插入图片描述
状态枚举类

public enum State {

    /**
     * 正常状态
     */
    NORMAL(0),
    /**
     * 始解状态
     */
    INITIAL(1),
    /**
     * 卍解状态
     */
    SWASTIKA(2),
    /**
     * 虚化状态
     */
    BLUR(3);

    private int value;

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

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

状态类接口

public interface ITransformState {

    /**
     * 获取当前状态
     * @return
     */
    State getState();

    /**
     * 吃下始解药丸
     */
    void eateInitialPill();

    /**
     * 吃下卍解药丸
     */
    void eateSwastikaPill();

    /**
     * 吃下虚化药丸
     */
    void eateBlurPill();

    /**
     * 使用必杀技
     */
    void useUniqueSkill();
}

初始状态实现类

public class NormalTransform implements ITransformState{

    private TransformStateMachine transformStateMachine;

    public NormalTransform(TransformStateMachine transformStateMachine) {
        this.transformStateMachine = transformStateMachine;
    }

    /**
     * 获取当前状态
     * @return
     */
    public State getState() {
        return State.NORMAL;
    }

    /**
     * 吃下始解药丸
     */
    public void eateInitialPill() {
        System.out.println("吃下始解药丸");
        transformStateMachine.setCurrentState(new InitialTransform(transformStateMachine));
        transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 200);
        System.out.println("**变身始解状态**战斗力加200");
    }

    /**
     * 吃下卍解药丸
     */
    public void eateSwastikaPill() {
        System.out.println("吃下卍解药丸");
        transformStateMachine.setCurrentState(new SwastikaTransform(transformStateMachine));
        transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 500);
        System.out.println("**变身卍解状态**战斗力加500");
    }

    /**
     * 吃下虚化药丸
     */
    public void eateBlurPill() {
        System.out.println("吃下虚化药丸");
        System.out.println("木有任何变化。。。");
    }

    /**
     * 使用必杀技
     */
    public void useUniqueSkill() {
        System.out.println("使用必杀技");
        System.out.println("木有任何变化。。。");
    }
}

始解状态实现类

public class InitialTransform implements ITransformState{

    private TransformStateMachine transformStateMachine;

    public InitialTransform(TransformStateMachine transformStateMachine) {
        this.transformStateMachine = transformStateMachine;
    }

    /**
     * 获取当前状态
     * @return
     */
    public State getState() {
        return State.INITIAL;
    }

    /**
     * 吃下始解药丸
     */
    public void eateInitialPill() {
        System.out.println("吃下始解药丸");
        System.out.println("木有任何变化。。。");
    }

    /**
     * 吃下卍解药丸
     */
    public void eateSwastikaPill() {
        System.out.println("吃下卍解药丸");
        transformStateMachine.setCurrentState(new SwastikaTransform(transformStateMachine));
        transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 500);
        System.out.println("**变身卍解状态**战斗力加500");
    }

    /**
     * 吃下虚化药丸
     */
    public void eateBlurPill() {
        System.out.println("吃下虚化药丸");
        System.out.println("木有任何变化。。。");
    }

    /**
     * 使用必杀技
     */
    public void useUniqueSkill() {
        System.out.println("使用必杀技");
        System.out.println("月牙天冲--->");
        transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
        transformStateMachine.setFightingPower(100);
        System.out.println("**变回初始状态**战斗力变成100");
    }
}

卍解状态实现类

public class SwastikaTransform implements ITransformState {

    private TransformStateMachine transformStateMachine;

    public SwastikaTransform(TransformStateMachine transformStateMachine) {
        this.transformStateMachine = transformStateMachine;
    }

    /**
     * 获取当前状态
     * @return
     */
    public State getState() {
        return State.SWASTIKA;
    }

    /**
     * 吃下始解药丸
     */
    public void eateInitialPill() {
        System.out.println("吃下始解药丸");
        System.out.println("木有任何变化。。。");
    }

    /**
     * 吃下卍解药丸
     */
    public void eateSwastikaPill() {
        System.out.println("吃下始解药丸");
        System.out.println("木有任何变化。。。");
    }

    /**
     * 吃下虚化药丸
     */
    public void eateBlurPill() {
        System.out.println("吃下虚化药丸");
        transformStateMachine.setCurrentState(new BlurTransform(transformStateMachine));
        transformStateMachine.setFightingPower(transformStateMachine.getFightingPower() + 1000);
        System.out.println("**变身虚化状态**战斗力加1000");
    }

    /**
     * 使用必杀技
     */
    public void useUniqueSkill() {
        System.out.println("使用必杀技");
        System.out.println("黑流牙突--->");
        transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
        transformStateMachine.setFightingPower(100);
        System.out.println("**变回初始状态**战斗力变成100");
    }
}

虚化状态实现类

public class BlurTransform implements ITransformState {

    private TransformStateMachine transformStateMachine;

    public BlurTransform(TransformStateMachine transformStateMachine) {
        this.transformStateMachine = transformStateMachine;
    }

    /**
     * 获取当前状态
     * @return
     */
    public State getState() {
        return State.BLUR;
    }

    /**
     * 吃下始解药丸
     */
    public void eateInitialPill() {
        System.out.println("吃下始解药丸");
        System.out.println("木有任何变化。。。");
    }

    /**
     * 吃下卍解药丸
     */
    public void eateSwastikaPill() {
        System.out.println("吃下卍解药丸");
        System.out.println("木有任何变化。。。");
    }

    /**
     * 吃下虚化药丸
     */
    public void eateBlurPill() {
        System.out.println("吃下虚化药丸");
        System.out.println("木有任何变化。。。");
    }

    /**
     * 使用必杀技
     */
    public void useUniqueSkill() {
        System.out.println("使用必杀技");
        System.out.println("王虚的闪光--->");
        transformStateMachine.setCurrentState(new NormalTransform(transformStateMachine));
        transformStateMachine.setFightingPower(100);
        System.out.println("**变回初始状态**战斗力变成100");
    }
}

变身状态机类

public class TransformStateMachine {

    /**
     * 战斗力
     */
    private int fightingPower;
    /**
     * 当前状态
     */
    private ITransformState currentState;

    /**
     * 初始化
     */
    public TransformStateMachine() {
        this.fightingPower = 100;
        this.currentState = new NormalTransform(this);
    }

    /**
     * 吃下始解药丸
     */
    public void eateInitialPill() {
        this.currentState.eateInitialPill();
    }

    /**
     * 吃下卍解药丸
     */
    public void eateSwastikaPill() {
        this.currentState.eateSwastikaPill();
    }

    /**
     * 吃下虚化药丸
     */
    public void eateBlurPill() {
        this.currentState.eateBlurPill();
    }

    /**
     * 使用必杀技
     */
    public void useUniqueSkill() {
        this.currentState.useUniqueSkill();
    }

    public int getFightingPower() {
        return this.fightingPower;
    }

    public void setFightingPower(int fightingPower) {
        this.fightingPower = fightingPower;
    }

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

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

测试类

public class Test {
    public static void main(String[] args) {
        TransformStateMachine transformStateMachine = new TransformStateMachine();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateInitialPill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateBlurPill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateSwastikaPill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateBlurPill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.useUniqueSkill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.eateSwastikaPill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
        transformStateMachine.useUniqueSkill();
        System.out.println("当前状态: " + transformStateMachine.getCurrentState() + "; 战斗力指数: " + transformStateMachine.getFightingPower() + '\n');
    }
}

测试结果

当前状态: NORMAL; 战斗力指数: 100

吃下始解药丸
**变身始解状态**战斗力加200
当前状态: INITIAL; 战斗力指数: 300

吃下虚化药丸
木有任何变化。。。
当前状态: INITIAL; 战斗力指数: 300

吃下卍解药丸
**变身卍解状态**战斗力加500
当前状态: SWASTIKA; 战斗力指数: 800

吃下虚化药丸
**变身虚化状态**战斗力加1000
当前状态: BLUR; 战斗力指数: 1800

使用必杀技
王虚的闪光--->
**变回初始状态**战斗力变成100
当前状态: NORMAL; 战斗力指数: 100

吃下卍解药丸
**变身卍解状态**战斗力加500
当前状态: SWASTIKA; 战斗力指数: 600

使用必杀技
黑流牙突--->
**变回初始状态**战斗力变成100
当前状态: NORMAL; 战斗力指数: 100

总结

状态模式一般用来实现有限状态机。

有限状态机的英文翻译是 Finite State Machine,缩写为 FSM,简称为状态机。

状态机有 3 个组成部分:状态(State)、事件(Event)、动作(Action)。其中,事件也称为转移条件(Transition Condition)。事件触发状态的转移及动作的执行。

不过,动作不是必须的,也可能只转移状态,不执行任何动作。

状态机的状态随着某种事件的发生而改变,事件就是状态转移的条件,状态转移时可能会有相应的操作要同时进行。

状态模式的优点

1.如果状态变更伴随着复杂的操作,状态模式能把所有操作封装到一个类里,允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

2.把每个状态和动作提取出来,封装到一个类中,就把着眼点从执行状态提高到整个对象的状态,这将使代码更加结构化,整体意图更加清晰。

3.以后对状态的修改之需要修改一个子类,新增一个新的状态,只需要新增一个状态类,对原有代码的修改少。

状态模式的缺点

1.会增加系统类和对象的个数。

2.一个State的子类至少拥有一个其他子类的信息,各个子类之间产生了依赖。

代码链接












在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章