控制结构(3) 状态机(state machine)
// 上一篇:卫语句(guard clause)
// 下一篇:局部化(localization)
基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构。
前情回顾
上次分析了guard 控制结构,有人说:“可是阅读很多开源代码也是if/else层层嵌套的”。是的,guard控制结构只是一种风格,语言并没有规定你一定要这么写,也没必要。两种风格都能写出逻辑清晰的代码,但是还是有细微差别,嵌套模式下,要写出清晰的代码需要更好的做好函数的分层(见第1节),例如,在分支里只做函数的转发调用,那么,代码也是清晰可读的。
function doSomething(){
if(condition1){
call1();
}else{
if(condition2){
call2();
}else{
call3();
}
}
}
我们来看一个新的结构。
典型代码:
let opening = false;
let opened = false;
let closing = false;
let closed = false;
let step1 = false;
function open(onComplete){
if(closing||closed){
return onComplete(RESULT.CLOSED)
}
if(opening){
return onComplete(RESULT.PENDING);
}
if(opened){
return onComplete(RESULT.SUCCESS);
}
opening = true;
doOpen(function(err){
onComplete(err);
opening = false;
opened = true;
});
}
function close(onComplete){
if(!opened){
return onComplete(RESULT.INVALID_OP);
}
if(closing||closed){
return onComplete(RESULT.PENDING);
}
closing = true;
doClose(function(err){
onComplete(err);
closing = false;
closed = true;
})
}
function start(onComplete){
open((err)=>{
if(err){
return onComplete(err);
}
step1((err)=>{
if(err){
return onComplete(err);
}
step2((err)=>{
onComplete(err);
close((err)=>{
log(`close, ret:${err}`);
})
})
})
})
}
function step1(onComplete){
if(!opened){
return onComplete(RESULT.NOT_OPEN);
}
doStep1(function(err){
step1 = true;
onComplete(err);
});
}
function step2(onComplete){
if(!opened){
return onComplete(RESULT.NOT_OPEN);
}
if(!step1){
return onComplete(RESULT.INVALID_STEP);
}
doStep2(function(err){
onComplete(err);
});
}
结构分析
这段代码使用了多个bool变量来做状态标记,为了安全有序的做好每个异步动作,需要在适当的地方对每个bool标志变量做判断。如果这个流程继续复杂起来,bool变量之间的状态组合会变的更加繁杂,也很容易一不小心就漏掉状态的组合判断,导致难以定位的BUG。此时,我们可以采用一种重要的控制结构。
...
是的,状态机(state machine)结构。我们先来看看使用状态机控制结构改写后的代码:
/**所有的状态*/
const STATE = {
INIT: 0,
OPENING: 1,
OPENED: 2,
STEP1: 3,
STEP2: 4,
CLOSING: 5,
CLOSED: 6,
ERROR: 7
}
/**每个状态都只允许从指定状态转移过来*/
const stateTransfer = {
STATE.INIT: [],
STATE.OPENING: [STATE.INIT],
STATE.OPENED: [STATE.OPENING],
STATE.STEP1: [STATE.OPENED],
STATE.STEP2: [STATE.STEP1],
STATE.CLOSING: [STATE.INIT, STATE.OPENING, STATE.OPENED, STATE.STEP1, STATE.STEP2],
STATE.CLOSED: [STATE.CLOSING],
STATE.ERROR: [STATE.INIT, STATE.OPENING, STATE.OPENED, STATE.STEP1, STATE.STEP2],
}
let state = STATE.INIT;
let stateChangeEvent = null;
function moveTo(err, newState){
let nextState = err ? STATE.ERROR : newState;
if(stateTransfer[nextState].indexOf(state)===-1){
return RESULT.INVALID_STATE_TRANSFER;
}
let oldState = state;
state = nextState;
stateChangeEvent(oldState, state, err);
do();
return RESULT.SUCCESS;
}
function do(){
switch(state){
case STATE.INIT:
moveTo(0, STATE.OPENING);
break;
case STATE.OPENING:
doOpen((err)=>{
moveTo(err, STATE.OPENED);
});
break;
case STATE.OPENED:
doStep1((err)=>{
moveTo(err, STATE.STEP1);
});
break;
case STATE.STEP1:
doStep2((err)=>{
moveTo(err, STATE.STEP2);
});
break;
case STATE.STEP2:
moveTo(0, STATE.CLOSING);
break;
case STATE.CLOSING:
doClose((err)=>{
moveTo(err, STATE.CLOSED);
});
break;
case STATE.CLOSED:
break;
default:
break;
}
}
function start(onStateChange){
stateChangeEvent = onStateChange;
do();
}
语义分析
看上去好像多了很多代码。然而在复杂的异步处理流程中,状态机能清晰地表达逻辑,保证逻辑代码的时序,并在形式推理上发现和消灭漏洞,阅读do里面的逻辑,状态之间的转换直接、明显。编程语言提供了基本的状态标志位:bool变量。这便于让程序员在编程中随意地使用bool变量标记各种状态,但0/1状态标示在稍微复杂一点的状态变化中,就会显得不好用:
- 出现组合爆炸,你需要对多个bool变量做组合判断,这很容易漏掉某个状态组合。
- 设计上的失误,导致bool标志位之间不是正交的,因而任一时刻,程序的状态不能确保唯一,这会导致程序出现未知的时序问题。
事实上,任何程序在运行中本身就是一个状态机:
- 当前在第i行执行
- 根据条件,进入第j行执行
这里的状态就是:
- pc, 也就是program counter, 程序计数器
- condition,改变pc的条件
如果想更清楚地理解这点,可以观看Lamport制作的视频,要看到第2集:
http://lamport.azurewebsites.net/video/intro.html
控制结构(3) 状态机(state machine)的更多相关文章
- 用状态机STATE MACHINE实现有选择的文件转换
用书上的例子实现在解析HTML文本时,对"<>"中的符号不进行字符转换. import sys import string from optparse import O ...
- 控制结构(3): 状态机(state machine)
// 上一篇:卫语句(guard clause) // 下一篇:局部化(localization) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 前情回顾 上次分析了guar ...
- Android4.0中蓝牙适配器state machine(状态机)的分析
今天晓东和大家来一起看一下Android4.0中蓝牙适配器(Bluetooth Adapter)的状态机变化的过程.首先,我们需要了解一下,蓝牙适配器究竟有哪些状态,从代码可以清晰地看到(framew ...
- 【UML】-NO.43.EBook.5.UML.1.003-【UML 大战需求分析】- 状态机图(State Machine Diagram)
1.0.0 Summary Tittle:[UML]-NO.43.EBook.1.UML.1.003-[UML 大战需求分析]- 状态机图(State Machine Diagram) Style:D ...
- 【翻译】What is State Machine Diagram(什么是状态机图)?
[翻译]What is State Machine Diagram(什么是状态机图)? 写在前面 在上一篇学习类图的时候将这个网站上的类图的一篇文章翻译了出来,感觉受益良多,今天来学习UML状态机图, ...
- Java Secret: Using an enum to build a State machine(Java秘术:用枚举构建一个状态机)
近期在读Hadoop#Yarn部分的源代码.读到状态机那一部分的时候,感到enmu的使用方法实在是太灵活了,在给并发编程网翻译一篇文章的时候,正好碰到一篇这种文章.就赶紧翻译下来,涨涨姿势. 原文链接 ...
- Raft算法系列教程2:状态机复制 (State Machine Replication)
分区容错如何保证? 在分布式系统设计中,需要遵循CAP理论,如果我们要让一个服务具有容错能力,那么最常用最直接的办法就是让一个服务的多个副本同时运行在不同的节点上.但是,当一个服务的多个副本都在运行的 ...
- State Machine.(状态机)
What is a State Machine? Any device that changes its state from one to another due to some actions a ...
- Finite State Machine 是什么?
状态机(Finite State Machine):状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动 作.完成特定操作的控制中心. 类 ...
随机推荐
- 无插件VIM编程技巧(网摘)
无插件VIM编程技巧 原文出处:[陈皓 coolshell] 相信大家看过<简明Vim教程>也玩了<Vim大冒险>的游戏了,相信大家对Vim都有一个好的入门了.我在这里把我日常 ...
- SQL存储过程和函数
SQL存储过程: 由来:在具体应用中,一个完整的操作会包含多条SQL语句,在执行过程中需要根据前面SQL语句的执行结果有选择的执行后面的SQL语句.因此,mysql提供了数据库对象存储过程和函数. 定 ...
- JAVA对XML文件的读写(有具体的代码和解析
XML 指可扩展标记语言(EXtensible Markup Language),是独立于软件和硬件的信息传输工具,应用于 web 开发的许多方面,常用于简化数据的存储和共享. xml指令 处理指令, ...
- Selenium发展史
Jason Huggins在2004年发起了Selenium项目,当时身处ThoughtWorks的他,为了不想让自己的时间浪费在无聊的重复性工作中,幸运的是,所有被测试的浏览器都支持Javascri ...
- 【Ubuntu 16】安装net-snmp
使用tar.gz压缩包安装mongodb时报错,没有库文件 libnetsnmpmibs.so.3o cannot open file or directory 在网上找了一篇文章 需要安装net-s ...
- Intellj IDEA常用快捷键
1.生成Getter.Setter方法:Alt + Insert
- Thread.interrupt()方法理解
原博客地址: 多线程编程 实战篇 (四) 不客气地说,至少有一半人认为,线程的"中断"就是让线程停止. 如果你也这么认为,那你对多线程编程还没有入门. 在java中,线程的中断(i ...
- Django 模板.html中 href参数传入
在Django模板中可能会出现访问多个URL指向同一函数,为减少代码可以利用正则表达式实现 url(r'^remove_userinfo(?P<nid>\d+)/',views.remov ...
- 【Vue】Vue中的父子组件通讯以及使用sync同步父子组件数据
前言: 之前写过一篇文章<在不同场景下Vue组件间的数据交流>,但现在来看,其中关于“父子组件通信”的介绍仍有诸多缺漏或者不当之处, 正好这几天学习了关于用sync修饰符做父子组件数据双向 ...
- TensorFlow构建卷积神经网络/模型保存与加载/正则化
TensorFlow 官方文档:https://www.tensorflow.org/api_guides/python/math_ops # Arithmetic Operators import ...