状态机杂谈
计算机控制系统的控制程序具有有限状态自动机(FA)的特征,可以用有限状态机理论来描述。有限自动机(Finite Automata Machine)是计算机科学的重要基石,它在软件开发领域内通常被称作有限状态机(Finite State Machine),是一种应用非常广泛的软件设计模式。
状态机这个名字听上去似乎是个很不得了的东西,然而实际上它普遍应用于计算机行业,毕竟现代编程语言都离不开形式语言理论研究的成果,否则编译器都做不出来还哪来的软件开发呢。
那么,状态机究竟是什么,有什么神奇的地方?
自动机与状态机
严格来说这俩是一个东西,计算机科学的知识表示计算机控制程序具有“有限状态自动机”的特征,一般称之为“有限状态机”;而“有限自动机”是一种数学模型,因此大概可以这么说
- 有限状态机是有限自动机这种数学模型在计算机软件开发领域的实践
至于有限自动机的相关信息在数学领域应该有大量的文献资料可供参考,因此不再赘述,仅引用部分文字如下
自动机是有限状态机(FSM)的数学模型。
FSM 是给定符号输入,依据(可表达为一个表格的)转移函数“跳转”过一系列状态的一种机器。在常见的 FSM 的“Mealy”变体中,这个转移函数告诉自动机给定当前状态和当前字符的时候下一个状态是什么。
逐个读取输入中的符号,直到被完全耗尽(把它当作有一个字写在其上的磁带,通过自动机的读磁头来读取它;磁头在磁带上前行移动,一次读一个符号)。一旦输入被耗尽,自动机被称为“停止”了。
依赖自动机停止时的状态,称呼这个自动机要么是“接受”要么“拒绝”这个输入。如果停止于“接受状态”,则自动机“接受”了这个字。在另一方面,如果它停止于“拒绝状态”,则这个字被“拒绝”。自动机接受的所有字的集合被称为“这个自动机接受的语言”。
那么现在回到状态机,从自动机的定义可以看得出来,状态机的重点就在于“状态”的维护和转移。
来看一个例子,比如工厂流水线制造汽车,需要许多名工人在流水线上依次工作,现在要求设计一个工人类来完成整个汽车的装配该如何设计呢?
直截了当一点的话,我们需要的就是一个能根据当前流水线位置来执行方法的工作对象而已,那么先分析一下汽车流水线装配有哪些步骤吧。
简单一点,分成三个步骤
- 装配
- 喷漆
- 测试
那么工人类可以如下设计
class Worker {
private CarInfo car;
public CarInfo Car {
set {
car = value;
}
get {
return car;
}
}
private bool isWorking = false;
public bool IsWorking {
get {
return isWorking;
}
}
public void workOnPipeline() {
if(!car.isComAssembleDone && !isWorking) {
isWorking = true;
PipelineTool.doComAssemble(car);
return;
}
if (!car.isPaintDone && !isWorking) {
isWorking = true;
PipelineTool.doPaint(car);
return;
}
if (!car.isTestDone && !isWorking) {
isWorking = true;
PipelineTool.doTest(car);
return;
}
isWorking = false;
}
}
使用CarInfo来说明当前汽车的状况,工人根据汽车状况来进行工作。
这样的设计当然可以工作得很好,但是别忘了这三个步骤只是最简单的抽象,如果要详细划分的话,流水线上的汽车可能会有很多很多情况,某些工作要先做完才能继续装配某些零件,电路的调试要放在内部装饰之前等等。如果要将这些情况全部考虑进去的话,工人类的主方法可能会被if-else分支变成无法维护的怪物。
除此之外,对工人职责的变更也非常困难,尤其是当步骤增加的时候,每新增一个步骤就要修改与这个步骤相关的所有代码,还必须一遍又一遍地测试,其开发效率可想而知。
想要解决这种问题,状态机就是一个足够优秀的方案。那么为了设计一个合适的状态机,首先要分析工人需要的状态,很显然的三个状态是
- 离开
- 空闲
- 工作
然后对第三种状态进行细化拆分,这样能较为详细地描述整个装配生产过程,将工作状态拆分为以下的细分状态
- 装配内部零件
- 装配外壳
- 装配内饰
- 喷漆
- 测试
有了这样的状态细分,那么可以设计一个状态枚举来维护所有的状态。
public enum WorkerState {
OFFDUTY, // 离开岗位
IDLE, // 空闲
INSIDE_COM, // 装配内部零件
OUTLAYER, // 装配外壳
INSIDE_DEC, // 装配内饰
PAINT, // 喷漆
TEST // 测试
}
接着为工厂流水线设计一个抽象类,所有在流水线上工作的对象都会由该抽象类派生。
abstract class FactoryPipeline {
private WorkerState _state; // 当前状态
public WorkerState state {
get {
return _state;
}
}
// 状态转移
public void changeState(WorkerState targetState) {
_state = targetState;
// 依照状态执行分支
switch(_state) {
case WorkerState.OFFDUTY:
doOffduty();
break;
case WorkerState.IDLE:
doIdle();
break;
case WorkerState.INSIDE_COM:
doInside_com();
break;
case WorkerState.OUTLAYER:
doOutlayer();
break;
case WorkerState.INSIDE_DEC:
doInside_dec();
break;
case WorkerState.PAINT:
doPaint();
break;
case WorkerState.TEST:
doTest();
break;
default:
Console.WriteLine("Error! No Such a State!");
break;
}
}
// 所有状态分支的抽象方法
protected abstract void doOffduty();
protected abstract void doIdle();
protected abstract void doInside_com();
protected abstract void doOutlayer();
protected abstract void doInside_dec();
protected abstract void doPaint();
protected abstract void doTest();
}
接着从流水线抽象基类派生一个工人类
class NormalWorker : FactoryPipeline {
protected override void doIdle() {
Console.WriteLine("Normal Worker idle state");
}
protected over

最低0.47元/天 解锁文章
24

被折叠的 条评论
为什么被折叠?



