当前位置:   article > 正文

设计模式-状态模式_状态机模式

状态机模式
一、为什么要了解状态机模式?

事出有因,年前在进行代码review的时候 有个语音通话的功能使用了状态机,

当时对状态模式一头的雾水。

只记得协程的内部原理也是用到了状态机。为此状态模式需要深入理解学习一下。

二、什么是状态机模式?

状态机模式官方的介绍:

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。

说人话:

一个对象有多个状态,因某些行为(事件)使其状态发生了转换,但是状态之间的转换没有对外暴露。这就是状态模式要解决的问题。

状态模式的核心:封装
状态的变更引起行为的变更

如果还是不理解就看下面的代码演示。


先说一个经常被其他文章使用到的一个样例:

1、实现一个电梯的基本功能

一个电梯可以进行下面的4动作:停止、运行、开门、关门。

电梯接口

public interface ILift {
     //首先电梯门开启动作
     public void open();
     //电梯门可以开启,那当然也就有关闭了
     public void close();
     //电梯要能上能下
     public void run();
     //电梯还要能停下来
     public void stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

电梯实现类

public class Lift implements ILift {
     //电梯门关闭
     public void close() {
             System.out.println("电梯门关闭...");
     }
     //电梯门开启
     public void open() {
             System.out.println("电梯门开启...");
     }
     //电梯开始运行起来
     public void run() {
             System.out.println("电梯上下运行起来...");
     }
     //电梯停止
     public void stop() {
             System.out.println("电梯停止了...");
     }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

调用

 public static void main(String[] args) {
         ILift lift = new Lift();
         //首先是电梯门开启,人进去
         lift.open();
         //然后电梯门关闭
         lift.close();
         //再然后,电梯运行起来,向上或者向下
         lift.run();
         //最后到达目的地,电梯停下来
         lift.stop();
     }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

太简单的程序了。现实情况往往比较复杂如下

2、完善电梯功能添加条件限制

现实情况电梯门不是随时可以开启的,有相应的前提:

具体来说:特定状态下才能做特定事

● 敞门状态

按了电梯上下按钮,电梯门开,这中间大概有10秒的时间,那就是敞门状态。在这个状态下电梯只能做的动作是关门动作。

● 闭门状态

电梯门关闭了,在这个状态下,可以进行的动作是:开门(我不想坐电梯了)、停止(忘记按路层号了)、运行。

● 运行状态

电梯正在跑,上下窜,在这个状态下,电梯只能做的是停止。

● 停止状态

电梯停止不动,在这个状态下,电梯有两个可选动作:继续运行和开门动作。

因为不同的动作 电梯会进入不同的状态:停止状态、运行状态、开门状态、关门状态。

而且各个状态之间的影响关系是:

开门关门运行停止
敞门状态
闭门状态
运行状态
停止状态

电梯状态和动作对应表(○表示不允许,☆表示允许动作)

因此对电梯添加了4种状态

public interface ILift {
     //电梯的4个状态
     public final static int OPENING_STATE = 1;  //敞门状态
     public final static int CLOSING_STATE = 2;  //闭门状态
     public final static int RUNNING_STATE = 3;  //运行状态
     public final static int STOPPING_STATE = 4; //停止状态
     //设置电梯的状态
     public void setState(int state);
     //首先电梯门开启动作
     public void open();
     //电梯门可以开启,那当然也就有关闭了
     public void close();
     //电梯要能上能下,运行起来
     public void run();
     //电梯还要能停下来
     public void stop();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

电梯具体实现类添加限制

public class Lift implements ILift {
     private int state;
     public void setState(int state) {
             this.state = state;
     }
     //电梯门关闭
     public void close() {
             //电梯在什么状态下才能关闭
             switch(this.state){
                     case OPENING_STATE:  //可以关门,同时修改电梯状态
                          this.closeWithoutLogic();  
                          this.setState(CLOSING_STATE);
                          break;
                     case CLOSING_STATE:  //电梯是关门状态,则什么都不做
                          //do nothing;
                          break;
                     case RUNNING_STATE: //正在运行,门本来就是关闭的,也什么都不做
                          //do nothing;
                          break;
                     case STOPPING_STATE:  //停止状态,门也是关闭的,什么也不做
                          //do nothing;
                          break;
                     }
     }
     //电梯门开启
     public void open() {
             //电梯在什么状态才能开启
             switch(this.state){
                     case OPENING_STATE: //闭门状态,什么都不做
                          //do nothing;
                          break;
                     case CLOSING_STATE: //闭门状态,则可以开启
                          this.openWithoutLogic();
                          this.setState(OPENING_STATE);
                          break;
                     case RUNNING_STATE: //运行状态,则不能开门,什么都不做
                          //do nothing;
                          break;
                     case STOPPING_STATE: //停止状态,当然要开门了
                          this.openWithoutLogic();
                          this.setState(OPENING_STATE);
                          break;
             }
     }
     //电梯开始运行起来
     public void run() {
             switch(this.state){
                     case OPENING_STATE: //敞门状态,什么都不做
                          //do nothing;
                          break;
                     case CLOSING_STATE: //闭门状态,则可以运行
                          this.runWithoutLogic();
                          this.setState(RUNNING_STATE);
                          break;
                     case RUNNING_STATE: //运行状态,则什么都不做
                          //do nothing;
                          break;
                     case STOPPING_STATE: //停止状态,可以运行
                          this.runWithoutLogic();
                          this.setState(RUNNING_STATE);
                }
     }
     //电梯停止
     public void stop() {
                switch(this.state){
                case OPENING_STATE: //敞门状态,要先停下来的,什么都不做
                     //do nothing;
                     break;
                case CLOSING_STATE: //闭门状态,则当然可以停止了
                     this.stopWithoutLogic();
                     this.setState(CLOSING_STATE);
                     break;
                case RUNNING_STATE: //运行状态,有运行当然那也就有停止了
                     this.stopWithoutLogic();
                     this.setState(CLOSING_STATE);
                     break;
                case STOPPING_STATE: //停止状态,什么都不做
                     //do nothing;
                     break;
             }
     }
     //纯粹的电梯关门,不考虑实际的逻辑
     private void closeWithoutLogic(){
             System.out.println("电梯门关闭...");
     }
     //纯粹的电梯开门,不考虑任何条件
     private void openWithoutLogic(){
             System.out.println("电梯门开启...");
     }
     //纯粹的运行,不考虑其他条件
     private void runWithoutLogic(){
             System.out.println("电梯上下运行起来...");
     }
     //单纯的停止,不考虑其他条件
     private void stopWithoutLogic(){
             System.out.println("电梯停止了...");
     }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

程序有点长,但是还是很简单的,就是在每一个接口定义的方法中使用switch…case来判断它是否符合业务逻辑,然后运行指定的动作。

通过代码功能演示发现了不少的问题:

  • 电梯实现类Lift有点长
  • 扩展性非常差劲
  • 非常规状态无法实现

因此我们需要对此代码优化处理使用:状态模式

三、代码演示

状态模式类图:

state
Context
Request()
State
Handle()
ConcreteContext

状态模式中有三种角色:

State – 抽象状态角色
接口或抽象类,负责对象状态定义,并且封装环境以实现状态切换。

ConcreteState – 具体状态角色
每一个具体状态必须完成两个职责:

本状态的行为管理
趋势状态处理
通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。

Context – 环境角色(machine)
定义客户端需要的接口,并且负责具体状态的切换

1、创建 抽象状态角色 – State

public abstract class State {
    // 定义一个环境角色 ,提供子类访问
    protected StateMachine machine;
    // 设置环境角色
    public void setState(StateMachine machine) {
        this.machine = machine;
    }

    // 行为1
    public abstract void action1();

    // 行为2
    public abstract void action2();

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2、创建具体状态角色 - ConcreteState1 、ConcreteState2

public class ConcreteState1 extends State{
    
    @Override
    public void action1() {
        // 转换到本状态下,必须要处理的逻辑
    }

    @Override
    public void action2() {
        // 设置当前状态 为state2
        super.machine.setCurrentState(StateMachine.STATE2);
        // 过渡到state2状态,由StateMachine实现
        super.machine.action2();
    }
}

public class ConcreteState2 extends State{
    @Override
    public void action1() {
        // 设置当前状态为 state1
        super.machine.setCurrentState(StateMachine.STATE1);
        // 过渡到state状态,由Context实现
        super.machine.action1();
    }

    @Override
    public void action2() {
        // 本状态下必须要处理的逻辑
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

3、创建环境角色 context – StateMachine

public class StateMachine {

    public final static State STATE1 = new ConcreteState1();
    public final static State STATE2  = new ConcreteState2();

    // 当前状态
    private State currentState;

    public StateMachine() {

    }

    // 获取当前状态
    public State getCurrentState() {
        return currentState;
    }

    // 设置当前状态
    public void setCurrentState(State currentState) {
        this.currentState = currentState;
        // 切换状态
        this.currentState.setState(this);
    }

    // 行为委托
    public void action1(){
        this.currentState.action1();
    }

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

环境角色有两个不成文的约束:

  • 把状态对象声明为静态常量,有几个状态对象就声明几个静态常量
  • 环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式
public static void main(String[] args) {
     //定义环境角色
     Context context = new Context();
     //初始化状态
     context.setCurrentState(new ConcreteState1());
     //行为执行
     context.handle1();
     context.handle2();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我们已经隐藏了状态的变化过程,它的切换引起了行为的变化。对外来说,我们只看到行为的发生改变,而不用知道是状态变化引起的

四、应用场景

在电梯添加行为条件限制后的代码比较繁琐,因此借助状态模式代码模板对比写下代码:

1、创建 抽象状态角色 – State

public abstract class Status {
    // 定义一个环境角色 ,提供子类访问
    public LiftStatusMachine machine;
    // 设置环境角色
    public void setLiftStateMachine(LiftStatusMachine machine) {
        this.machine = machine;
    }
    // 行为 方法
    public abstract void open();

    public abstract void close();

    public abstract void running();

    public abstract void stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2、创建具体状态角色 - ConcreteState1 、ConcreteState2
这里的ConcreteState分别是:CloseStatus、OpenStatus、RunningStatu、StopStatus

CloseStatus

public class CloseStatus extends Status {
    // 电梯关门状态 可以打开
    @Override
    public void open() {
        // 设置电梯为 打开状态
        super.machine.setCurStatus(LiftStatusMachine.sOpenStatus);
        // 打开电梯
        super.machine.open();
    }

    @Override
    public void close() {
        Log.println("电梯关闭");
    }

    @Override
    public void running() {
        // 设置电梯为 运行状态
        super.machine.setCurStatus(LiftStatusMachine.sRunningStatus);
        // 电梯运行
        super.machine.running();
    }

    @Override
    public void stop() {
        // 设置电梯为 停止状态
        super.machine.setCurStatus(LiftStatusMachine.sStopStatus);
        // 电梯停止
        super.machine.stop();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

OpenStatus

public class OpenStatus extends Status {
    @Override
    public void open() {
        Log.println("电梯打开 ");
    }

    @Override
    public void close() {
        super.machine.setCurStatus(LiftStatusMachine.sCloseStatus);
        super.machine.close();
    }

    @Override
    public void running() {
        // do nothing
        Log.println("error 电梯门打开,无法运行 running !!!");
    }

    @Override
    public void stop() {
        // do nothing
        Log.println("error 电梯门打开,无法停止 stop !!!");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

RunningStatus

public class RunningStatus extends Status {

    @Override
    public void open() {
        // do nothing
        Log.println("error 电梯运行中 无法打开电梯门 open !!! ");
    }

    @Override
    public void close() {
        // do nothing
        Log.println("error 电梯运行中 无法再关闭电梯门 open !!! ");
    }

    @Override
    public void running() {
        Log.println("电梯上下运行中...");
    }

    @Override
    public void stop() {
        super.machine.setCurStatus(LiftStatusMachine.sStopStatus);
        super.machine.stop();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

StopStatus

public class StopStatus extends Status {
    @Override
    public void open() {
        // 电梯停止状态 可以打开电梯门
        super.machine.setCurStatus(LiftStatusMachine.sOpenStatus);
        super.machine.open();
    }

    @Override
    public void close() {
        // 电梯停止状态 无法关闭电梯门(原本就是关闭的???)
        // no nothing
        Log.println("error 电梯停止无法再次关闭电梯门!close ");
    }

    @Override
    public void running() {
        // 电梯停止状态  可以再次运行
        super.machine.setCurStatus(LiftStatusMachine.sRunningStatus);
        super.machine.running();
    }

    @Override
    public void stop() {
        Log.println("电梯 停止运行");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

3、创建环境角色 context – StateMachine

public class LiftStatusMachine {
    /**
     * 当前电梯状态
     */
    private Status curStatus;

    /**
     * 初始化所有电梯状态 电梯门打开、电梯门关闭、电梯运行、电梯停止
     */
    public final static Status sOpenStatus = new OpenStatus();
    public final static Status sCloseStatus = new CloseStatus();
    public final static Status sRunningStatus = new RunningStatus();
    public final static Status sStopStatus = new StopStatus();
    
    public LiftStatusMachine() {
        // 默认电梯是停止状态
        curStatus = new StopStatus();
        curStatus.setLiftStateMachine(this);
    }

    public Status getCurStatus(){
        return curStatus;
    }

    public void setCurStatus(Status status) {
        curStatus = status;
        curStatus.setLiftStateMachine(this);
    }

    public void open() {
        this.curStatus.open();
    }

    public void close() {
        this.curStatus.close();
    }

    public void running() {
        this.curStatus.running();
    }

    public void stop() {
        this.curStatus.stop();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

使用:

public static void main(String[] args) {
        LiftStatusMachine machine = new LiftStatusMachine();
        machine.open();
        machine.stop();
        machine.running();
        machine.open();
        machine.close();
        machine.stop();
        machine.running();
        machine.stop();
        machine.open();
        machine.close();
        machine.close();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
五、少啰嗦一句话解释状态机模式
六、参考

https://juejin.cn/post/6844904005336825863

https://cloud.tencent.com/developer/article/1625473

https://yuriyshea.com/archives/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%8A%B6%E6%80%81%E6%9C%BA

https://www.kancloud.cn/sstd521/design/193606

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/696500
推荐阅读
相关标签
  

闽ICP备14008679号