实现一个状态机
问题的描述
最近在改仿真软件的状态切换 ,什么意思呢,这东西有点像个播放器,但是不仅仅是播放暂停那么简单。首先我们画一个图 ,以一个图说明:
通过以前的面向对象设计经验我们知道有一种叫状态机的东西, 简而言之就是把每个状态 通过节点对象包装 ,节点是什么类型就是当前处于什么状态,看到箭头的方向没 代表到另一个状态去 ,到的这个过程 我们 把抽象成状态对象的方法。基本思路是把节点抽象成泛化对象,能不能到哪个 到的过程中出现了异常又怎么办 ,分而治之的思想充分的体现了面向对象的威力。 原项目根本没有考虑到任何类似状态机这种的东西 ,全是用的判断,与业务嵌入太重。实际的比理想化的难得的多,但是我们不怕 不管怎样我们先开个头。
实现
首先不管如何所有的指令我们要罗列出来(启动,运行,暂停也叫冻结,停止,快照):
1 public enum Command 2 { 3 Start, 4 ToRun, 5 ToPause, 6 ToStop, 7 Snap 8 }
然后是对节点状态进行泛化,说白了就是抽象出几个状态了类,然后他们有一个共同基类,按钮是否变灰就是根据状态,来的。某个指令能不能执行显然这个是所有泛化对象都要有的,所以我们作为基类方法。
1 public class BaseState 2 { 3 public string StateInfo { get; set; } = "未知状态"; 4 public string ErrorInfo { get; set; } 5 public void Error() 6 { 7 8 } 9 10 public virtual void Execute(Command cmd) 11 { 12 13 } 14 public virtual bool CanExecute(Command cmd) 15 { 16 return true; 17 } 18 19 }
然后是各个具体节点的状态类,别看代码这么长,都是些冗长重复的东西。
1 public class Run : BaseState 2 { 3 public Run() 4 { 5 StateInfo = "运行"; 6 Console.WriteLine("状态变为运行"); 7 } 8 public void ToStop() 9 { 10 Console.WriteLine("转到停止中..."); 11 Envierment.st = new Stop(); 12 } 13 14 public void ToPause() 15 { 16 17 Console.WriteLine("转到暂停中..."); 18 Envierment.st = new Pause(); 19 } 20 21 public void Snap() 22 { 23 Console.WriteLine("快照中..."); 24 Console.WriteLine("快照完成..."); 25 } 26 27 public override void Execute(Command cmd) 28 { 29 //base.Execute(cmd); 30 if (cmd == Command.ToStop) 31 ToStop(); 32 else if (cmd == Command.ToPause) 33 ToPause(); 34 else if (cmd == Command.Snap) 35 Snap(); 36 else 37 { 38 39 } 40 } 41 public override bool CanExecute(Command cmd) 42 { 43 //return base.CanExecute(cmd); 44 if (cmd == Command.ToStop || cmd == Command.ToPause || cmd == Command.Snap) 45 return true; 46 else 47 return false; 48 } 49 50 } 51 52 public class Pause : BaseState 53 { 54 public Pause() 55 { 56 StateInfo = "暂停"; 57 Console.WriteLine("状态变为暂停"); 58 } 59 public void ToRun() 60 { 61 Console.WriteLine("转到运行中..."); 62 63 Envierment.st = new Run(); 64 } 65 public void ToStop() 66 { 67 Console.WriteLine("转到停止中..."); 68 69 Envierment.st = new Stop(); 70 } 71 72 public void Snap() 73 { 74 Console.WriteLine("快照中..."); 75 Console.WriteLine("快照完成..."); 76 } 77 public override void Execute(Command cmd) 78 { 79 //base.Execute(cmd); 80 if (cmd == Command.ToStop) 81 ToStop(); 82 else if (cmd == Command.ToRun) 83 ToRun(); 84 else if (cmd == Command.Snap) 85 Snap(); 86 else 87 { 88 89 } 90 } 91 public override bool CanExecute(Command cmd) 92 { 93 //return base.CanExecute(cmd); 94 if (cmd == Command.ToStop || cmd == Command.ToRun || cmd == Command.Snap) 95 return true; 96 else 97 return false; 98 } 99 } 100 101 public class Stop : BaseState 102 { 103 public Stop() 104 { 105 StateInfo = "停止"; 106 107 Console.WriteLine("状态变为停止"); 108 } 109 public void Start() 110 { 111 Console.WriteLine("转到暂停中..."); 112 Envierment.st = new Pause(); 113 } 114 115 public override void Execute(Command cmd) 116 { 117 //base.Execute(cmd); 118 if (cmd == Command.Start) 119 Start(); 120 else 121 { 122 123 } 124 } 125 public override bool CanExecute(Command cmd) 126 { 127 //return base.CanExecute(cmd); 128 if (cmd == Command.Start) 129 return true; 130 else 131 return false; 132 } 133 }
整体使用
如何用?我们的思想是new一个对象出来放那,然后这个实例他就代表了一个全局的状态,状态切换走的时候 我们new新的对象代替,他们都是基于同一个父类,不同子类是否能执行某些状态切换方法在其内部进行,一个类就代表了一个特定类型的节点,如此实现分而治之的效果。初始是stop状态
1 public class Envierment 2 { 3 public static BaseState st; 4 public static void Initial() 5 { 6 st = new Stop(); 7 } 8 }
1 <WrapPanel> 2 <Button Name="btn_start" Height="25" Width="50" Click="btncmd_Click">启动</Button> 3 <Button Name="btn_torun" Height="25" Width="50" Click="btncmd_Click">运行</Button> 4 <Button Name="btn_topause" Height="25" Width="50" Click="btncmd_Click">暂停</Button> 5 <Button Name="btn_snap" Height="25" Width="50" Click="btncmd_Click">快照</Button> 6 <Button Name="btn_tostop" Height="25" Width="50" Click="btncmd_Click">停止</Button> 7 </WrapPanel> 8 <WrapPanel> 9 <Label Height="25">当前状态:</Label> 10 <Label Height="25" Name="lab_state">停止</Label> 11 </WrapPanel>
1 public Window1() 2 { 3 InitializeComponent(); 4 Envierment.Initial(); 5 lab_state.Content = Envierment.st.StateInfo; 6 } 7 private void btncmd_Click(object sender, RoutedEventArgs e) 8 { 9 Button btn = sender as Button; 10 Command cmd = Command.ToStop; 11 12 if (btn.Name == "btn_start") 13 cmd = Command.Start; 14 if (btn.Name == "btn_torun") 15 cmd = Command.ToRun; 16 if (btn.Name == "btn_topause") 17 cmd = Command.ToPause; 18 if (btn.Name == "btn_snap") 19 cmd = Command.Snap; 20 if (btn.Name == "btn_tostop") 21 cmd = Command.ToStop; 22 23 Envierment.st.Execute(cmd); 24 25 if (Envierment.st.CanExecute(Command.Start)) 26 btn_start.IsEnabled = true; 27 else 28 btn_start.IsEnabled = false; 29 30 if (Envierment.st.CanExecute(Command.ToRun)) 31 btn_torun.IsEnabled = true; 32 else 33 btn_torun.IsEnabled = false; 34 35 if (Envierment.st.CanExecute(Command.ToPause)) 36 btn_topause.IsEnabled = true; 37 else 38 btn_topause.IsEnabled = false; 39 40 if (Envierment.st.CanExecute(Command.Snap)) 41 btn_snap.IsEnabled = true; 42 else 43 btn_snap.IsEnabled = false; 44 45 if (Envierment.st.CanExecute(Command.ToStop)) 46 btn_tostop.IsEnabled = true; 47 else 48 btn_tostop.IsEnabled = false; 49 50 lab_state.Content = Envierment.st.StateInfo; 51 }
最终的效果