有限状态机问题编程实践(下)

简介: 有限状态机问题编程实践(下)

现在我们需要另外的一个模型来负责状态变迁的过程控制, 它需要足够的通用和稳定, 整个状态机的运行模式应该是这样:

//定义初始状态为shutdown

define state = shutdown;

//定义状态变迁的实例映射关系

define operations={启动:启动操作实例, 关闭:关闭操作实例...}

//接收到了消息

while receive x->{type,cmd}

//根据类型检索消息

deviceOperation = operations.get(x.type)

//强制状态检查

if deviceOperation.avaliableStatus() contains state; then

//执行指令

deviceOperation.doOperation(x.instruction)

//更新状态

state=deviceOperation.nextStatus()

endif

done

在能够按照我们想象的方式执行之前, 我们还需要一点灵活性, 将状态机的状态变迁图映射到操作模型中来, 也就是回答一个给定迁移动作的原状态和目标状态分别是什么以及如何执行的问题, 这一点可以通过配置的方式来完成, 比如:

<stateManagement>

<operation>

<eventType>boot</eventType>

<sourceStatus>

<value>powerOff</value>

</sourceStatus>

<targetStatus>started</targetStatus>

<actionExecutor>a.b.BootExecutor</actionExecutor>

</operation>

<operation>

<eventType>shutdown</eventType>

<sourceStatus>

<value>started</value>

<value>linked</value>

<value>standby</value>

<value>linkedstandby</value>

</sourceStatus>

<targetStatus>powerOff</targetStatus>

<actionExecutor>a.b.ShutDownExecutor</actionExecutor>

</operation>

....

</stateManagement>

通过解析这样的配置文件, 我们可以很容易的将操作类型与操作实例映射起来:

public class ConfiguredDeviceOperation implements DeviceOperation {

/** 操作类型 */

private final EventTypeEnum operationType;

/** 目标状态 */

private final DeviceStatus nextStatus;

private final DeviceStatus nextStatus;

/** 指令执行器 */

private final Executor executor;

/** 支持此操作的源状态集合 */

private final Set<DeviceStatus> avaliableStatus = new HashSet<DeviceStatus>();

/**

* 构造函数, 根据给定的配置服务对象构造一个设备操作对象

* @param operationType 操作类型

* @param nextStatus 下一个状态

* @param avaliableStatus 可执行操作的目标状态

* @param executor 执行器

*/

public ConfiguredDeviceOperation(final EventTypeEnum operationType, final DeviceStatus nextStatus, final

Set<DeviceStatus> avaliableStatus, final Executor executor) {

this.operationType = operationType;

this.nextStatus = nextStatus;

this.executor = executor;

this.avaliableStatus.addAll(avaliableStatus);

}

...

现在整个状态机每一条类型不同的边对应了一条配置,我们把易变的部分从状态迁移逻辑中抽离出来了,现在控制模型的代码只需要表达一个通用的执行流程,显著的增加了结构的稳定性:

//获取接受到的命令类型

final EventTypeEnum eventType = event.eventType();

//获取接受到的命令内容

final byte[] cmd = event.getInstruction();

//从加载的配置中获取设备操作实例

final DeviceOperation deviceOperation = CONFIG.get(eventType);

//判断当前状态是否可以执行操作

if(deviceOperation.avaliableStatus().contains(currentDeviceStatus)) {

deviceOperation.doOperation(cmd);

currentDeviceStatus = deviceOperation.nextStatus();

return true;

}

return false;

现在我们再回答要新增一个状态端点要做什么的问题:

  • 在状态图中新增端点和边
  • 对新指令添加新的指令执行器。
  • 在配置中写出新增边的描述,对于已存在的边,在源状态列表中添加新的状态端点值。

归纳起来,我们需要新增指令执行器,增加和修改配置项。通过简单的改动,较大程度的消除了代码的更改,符合开闭原则,同时,对于配置项的修改,我们甚至可以更进一步,在后台中增加新的功能来辅助完成。从代码的复杂度上来看,消除了大量的分支判断,让代码更有层次,更加简洁了,读起来不再烧脑,从维护的角度看,减少代码的修改也就减少了出错的可能,从mock的角度看,我们抽象出了边的概念,使用mockito或你所熟悉的任意mock方式来修改其行为都是非常方便的。


通用化的描述-使用DSL

前面我们使用特定的java语法来实现了一个较为灵活的状态机,引入了一段xml配置文件来简化我们的实现, 但是对于描述像状态机这样有着显著领域特征的问题, 这种方式还是太程序化以及依赖编程技巧, 如果我们想要清晰的, 通用的表达我们所要解决的问题,或者想要提高开发效率,抽象构建模型,抽取公共的代码,减少重复的劳动,亦或想要灵活应对环境或平台的改变,脱离特定语言的捆绑, 那么我们可以考虑使用DSL来解决问题:

<stateManagement initial="powerOff">

<events>

<event id="deviceShutDown" type="shutdown" />

<event id="deviceBoot" type="boot" />

<event id="deviceActive" type="active" />

<event id="deviceStandBy" type="standby" />

...

</events>

<states>

<state name="powerOff">

<transition event="deviceBoot" target="started"/>

<state/>

<state name="started">

<transition event="deviceShutDown" target="powerOff"/>

<transition event="deviceStandBy" target="standby"/>

<state/>

<state name="standby">

<transition event="deviceShutDown" target="powerOff"/>

<transition event="deviceActive" target="started"/>

<state/>

...

<states/>

</stateManagement>

这里是一段状态机的外部DSL代码, 本质上就是一段XML, 我们抽取了这类问题的惯有模式,用声明的方式提供了事件类型, 状态以及此状态下可能的转换行为。这种DSL描述的控制结构可以很容易的与各种特定编程语言进行绑定,甚至可以定制化的进行代码生成。此外,从表述能力来看, 它明显会好过java实现的版本, 一个团队中的业务专家,开发,测试人员可以很容易的去阅读和理解,降低沟通和理解的成本。


结语

状态机是我们日常工作中非常常见的一种问题场景, 在这篇文章中我们对一个简单的例子进行分析并运用常见的工程手段来得出一个灵活的实现,文章中没有去谈论任何设计模式相关的东西, 而是着眼于更加本质的需求: 灵活, 简洁, 符合开闭原则 去不断的分析和改进, 最终获得一个满意的结果, 在此之外, 我们也尝试着使用外部DSL来抽取了状态机问题的惯有模式,区别于java语言版本的是, 这种方式更加注重通用化能力和信息交流的方便程度, 提供了更加可视化的,便携的解决方式。

相关文章
|
6月前
|
Python
电学:原理、应用与编程实践
电学:原理、应用与编程实践
|
6月前
|
Python
物理光学初探:原理、应用与编程实践
物理光学初探:原理、应用与编程实践
|
11月前
|
人工智能 安全 图形学
有限状态机的概念
有限状态机的概念
154 0
|
C语言 Perl
西门子S7-1200有哪几种编程方法?什么是线性化编程、模块化编程、结构化编程?
今天我们来说一说西门子S7-1200有哪几种编程方法,给大家简单介绍一下什么是线性化编程、模块化编程和结构化编程。
西门子S7-1200有哪几种编程方法?什么是线性化编程、模块化编程、结构化编程?
有限状态机
有限状态机简介 有限状态机(FSM)是许多数字系统中用来控制系统和数据流路径行为的时序电路。FSM的实例包括控制单元和时序。 本实验介绍了两种类型的FSM(Mealy和Moore)的概念,以及开发此类状态机的建模方式。 请参阅Vivado教程,了解如何使用Vivado工具创建项目和验证数字电路。 Mealy FSM(米利型有限状态机) 有限状态机(FSM)或称简单状态机用于设计计算机程序和时序逻辑电路。它被设想为抽象机器,可以处于有限数量的用户定义状态之一。机器一次只能处于一种状态; 它在任何给定时间所处的状态称为当前状态。 当由触发事件或条件启动时,它可以从一种状态改变为另一种状态;
214 0
|
消息中间件 安全 程序员
关于防御性编程,你应该知道的事
提起编程,对于程序员同学而言并不陌生,关于防御性编程相信大家也有所耳闻,但是它具体包括哪些内容呢?
关于防御性编程,你应该知道的事
|
Java API
有限状态机问题编程实践(上)
摘要:一般来说,实体的可能状态是有限的, 在满足一定的条件的情况下触发特定动作会发生实体的状态迁移。对于这类问题,我们一般称为FSM(Finite State Machine), 即有限状态机。本文分享一个有限状态机的java实现,以及使用DSL实现的通用化描述。
705 0
有限状态机问题编程实践(上)
|
API Python
transitions-一种轻量级的,面向对象的有限状态机实现
transitions是Python中的轻量级、面向对象的状态机实现,具有许多扩展。与Python 2.7+和3.0+兼容。 任何好的状态机(毫无疑问,还有很多坏的状态机)的灵魂都是一组状态。我们通过将字符串列表传递给Machine初始化程序来定义有效的模型状态。但是在内部,状态实际上表示为State对象。
1620 0
transitions-一种轻量级的,面向对象的有限状态机实现
|
调度
状态机思路在程序设计中的应用(转)
  状态机的概念   状态机是软件编程中的一个重要概念,比这个概念更重要的是对它的灵活应用。在一个思路清晰而且高效的程序中,必然有状态机的身影浮现。   比如说一个按键命令解析程序,就可以被看做状态机:本来在A状态下,触发一个按键后切换到了B状态,再触发另一个键后切换到C状态,或者返回到A状态。
1779 0
下一篇
无影云桌面