《领域特定语言》一1.2 状态机模型

简介: 本节书摘来自华章出版社《领域特定语言》一书中的第1章,第1.2节,作者 (英)Martin Fowler,更多章节内容可以访问云栖社区“华章计算机”公众号查看

1.2 状态机模型

一旦团队达成共识,认为对于指定控制器如何运作而言,状态机是一个恰当的抽象,那么,下一步就是确保这个抽象能够运用到软件自身。如果人们在考虑控制器行为时,也要考虑事件、状态和转换,那么,我们希望这些词汇也可以出现在软件代码里。从本质上说,这就是领域驱动设计(Domain–Driven Design)中的Ubiquitous Language [Evans DDD] 原则,也就是说,我们在领域人员(那些描述建筑安全该如何运作的人)和程序员之间构建的一种共享语言。
对于Java程序来说,处理这种事,自然的方式就是以状态机为Domain Model [Fowler PoEAA]。状态机框架的类图见图1-2。
image

通过接收事件消息和发送命令消息,控制器得以同设备通信。这些消息都是四字母编码,它们可以通过通信通道进行发送。在控制器代码里,我想用符号名(symbolic name)引用这些消息。我创建了事件类和命令类,它们都有代码(code)和名字(name)。我把它们放到单独的类里(有一个超类),因为在控制器的代码里,它们扮演着不同的角色。

class AbstractEvent...
 private String name, code;

 public AbstractEvent(String name, String code) {
  this.name = name;
  this.code = code;
 }
 public String getCode() { return code;}
 public String getName() { return name;}
public class Command extends AbstractEvent
public class Event extends AbstractEvent

状态类记录了它会发送的命令及其相应的转换。

class State...
 private String name;
 private List<Command> actions = new ArrayList<Command>();
 private Map<String, Transition> transitions = new HashMap<String, Transition>();

class State...
 public void addTransition(Event event, State targetState) {
  assert null != targetState;
  transitions.put(event.getCode(), new Transition(this, event, targetState));
 }

class Transition...
 private final State source, target;
 private final Event trigger;

 public Transition(State source, Event trigger, State target) {
  this.source = source;
  this.target = target;
  this.trigger = trigger;
 }
 public State getSource() {return source;}
 public State getTarget() {return target;}
 public Event getTrigger() {return trigger;}
 public String getEventCode() {return trigger.getCode();}

状态机保存了其起始状态。

class StateMachine...
 private State start;

 public StateMachine(State start) {
  this.start = start;
 }

这样,从这个状态可以到达状态机里的任何状态。

class StateMachine...
 public Collection<State> getStates() {
  List<State> result = new ArrayList<State>();
  collectStates(result, start);
  return result;
 }

 private void collectStates(Collection<State> result, State s) {
  if (result.contains(s)) return;
  result.add(s);
  for (State next : s.getAllTargets())
   collectStates(result, next);
 }
class State...
 Collection<State> getAllTargets() {
  List<State> result = new ArrayList<State>();
  for (Transition t : transitions.values()) result.add(t.getTarget());
  return result;
 }

为了处理重置事件,我在状态机上保存了一个列表。

class StateMachine...
 private List<Event> resetEvents = new ArrayList<Event>();

 public void addResetEvents(Event... events) {
  for (Event e : events) resetEvents.add(e);
 }

像这样用一个单独结构处理重置事件并不是必需的。简单地在状态机上声明一些额外的转换,也可以处理这种情况,如下所示:

class StateMachine...
 private void addResetEvent_byAddingTransitions(Event e) {
  for (State s : getStates())
   if (!s.hasTransition(e.getCode())) s.addTransition(e, start);
 }

我倾向于在状态机上设置显式的重置事件,这样可以更好地表现意图。虽然这样做确实使状态机有点复杂,但它也更加清晰地表现出通用状态机该如何运作,要定义特定状态机也会更加清晰。
处理完结构,再来看看行为。事实证明,这真的相当简单。控制器有个handle方法,它以从设备接收到的事件代码为参数。

class Controller...
 private State currentState;
 private StateMachine machine;

 public CommandChannel getCommandChannel() {
  return commandsChannel;
 }

 private CommandChannel commandsChannel;

 public void handle(String eventCode) {
  if (currentState.hasTransition(eventCode))
   transitionTo(currentState.targetState(eventCode));
  else if (machine.isResetEvent(eventCode))
   transitionTo(machine.getStart());
   // ignore unknown events
 }

 private void transitionTo(State target) {
  currentState = target;
  currentState.executeActions(commandsChannel);
 }

class State...
 public boolean hasTransition(String eventCode) {
  return transitions.containsKey(eventCode);
 }
 public State targetState(String eventCode) {
  return transitions.get(eventCode).getTarget();
 }
 public void executeActions(CommandChannel commandsChannel) {
  for (Command c : actions) commandsChannel.send(c.getCode());
 }

class StateMachine...
 public boolean isResetEvent(String eventCode) {
  return resetEventCodes().contains(eventCode);
 }

 private List<String> resetEventCodes() {
  List<String> result = new ArrayList<String>();
  for (Event e : resetEvents) result.add(e.getCode());
  return result;
 }

对于未在状态上注册的事件,它会直接忽略。对于可识别的任何事件,它就会转换为目标状态,并执行这个目标状态上定义 的命令。

相关文章
|
机器学习/深度学习 存储 算法
时序数据特征工程浅析
内容摘要特征工程是指将原始数据标记处理为价值密度更高,更容易解释目标问题的工程化过程,在面向大量原始采集的数据集统计分析,尤其是对于高通量持续采集、且价值密度较低的时序数据更是如此。时序数据特征工程则是指利用有效方法,将原始时序数据转化为带有含义分类标签的序列数据片段或特征数值,例如,我们可以将指定时间窗口序列数据标识为特定异常关联数据,并保留平均、最大、最小值作为该序列的特征值。这样我们就可以围
4604 0
时序数据特征工程浅析
|
机器学习/深度学习 人工智能 自然语言处理
一文搞懂【知识蒸馏】【Knowledge Distillation】算法原理
一文搞懂【知识蒸馏】【Knowledge Distillation】算法原理
一文搞懂【知识蒸馏】【Knowledge Distillation】算法原理
|
安全 Java 调度
【Queue队列数据结构及其应用】
【Queue队列数据结构及其应用】
229 0
|
8月前
|
存储 安全 API
NAKIVO Backup & Replication 11.0.4 发布,现已支持 vSphere 9.0
NAKIVO Backup & Replication 11.0.4 发布,现已支持 vSphere 9.0
136 3
NAKIVO Backup & Replication 11.0.4 发布,现已支持 vSphere 9.0
|
数据可视化 JavaScript API
NGLView 安装与配置-交互式分子结构和轨迹查看
NGLView 安装与配置-交互式分子结构和轨迹查看
1093 0
NGLView 安装与配置-交互式分子结构和轨迹查看
|
存储 算法 物联网
RFID室内人员定位管理
RFID室内人员定位管理系统通过射频信号实现人员位置的精确追踪。为人员配备带有唯一识别码的RFID标签(如卡片、腕带),室内安装多个阅读器与标签通信,读取信息。系统基于RSSI、TOA等算法计算标签位置,并在电子地图上显示实时动态。管理软件可分析人员轨迹、停留时间等数据,助力高效调度与管理,广泛适用于复杂室内场景。
|
9月前
|
机器学习/深度学习 算法 数据可视化
近端策略优化算法PPO的核心概念和PyTorch实现详解
本文深入解析了近端策略优化(PPO)算法的核心原理,并基于PyTorch框架实现了完整的强化学习训练流程。通过Lunar Lander环境展示了算法的全过程,涵盖环境交互、优势函数计算、策略更新等关键模块。内容理论与实践结合,适合希望掌握PPO算法及其实现的读者。
1464 2
近端策略优化算法PPO的核心概念和PyTorch实现详解
|
12月前
|
存储 JSON 数据格式
什么情况,一夜之间冲上热搜,狂揽29.6k星,再见吧SQLite!这个嵌入式分析引擎实在太香了
DuckDB是一款嵌入式OLAP数据库,专为高效分析型查询设计,被誉为“分析型SQLite”。它采用列式存储和向量化查询引擎,显著提升分析任务性能。无需独立服务器,支持Python、R、Java等语言,安装简单,5分钟即可上手。DuckDB可直接查询CSV、JSON、Parquet文件,支持Pandas零拷贝交互,优化SQL语法简化复杂查询。适用于探索性数据分析、数据湖ETL流水线及边缘设备实时分析等场景,是数据科学家和开发者的理想工具。项目地址:https://github.com/duckdb/duckdb
1341 4
|
8月前
|
机器学习/深度学习 边缘计算 算法
虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型(simulink仿真实现)
虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型(simulink仿真实现)
735 0
|
边缘计算 文字识别 自然语言处理
当OCR遇见大语言模型:智能文本处理的进化之路
简介:本文探讨光学字符识别(OCR)技术与大语言模型(LLM)结合带来的革新。传统OCR在处理模糊文本、复杂排版时存在局限,而LLM的语义理解、结构解析和多模态处理能力恰好弥补这些不足。文中通过代码实例展示了两者融合在错误校正、文档解析、多语言处理、语义检索及流程革新上的五大优势,并以财务报表解析为例,说明了该技术组合在实际应用中的高效性。此外,文章也展望了未来的技术发展趋势,包括多模态架构、小样本学习和边缘计算部署等方向,预示着文本处理技术正迈向智能认知的新时代。(240字)
1041 1