zoukankan      html  css  js  c++  java
  • 状态机解决复杂逻辑及使用

    状态机解决复杂逻辑

    开发回顾:

    第一代:两个变量控制逻辑

    1 鼠标 切换背景成程序A的视图/程序B的视图 IsBackgroundA 用于表示当前背景的变量
    切换程序AB激活状态 IsAppAActive 用于表示当前激活程序的变量

    第二代:两个变量控制逻辑

    1 鼠标 切换背景成程序A的视图/程序B的视图 IsBackgroundA 用于表示当前背景的变量
    切换程序AB激活状态 IsAppAActive 用于表示当前激活程序的变量
    2 快捷键 切换背景成程序A的视图/程序B的视图
    切换程序AB激活状态

    第三代:三个变量控制逻辑,且出现两个变量组合来确定关系的情况

    1 鼠标 切换背景成程序A的视图/程序B的视图 IsBackgroundA 用于表示当前背景的变量
    切换程序AB激活状态 IsAppAActive 用于表示当前激活程序的变量
    2 快捷键 切换背景成程序A的视图/程序B的视图
    切换程序AB激活状态
    3 程序A激活状态下 鼠标滑过X区域时,激活B IsAppAActive =true&&CurrentActiveState 用于记录鼠标滑进X区域前的状态,方便滑出后赋值原始状态
    鼠标滑出X区域时,恢复原始激活状态
    执行程序B命令时,激活程序B

    第四代:四个变量控制+排列组合

    需要在鼠标和快捷键上激活命令上添加动画,此时我已经觉得程序不可控起来,

    第一点,是因为动画添加的时机不同,激活A时可能需要先激活A在开始动画,B可能需要先展示动画再激活B,

    第二点,此时需要引入第四个变量来控制动画效果,因为弹入弹出动画是相反的

    1 鼠标 切换背景成程序A的视图/程序B的视图 IsBackgroundA 用于表示当前背景的变量
    切换程序AB激活状态 IsAppAActive 用于表示当前激活程序的变量
    2 快捷键 切换背景成程序A的视图/程序B的视图
    切换程序AB激活状态
    3 程序A激活状态下 鼠标滑过X区域时,激活B IsAppAActive =true&&CurrentActiveState 用于记录鼠标滑进X区域前的状态,方便滑出后赋值原始状态
    鼠标滑出X区域时,恢复原始激活状态
    执行程序B命令时,激活程序B
    4 激活程序时添加动画 IsRightAnimation 来选择动画展示效果 , IsBackgroundA&&IsAppAActive==》来选择动画展示时机

    第五代:五个变量控制+排列组合

    需要多台设备同时开启程序进行同步,接受来自服务器的鼠标键盘命令,需要添加一个变量表示是否具有主控权

    另外,鼠标执行方法中添加了多个判断,包括动画,有的方法是写在Anmation.completed方法中,每次执行一个命令所有变量值几乎都会变一次,遇到问题简直不能调试

    1 鼠标 切换背景成程序A的视图/程序B的视图 IsBackgroundA 用于表示当前背景的变量
    切换程序AB激活状态 IsAppAActive 用于表示当前激活程序的变量
    2 快捷键 切换背景成程序A的视图/程序B的视图
    切换程序AB激活状态
    3 程序A激活状态下 鼠标滑过X区域时,激活B IsAppAActive =true&&CurrentActiveState 用于记录鼠标滑进X区域前的状态,方便滑出后赋值原始状态
    鼠标滑出X区域时,恢复原始激活状态
    执行程序B命令时,激活程序B
    4 激活程序时添加动画 IsRightAnimation 来选择动画展示效果 , IsBackgroundA&&IsAppAActive==》来选择动画展示时机
    5 不同设备间同步,添加发送命令和接受命令 需要多台设备同时开启程序进行同步 IsMaster 是否具有控制权

    第六代:五个变量控制+排列组合+动画时机

    又加一个动画时机,写代码没底,做不下去了感觉,囧

    1 鼠标 切换背景成程序A的视图/程序B的视图 IsBackgroundA 用于表示当前背景的变量
    切换程序AB激活状态 IsAppAActive 用于表示当前激活程序的变量
    2 快捷键 切换背景成程序A的视图/程序B的视图
    切换程序AB激活状态
    3 程序A激活状态下 鼠标滑过X区域时,激活B IsAppAActive =true&&CurrentActiveState 用于记录鼠标滑进X区域前的状态,方便滑出后赋值原始状态
    鼠标滑出X区域时,恢复原始激活状态
    执行程序B命令时,激活程序B
    4 激活程序时添加动画 IsRightAnimation 来选择动画展示效果 , IsBackgroundA&&IsAppAActive==》来选择动画展示时机
    5 不同设备间同步,添加发送命令和接受命令 需要多台设备同时开启程序进行同步 IsMaster 是否具有控制权
    6 切换背景时添加动画 切换程序A动画
    切换程序B动画

    通过状态机整理逻辑:

    定义的状态与触发事件
     public enum ModelState
        {
            MainBackground,
            U3DBackground,
            U3DActive,
            U3DNotActive,
            TaskBarNormal
        }
    
        public enum ModelEvent
        {
            SetMainBackground,
            SetU3DBackground,
            SetU3DActive,
            SetU3DNotActive,
            IncCavansFunction,
            MouseEnterTaskbar,
            MouseLeaveTaskbar
        }
    
    定义状态机事件
      ...
      builder.In(ModelState.MainBackground)
                    .On(ModelEvent.SetMainBackground)
                    .On(ModelEvent.SetU3DBackground).Goto(ModelState.U3DActive);
    
                builder.In(ModelState.U3DBackground)
                  .On(ModelEvent.SetMainBackground).Goto(ModelState.MainBackground);
      ...
    
    为了统一操作流程,我将所有方法定义在状态进入时触发
     	...
     		   builder.In(ModelState.MainBackground)
                    .ExecuteOnEntry(
                    () => {
                        SetTaskbar(false, true);
                        SetBackground(false);
                        SetU3DActive(false);
                    });
    
                builder.In(ModelState.U3DActive)
                 .ExecuteOnEntry(
                    () => {
                        StateMachineMsg += $"------------------------" + Environment.NewLine;
                        StateMachineMsg += $"ModelState.U3DActive" + Environment.NewLine;
                        SetTaskbar(true);
                     SetU3DActive(true);
                     SetBackground(true);
                 });
    	...
    

    之后如果再有什么调整只需要给状态机增加状态和事件,或者调整状态就可以了。

    状态及简单使用:

    安装:

    我觉得主要难点是定义状态和事件,最开始用的时候状态和事件会分不开。

    我们拿上下电梯举例:

    1.0版本:

    电梯四个状态,开门,关门,上一层和下一层

    代码描述:

       builder.In(States.OnFloor)
                    .On(Events.GoUp).Goto(States.MovingUp)
                    .On(Events.GoDown).Goto(States.MovingDown)
       builder.In(States.MovingUp)
       				.On(Events.Stop).Goto(States.OnFloor);
       builder.In(States.MovingDown)
       				.On(Events.Stop).Goto(States.OnFloor);						
    

    2.0版本

    在楼层时,我们增加开门关门的语音提示,我们增加一个关门/开门状态,在进入状态时播放提示

      builder.In(States.OnFloor)
                    .On(Events.CloseDoor).Goto(States.DoorClosed)
                    .On(Events.OpenDoor).Goto(States.DoorOpen)
                    .On(Events.GoUp).Goto(States.MovingUp)
                    .On(Events.GoDown).Goto(States.MovingDown)
       builder.In(States.MovingUp)
       				.On(Events.Stop).Goto(States.OnFloor);
       builder.In(States.MovingDown)
       				.On(Events.Stop).Goto(States.OnFloor);			
       builder.In(States.DoorOpen)
           			 .ExecuteOnEntry(
                    () => {
                        Said(“正在开门.”);
                    });
       				.On(Events.CloseDoor).Goto(States.DoorClosed)	
       builder.In(States.DoorClosed)
                    .ExecuteOnEntry(
                    () => {
                        Said(“正在关门.”);
                    });
       				.On(Events.OpenDoor).Goto(States.DoorOpen)
           
           
    

    3.0版本

    我们可以发现,Door状态只与OnFloor发生关系,Moving状态也只与OnFloor发生关系

    我们可以将定两个层级关系的状态来描述这些关系。

     builder.In(States.OnFloor)
                    .On(Events.CloseDoor).Goto(States.DoorClosed)
                    .On(Events.OpenDoor).Goto(States.DoorOpen)
                    .On(Events.GoUp)
                    .On(Events.GoDown)
     builder.In(States.Moving)
                    .On(Events.Stop).Goto(States.OnFloor);
    
     builder.In(States.DoorOpen)
           			 .ExecuteOnEntry(
                    () => {
                        Said(“正在开门.”);
                    });
     builder.In(States.DoorClosed)
                    .ExecuteOnEntry(
                    () => {
                        Said(“正在关门.”);
                    });
    
    //定义层级
     builder.DefineHierarchyOn(States.Moving)
                    .WithHistoryType(HistoryType.Shallow)
                    .WithInitialSubState(States.MovingUp)
                    .WithSubState(States.MovingDown);
    
     builder.DefineHierarchyOn(States.OnFloor)
                    .WithHistoryType(HistoryType.None)
                    .WithInitialSubState(States.DoorClosed)
                    .WithSubState(States.DoorOpen);
    

    History Types:

    None: 状态进入初始子状态。子状态本身进入其初始子状态等,直到到达最内层的嵌套状态。.
    Deep: T状态进入其最后一个活动子状态。子状态本身进入其最后的活跃状态等,直到到达最内层的嵌套状态.
    Shallow: 状态进入其最后一个活动子状态。子状态本身进入其初始子状态等,直到到达最内层的嵌套状态.
    

    比如:

    当我们去MovingUp的状态,是先到Moving状态,再到MovingUp状态。

    4.0版本,添加在楼层检查超重

    builder.In(States.OnFloor)
                    .On(Events.CloseDoor).Goto(States.DoorClosed)
                    .On(Events.OpenDoor).Goto(States.DoorOpen)
                    .On(Events.GoUp)
                        .If(CheckOverload).Goto(States.MovingUp)
                        .Otherwise().Execute(this.AnnounceOverload)
                    .On(Events.GoDown)
                        .If(CheckOverload).Goto(States.MovingDown)
                        .Otherwise().Execute(this.AnnounceOverload);
    

    5.0版本 添加电梯异常状态

              builder.DefineHierarchyOn(States.Healthy)
                    .WithHistoryType(HistoryType.Deep)
                    .WithInitialSubState(States.OnFloor)
                    .WithSubState(States.Moving);
    
                builder.DefineHierarchyOn(States.Moving)
                    .WithHistoryType(HistoryType.Shallow)
                    .WithInitialSubState(States.MovingUp)
                    .WithSubState(States.MovingDown);
    
                builder.DefineHierarchyOn(States.OnFloor)
                    .WithHistoryType(HistoryType.None)
                    .WithInitialSubState(States.DoorClosed)
                    .WithSubState(States.DoorOpen);
    
                builder.In(States.Healthy)
                    .On(Events.Error).Goto(States.Error);
    
                builder.In(States.Error)
                    .On(Events.Reset).Goto(States.Healthy)
                    .On(Events.Error);
    
                builder.In(States.OnFloor)
                    .ExecuteOnEntry(this.AnnounceFloor)
                    .ExecuteOnExit(Beep)
                    .ExecuteOnExit(Beep) // just beep a second time
                    .On(Events.CloseDoor).Goto(States.DoorClosed)
                    .On(Events.OpenDoor).Goto(States.DoorOpen)
                    .On(Events.GoUp)
                        .If(CheckOverload).Goto(States.MovingUp)
                        .Otherwise().Execute(this.AnnounceOverload)
                    .On(Events.GoDown)
                        .If(CheckOverload).Goto(States.MovingDown)
                        .Otherwise().Execute(this.AnnounceOverload);
                builder.In(States.Moving)
                    .On(Events.Stop).Goto(States.OnFloor);
    

    再也不用变量+IF ELSE了,

    添加状态和事件就可以了

    逻辑也更清晰

    Demo

    tiancai4652/StateMachineSapmle: appccelerate/statemachine wpf Sample (github.com)

  • 相关阅读:
    Building fresh packages卡很久
    后端阿里代码扫描
    npm 使用淘宝镜像
    git镜像
    mysql安装8.0.18
    idea2019.2.2版本破解
    JDK下载很慢
    解决GitHub下载速度慢下载失败的问题
    Hashtable多线程遍历问题
    2-18 求组合数 注:代码有问题找不出哪儿错了
  • 原文地址:https://www.cnblogs.com/swobble/p/14769617.html
Copyright © 2011-2022 走看看