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)

  • 相关阅读:
    单例模式
    Curator Zookeeper分布式锁
    LruCache算法原理及实现
    lombok 简化java代码注解
    Oracle客户端工具出现“Cannot access NLS data files or invalid environment specified”错误的解决办法
    解决mysql Table ‘xxx’ is marked as crashed and should be repaired的问题。
    Redis 3.0 Cluster集群配置
    分布式锁的三种实现方式
    maven发布项目到私服-snapshot快照库和release发布库的区别和作用及maven常用命令
    How to Use Convolutional Neural Networks for Time Series Classification
  • 原文地址:https://www.cnblogs.com/swobble/p/14769617.html
Copyright © 2011-2022 走看看