控制结构(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)的更多相关文章
- 控制结构(3) 状态机(state machine)
// 上一篇:卫语句(guard clause) // 下一篇:局部化(localization) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 前情回顾 上次分析了guar ...
- 用状态机STATE MACHINE实现有选择的文件转换
用书上的例子实现在解析HTML文本时,对"<>"中的符号不进行字符转换. import sys import string from optparse import O ...
- 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):状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动 作.完成特定操作的控制中心. 类 ...
随机推荐
- 好用的开源库(一)——MaterialEditText
GIthub地址:https://github.com/rengwuxian/MaterialEditText#features 使用文档: 在android新推出的Material Design中对 ...
- 做一个开源的小程序登录模块组件(token)
先了解下SSO 对于单点登陆浅显一点的说就是两种,一种web端的基于Cookie.另一种是跨端的基于Token,一般想要做的都优先做Token吧,个人建议,因为后期扩展也方便哦. 小程序也是呢,做成t ...
- Java虚拟机学习笔记(一)
Java虚拟机运行时数据区域 Java虚拟机将其所管理的内存划分为若干个不同的数据区域.这些区域都有着各自的用途,以及创建和销毁时间.其中有一些会随着虚拟机启动而启动,随着虚拟机退出而销毁:有些则是与 ...
- html标签种类很多,为什么不都用div?
why not divs? 所有html页面标签都可以用div解决,为什么还会存在各种不同的标签呢? 代码是写给机器阅读的,初始化标签更利于快速编程,毕竟很多标签有了自定义属性,无需编码控制,可维护性 ...
- webpack 4 简单介绍
webpack是什么? webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler). 为什么要使用webpack呢? 随着web技术的发展,前端开发不再仅仅是 ...
- Linux内存描述之内存节点node--Linux内存管理(二)
1 内存节点node 1.1 为什么要用node来描述内存 这点前面是说的很明白了, NUMA结构下, 每个处理器CPU与一个本地内存直接相连, 而不同处理器之前则通过总线进行进一步的连接, 因此相对 ...
- CentOS 6.2 中文
在虚拟机里面安装好centos6.2之后,默认是英文! 对于命令行操作无所谓啦,但是如果想看界面,就不是很适应! 修改方法如下: 1.用root登录系统,密码为创建虚拟机时候的密码.创建虚 ...
- 南邮攻防训练平台逆向maze
文件为 ELF64 可执行文件,输入正确 flag 返回判定结果的逆向题. 用 ida 打开,先看f5之后的代码,容易知道 flag 长度为24位,以 "nctf{" 为前缀: 先 ...
- 使用jquery实现选项卡切换效果
几张简陋的框架效果图 页面加载时: 选项卡操作后: css样式: <style type="text/css"> *{margin:0px;padding:0px;} ...
- PHP实现表单提交发送邮件
只需要三个文件就可以了: 注意: 文件自命名需修改表单提交url,包含的类文件名: HTML表单文件: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ...