代码失控与状态机(上)

前言 前几天和某某同学吃饭席间,他聊到每当要修改老项目中自己写的代码时就痛苦不堪,问我是不是也有同感。我觉得这应该是不少程序猿的心声,之所以会这样,大致有两个主因: 项目的整体设计很糟糕,只管往上堆砌各种功能、补丁,对于代码质量和结构关系基本无暇顾及,最终积重难返滑向失控。 对技术缺乏必要的敬畏心,基础不够扎实、知识面较窄,不能(无法)进行合理的规划,最终导致停留在低水平的代码堆砌上,只求完成功能就万事大吉。 程序猿饭桌上总少不了对产品经理的吐槽:“产品经理又对业务流程进行了疯狂调整,我觉得这会导致状态机无法支持了。”他的这个槽点让我一时有些语塞,倒不是怀疑产品经理的脑洞还能大到把状态机开到失控,只是诧异难道我们还有比状态机更适合应对业务流程变更的武器吗? 事实上状态机对于软件工程师来说应该是个很基础的知识点,它原理简单却拥有强大的适应力并被广泛应用 (譬如:游戏开发、工作流、编译器、正则表达式等解析器中) ,掌握好它的原理和应用,能帮助我们从容应对很多棘手问题,它于程序猿应对复杂流程性问题,就好比医生使用抗生素应对细菌感染一样的最佳武器。同时,它还是防止代码失控的一剂良药。 基本概念 状态机一般泛指“有限状态机(Finite State Machine)”,《离散数学》中有关于它的专门章节,以下谨为我对相关概念的形式上的非精准释义,如有出入请以教科书或相关学术资料为准。 状态:顾名思义表示某个时刻系统处于一个特定的阶段。通常我们不考虑中间态,也可以把中间态进行退化处理。当状态发生变更,就叫状态转换(Transfer)或状态迁移(Transition)。 事件:驱动系统进行状态转换/迁移的源,提供这种源的也常被称为“触发器(Trigger)”。 行为:当系统进行状态转换时进行的响应处理,提供响应处理的程序也常被称为“处理器(Handler)”。 有了上面的基本概念,我们来看一个最简单的状态图: 状态图 你可能会奇怪这个图怎么跟网上那些状态机图不一样,连状态转换条件都没有呢?这是因为,我觉得在了解状态机之前,最好先将确立以下两种概念: 状态驱动: 状态机负责根据输入来驱动状态流转。 迁移判定: 在状态流转过程中确定当前状态是否需要进行转换/迁移,以及转换/迁移到哪个状态中的判定机制。 所以,在常见的状态机图中标注的那些状态转换条件只是“迁移判定”的一种具体表现形式,它即可以由状态机内置,也可以是独立的判定器来处理,又或者由状态图预先定义好,如此等等。 建立“状态驱动”和“迁移判定”这两个被抽象化的概念,有助于我们深入理解状态机的机理,并且对我们设计一个鲁棒性和扩展性更好到状态机有实际指导意义。 状态机图 以下是表示一个‘简陋’的 Email 地址格式的解析器状态图,状态迁移条件采用正则表达式来表达,其中图二又称为“状态迁移图”。 节点式状态机图 图一:节点式 表格式状态机图 图二:表格式(红色格表示拒绝或异常;灰色格表示忽略或无意义;其他表示迁移条件) 代码实现 有了上面的状态图,就像建筑工人拿到了详细的建筑设计图纸;现在我们只需要对着状态机图,把它映射成代码即可完成一个基本状态机。状态机图越详细,实现起来就越容易,同时代码的可维护性也越好。 public class Email { public string Identifier { get; private set;} public string Host { get; private set; } public string Domain { get; private set; } private Email() {} public static Email Parse(string text) { if(string.IsNullOrEmpty(text)) return null; var state = State.None; /* The State-Driven */ for(int i=0; i
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信