状态机编程实例及适用范围

简介

        状态机这一概念并不源于软件开发,但其思想确深入软件发展之中(已然成为了一种设计模式),如果之前有好好学过组成原理或者编译原理,一定不会对这个概念陌生。写这篇文章的目的是为了尽可能通俗地总结一下状态机编程思想的特点及适用范围,但介于笔者履历有限,疏漏之处在所难免,请酌情参考。*注:本文只讨论的状态机特指“有限状态自动机”。

状态机编程思想特点

        与传统的上下文编程不同,状态机将程序的行为划分为若干个状态,对于每一个状态规定其行为和可能的状态转换关系。状态机的状态即可以由其内部定义的状态转换关系改变,也可由外部操作改变,从而影响状态机的行为。这样说也不是很通俗,我们看一个例子。

状态机编程实例

        问题情景:求自然数1到100的和。
        这个问题很简单,一般而言,我们会这样解:
int sum = 0;
for(int i = 1;i <= 100;i++)
{sum+=i;
}
System.out.println(sum);

接下来看看状态机编程是如何解决同一个问题的:

        int status = 0;int sum = 0;int i = 1;while(status != -1){switch(status){case 0:{if(i <= 100){sum+=i;i++;}else{status = 1;}}break;case 1:{System.out.println(sum);status = -1;}break;}}

对于上面的status变量,就是状态机的状态变量了,从上面程序中得知,status的有效变量有三个:-1,0,1;我们对这三个状态用语言来概括一下:

状态 状态概括 行为描述 状态转换关系
0 加法状态 将sum的值加i,将i自加 若i大于100,则跳入状态1
1 输出状态 将sum的值输出 跳转到状态-1
-1 终止状态 程序结束
        可以看到,由于程序开始时初始状态为0,i为1,所以执行了0状态100次,之后将sum值输出并退出。同样的一个问题,显然状态机编程更为复杂,那它存在的必要性在哪呢?什么情况下应该使用它呢?

状态机编程思想的适用范围

        在讲述状态机编程的适用范围之前,先来回忆一下,以前编写的很大一部分程序是这样的:编写-》运行-》输入参数-》获得结果-》结束。包括上面的问题实例和很多算法或是数据结构的小程序,程序运行一次输出结果就结束了,换句话说,程序本身没有保持自身持续运行的必要性,在这种情况下使用状态机编程思想无疑是自找麻烦。但是实际上,有很多程序是需要保持自身持续运行的,尤其是为了解决某些阻塞过程而使用多线程机制编写的模块;在这种情形下,应用自动机编程来控制这种模块的行为再好不过。我举个例子说明一下。
问题情景:[音乐播放器]编写音乐播放器,控制其播放、停止、暂停、继续等。
为了能将主要重点放在状态机上,我们对音乐播放器这一模块进行一个抽象,一个音乐播放过程大致能概括如下:
1)打开一个音频输出设备,获得设备句柄
2)打开一个音频文件,获得文件句柄
3)从音频文件句柄读入byte到缓存byte组
4)将缓存byte组写入音频设备句柄(这里是阻塞过程,同时伴随着音乐的播放)
5)若读入过程遇到结尾,则结束播放
我们将上述音频设备抽象为“SoundDevice”,并进行播放器模块的API设计,例如这样的API:
public void play(InputStream mediaFileInputStream);//播放音频文件
public void stop();//关闭音频文件
public void pasue();//暂停播放
public void resume();//继续播放

API的设计不是很困难,现在考虑这样的问题,这个模块必须足够强大,对一些非法操作进行足够的容错处理,例如在关闭的情况下再次关闭,或者在关闭后继续播放等等的调用都要考虑,这时,使用原来“上下文”流水式的编程思想就会很麻烦了,我们看一下它的状态设计:

状态(enum) 状态概括 行为叙述 状态转换关系
CLOSE 停止状态 无行为 可跳转至播放状态
PLAYING 播放状态 从给定缓冲区读取音频byte组,写入设备,若遇到结尾则跳转到CLOSE 可跳转到停止或暂停状态
WAIT 暂停状态 无行为 可跳转回播放状态
TERM 终止状态 终止模块任务 结束线程  

之后是编写的源文件(伪代码):

public class MediaPlayer extends Thread
{SoundDevice soundDevice;//抽象的播放设备byte[] buffer;//音频数据缓存InputStream is;//音频文件流(代指数据源)MediaStatus status = MediaStatus.CLOSE;public enum MediaStatus{PLAYING,WAIT,CLOSE,TERM}//播放音频public void play(InputStream mediaFileInputStream){if(status == MediaStatus.CLOSE){synchronized(is){is = mediaFileInputStream;status = MediaStatus.PLAYING;}}}//关闭音频文件public void stop(){if(status == MediaStatus.PLAYING){synchronized(is){is.close();status = MediaStatus.CLOSE;}}}//暂停播放public void pasue(){if(status == MediaStatus.PLAYING){status = MediaStatus.WAIT;}}//继续播放public void resume(){if(status == MediaStatus.WAIT){status = MediaStatus.PLAYING;}}@Overridepublic void run(){while(status != MediaStatus.TERM){synchronized(is){switch(status){case PLAYING:{if(0 != is.read(buffer)){soundDevice.write(buffer);}else{is.close();status = MediaStatus.CLOSE;}}break;case WAIT:{}case CLOSE:{}default:{Thread.sleep(30)}break;//不要让线程空跑}}}}
}
虽然从代码量上讲没有明显的缩减,但是由于编写每个接口时都只关注某一个状态和转换关系,因此每个问题都不复杂。在给定的接口中,除play需要更改内部数据源引用外,其他接口都是在给定状态下跳转这种简单语句。RUN方法中虽然WAIT和CLOSE都没有任何行为,但是却分出两个状态,这是为了区别“播放一个新文件”与“在暂停基础上继续播放"的两种情况,这样使播放本身(也就是我们对音频设备的写入)不需要重复了。
PS:
其实以前写过的很多程序都是这样的线程自动机,我们把任务划分为状态量与过程量,我们通过外部接口恰当的更改状态量,线程内部循环则能根据给定的状态量完成对应过程,笔者认为这个模式还是很重要的,关于它今后有机会还会在深入总结探讨的。

状态机编程实例及适用范围相关推荐

  1. STM32状态机编程实例——全自动洗衣机(上)

    前面几篇文章,以按键功能,介绍了状态机的原理与按键状态机实例,实现按键单击.双击.长按等状态的检测. 本篇,继续使用状态机编程,来实现一个更有趣的功能--全自动洗衣机. 1 全自动洗衣机功能分析 下面 ...

  2. 排线电机运行控制(梯形图状态机编程)

    这篇博客主要分析排线电机的逻辑时序动作控制,有关排线电机的速度控制(比例随动编程应用)可以参看相关的文章,有详细算法和源代码分析: 绕线机-排线伺服速度解算FC(比例随动编程应用)_RXXW_Dor的 ...

  3. Python并发编程实例教程

    有关Python中的并发编程实例,主要是对Threading模块的应用,文中自定义了一个Threading类库. 一.简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态 ...

  4. iOS网络编程-iCloud键值数据存储编程实例

    iCloud键值数据存储设计 iCloud键值数据存储编程实例,画面中有两个开关控件,左图是设备1点击"设置iCloud数据"按钮,将控件状态保存到iCloud服务器.右图是设备2 ...

  5. NIO Socket编程实例

    1.阻塞模式实例 NIOUtil类,用来通过SOcket获取BufferedReader和PrintWriter. package IO;import java.io.BufferedReader; ...

  6. linux c编程项目实例,Linux c编程实例_例子

    例一:字符与整型变量的实现 #include int main() { int c1,c2; char c3; c1='a'-'A'; c2='b'-'B'; c3='c'-; printf(&quo ...

  7. C#中Socket多线程编程实例

    C#是微软随着VS.net新推出的一门语言.它作为一门新兴的语言,有着C++的强健,又有着VB等的RAD特性.而且,微软推出C#主要的目的是为了对抗Sun公司的Java.大家都知道Java语言的强大功 ...

  8. 《突破C#编程实例五十讲》源文件下载(2)

    上接<<突破C#编程实例五十讲>源文件下载(1)> 有兴趣的朋友下载看看吧,一共有9个压缩包分3篇文章,下载要注意哦,不然解压要出错哦! 转载于:https://blog.51 ...

  9. java编程50实例_java编程实例大全及详解谜底(50例).doc

    java编程实例大全及详解谜底(50例).doc 还剩 33页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价低环保! 内容要点: 谓 ...

  10. Hadoop Streaming编程实例

    Hadoop Streaming是Hadoop提供的多语言编程工具,通过该工具,用户可采用任何语言编写MapReduce程序,本文将介绍几个Hadoop Streaming编程实例,大家可重点从以下几 ...

最新文章

  1. 5月第4周网络安全报告:应用程序漏洞占比达70.3%
  2. 逆向工程核心原理读书笔记-API钩取之记事本小写转大写
  3. SAP云平台与企业数字型转型
  4. OpenGL中shader使用
  5. Linux中拷贝和移动文件
  6. LING与HQL(三)
  7. 初步使用计算机学设计,幼儿园计算机教学设计参考
  8. 用iostat对linux硬盘IO性能进行检测
  9. 关于中国互联网的国际出口-转载
  10. 圆柱体积怎么算立方公式_圆柱体积怎么算立方 高为3米则此圆柱的体积为27
  11. 工信部:将于近期发放5G商用牌照
  12. 历史时点数据统计如何设计表结构_如何做好调薪管理?年度调薪的操作方式
  13. 开机黑屏显示html,电脑开机黑屏只显示光标怎么办?来看看几种原因分析及解决方法!...
  14. 大数据行业前景_大数据未来展望
  15. 计算机安装Hp1005打印机,hp1005打印机安装方法
  16. 【小白练习】一元二次方程计算器
  17. 送书 | 《Django项目开发实战》
  18. 罗永浩的1.1亿,薇娅的卖火箭,蛋蛋的4.8亿,直播魔幻夜埋下了什么
  19. 一篇讲autoconf/automake的好文章
  20. 如何生成股票的macd指标

热门文章

  1. r语言和python的区别-Python与R语言的简要对比
  2. 微信小程序云开发教程-墨刀原型工具入门
  3. iOS 逆向编程(二)越狱入门知识
  4. 案例|高稳定紫外LED光源助力流体力学PSP技术
  5. 用ImDisk在Windows 10中创建内存虚拟盘
  6. Axure 9 简介【原型工具】
  7. cad安装日志文件发生错误_cad安装出现错误 - 卡饭网
  8. QTreeView导航菜单效果
  9. 车载中控桌面布局android,安卓车载导航桌面主题
  10. 中国GBA模拟器先驱李可文不幸去世