本文最后更新于 1274 天前,其中的信息可能已经有所发展或是发生改变。
State Pattern
有限状态机
简称状态机, 由三部分组成:状态、事件、动作。事件触发状态转移,执行动作(非必须)。
状态机实现方式一:分支逻辑法
就是各种if else
,switch case
。判断不同的状态,遇到不同的事件,执行不同的操作,例如改变状态,执行动作。适用于简单的场景,毕竟不要过度设计。
状态机实现方式二:查表法
状态机有两个自变量(状态、事件)和一个因变量(动作)组成,如果动作简单,就可以将动作放入二维数组,通过状态和事件快速找出动作。另外可放入配置文件,不用硬编码,更加灵活。
状态转移的二维数组
马里奥的状态
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}
};
状态机实现方式三:状态模式
如果动作复杂,查表就不太适用了。这时可以将事件抽取为接口,不同的状态实现各自需要的事件接口,执行相应的动作。
State:状态枚举类
public enum State {
SMALL(0),
SUPPER(1),
FIRE(2);
private int value;
}
事件
MeetMonster:遇到怪物
public interface MeetMonster extends Event{
void meetMonster(MarioStateMachine marioStateMachine);
}
ObtainCape:获得蘑菇
public interface ObtainCape extends Event {
void obtainCape(MarioStateMachine marioStateMachine);
}
ObtainFireFlower:获得火焰花
public interface ObtainFireFlower extends Event{
void obtainFireFlower(MarioStateMachine marioStateMachine);
}
状态
Mario:马里奥状态抽象类
为了让状态类只实现各自所需的接口,并且状态机可以不区分状态类,执行所有动作。需要在事件接口和状态实现类之间添加一层抽象类,实现所有事件,状态子类只需重写自己需要实现的事件方法。
public abstract class Mario implements ObtainCape, ObtainFireFlower, MeetMonster {
public abstract State getState();
public void obtainCapeWrapper(MarioStateMachine marioStateMachine) {
System.out.print(" 获得蘑菇 ");
obtainCape(marioStateMachine);
}
@Override
public void obtainCape(MarioStateMachine marioStateMachine) {
throw new RuntimeException("unimplemented exception");
}
public void obtainFireFlowerWrapper(MarioStateMachine marioStateMachine) {
System.out.print(" 获得火焰花 ");
obtainFireFlower(marioStateMachine);
}
@Override
public void obtainFireFlower(MarioStateMachine marioStateMachine) {
throw new RuntimeException("unimplemented exception");
}
public void meetMonsterWrapper(MarioStateMachine marioStateMachine) {
System.out.print(" 遇到怪物 ");
meetMonster(marioStateMachine);
}
@Override
public void meetMonster(MarioStateMachine marioStateMachine) {
if (marioStateMachine.getCurrentSate().getState().equals(State.SMALL)) {
System.out.print(" 嗝屁 ");
} else {
System.out.print(" 变小 ");
marioStateMachine.setCurrentSate(SmallMario.getInstance());
}
System.out.print(" 分数减100 \n");
marioStateMachine.setScore(marioStateMachine.getScore()-100);
}
}
FireMario:火焰马里奥状态
public class FireMario extends Mario {
public static final FireMario instance=new FireMario();
public static FireMario getInstance() {
return instance;
}
private FireMario() {
}
@Override
public State getState() {
return State.FIRE;
}
@Override
public void obtainCape(MarioStateMachine marioStateMachine) {
System.out.print(" 变大 ");
marioStateMachine.setCurrentSate(SupperMario.getInstance());
System.out.print(" 分数加300 \n");
marioStateMachine.setScore(marioStateMachine.getScore()+300);
}
@Override
public void obtainFireFlower(MarioStateMachine marioStateMachine) {
System.out.print(" 分数加200 \n");
marioStateMachine.setScore(marioStateMachine.getScore()+200);
}
}
SmallMario:小的马里奥状态
public class SmallMario extends Mario{
public static final SmallMario instance=new SmallMario();
public static SmallMario getInstance() {
return instance;
}
private SmallMario() {
}
@Override
public State getState() {
return State.SMALL;
}
@Override
public void obtainCape(MarioStateMachine marioStateMachine) {
System.out.print(" 变大 ");
marioStateMachine.setCurrentSate(SupperMario.getInstance());
System.out.print(" 分数加100 \n");
marioStateMachine.setScore(marioStateMachine.getScore()+100);
}
@Override
public void obtainFireFlower(MarioStateMachine marioStateMachine) {
System.out.print(" 变火人 ");
marioStateMachine.setCurrentSate(FireMario.getInstance());
System.out.print(" 分数加200 \n");
marioStateMachine.setScore(marioStateMachine.getScore()+200);
}
}
SupperMario:超级马里奥状态
public class SupperMario extends Mario {
public static final SupperMario instance=new SupperMario();
public static SupperMario getInstance() {
return instance;
}
private SupperMario() {
}
@Override
public State getState() {
return State.SUPPER;
}
@Override
public void obtainCape(MarioStateMachine marioStateMachine) {
System.out.print(" 分数加200 \n");
marioStateMachine.setScore(marioStateMachine.getScore()+200);
}
@Override
public void obtainFireFlower(MarioStateMachine marioStateMachine) {
System.out.print(" 变火人 ");
marioStateMachine.setCurrentSate(FireMario.getInstance());
System.out.print(" 分数加300 \n");
marioStateMachine.setScore(marioStateMachine.getScore()+300);
}
}
MarioStateMachine:状态机
public class MarioStateMachine {
private int score;
private Mario currentSate;
public MarioStateMachine() {
System.out.print(" 初始化马里奥,分数为0,状态为small \n");
score = 0;
currentSate = SmallMario.getInstance();
}
public void obtainCape() {
this.currentSate.obtainCapeWrapper(this);
}
public void meetMonster() {
this.currentSate.meetMonsterWrapper(this);
}
public void obtainFireFlower() {
this.currentSate.obtainFireFlowerWrapper(this);
}
}
Application:应用类
public class Application {
public static void main(String[] args) {
MarioStateMachine marioStateMachine = new MarioStateMachine();
marioStateMachine.obtainCape();
marioStateMachine.obtainCape();
marioStateMachine.obtainFireFlower();
marioStateMachine.meetMonster();
marioStateMachine.obtainFireFlower();
marioStateMachine.obtainCape();
marioStateMachine.obtainCape();
marioStateMachine.obtainFireFlower();
System.out.println("最终成绩:"+marioStateMachine.getScore());
}
}
输出结果
初始化马里奥,分数为0,状态为small
获得蘑菇 变大 分数加100
获得蘑菇 分数加200
获得火焰花 变火人 分数加300
遇到怪物 变小 分数减100
获得火焰花 变火人 分数加200
获得蘑菇 变大 分数加300
获得蘑菇 分数加200
获得火焰花 变火人 分数加300
最终成绩:1500
总结
状态模式是有限状态机的一种实现,适合状态不多(类爆炸),动作复杂的场景。
状态多,动作简单适合查表法。
状态不多,动作也简单适合分支逻辑法。