AKKA文档(Java版)—建立有限状态机角色

简介:

建立有限状态机角色

概述

有限状态机模式在Erlang design principles里面被很好描述出来.简而言之,它可以被视为一组关系:
State(S) x Event(E) -> Actions (A), State(S’)

这些关系描述为:

如果我们在状态S 和 时间E 发生,我们应该执行动作A 与转换到状态S’.

而Scala 程序语言使构建一个良好内部DSL(领域特定语言)成为可能,后者用于规划有限状态机(请见FSM)。对于用同样方法,由于Java语法冗长不适合构建。本章节介绍通过自身训练效实现相同关注点分离方法。

状态应该如何处理

FSM角色实现引用的所有可变字段(或可传递的可变数据结构)应该被收集在一个地方及仅通过使用小粒度的的定义良好的一组方法来改变。要做到这一点的一种实现方法是集合所有可变状态在一个超类中,并且保持其的私有及为改变其提供保护方法。

1 import java.util.ArrayList;
2 import java.util.List;
3 import akka.actor.ActorRef;

 

01 public abstract class MyFSMBase extends UntypedActor {
02  
03   /*
04    * This is the mutable state of this state machine.
05    */
06   protected enum State {
07     IDLE, ACTIVE;
08   }
09  
10   private State state = State.IDLE;
11   private ActorRef target;
12   private List<Object> queue;
13  
14   /*
15    * Then come all the mutator methods:
16    */
17   protected void init(ActorRef target) {
18     this.target = target;
19     queue = new ArrayList<Object>();
20   }
21  
22   protected void setState(State s) {
23     if (state != s) {
24       transition(state, s);
25       state = s;
26     }
27   }
28  
29   protected void enqueue(Object o) {
30     if (queue != null)
31       queue.add(o);
32   }
33  
34   protected List<Object> drainQueue() {
35     final List<Object> q = queue;
36     if (q == null)
37       throw new IllegalStateException("drainQueue(): not yet initialized");
38     queue = new ArrayList<Object>();
39     return q;
40   }
41  
42   /*
43    * Here are the interrogation methods:
44    */
45   protected boolean isInitialized() {
46     return target != null;
47   }
48  
49   protected State getState() {
50     return state;
51   }
52  
53   protected ActorRef getTarget() {
54     if (target == null)
55       throw new IllegalStateException("getTarget(): not yet initialized");
56     return target;
57   }
58  
59   /*
60    * And finally the callbacks (only one in this example: react to state change)
61    */
62   abstract protected void transition(State old, State next);
63 }

上面方法好处是状态改变可以表现在一个中心位置之上,当添加到FSM机器时,这使得它不可能忘记插入代码对于状态转变的反应。

消息集束器的例子

上面显示的基类被设计支持一个类似例子作为Scala FSM 文档:一个接收和排队消息的角色,分批交付给一个可配置的目标角色。涉及的消息是:

01 public final class SetTarget {
02   final ActorRef ref;
03  
04   public SetTarget(ActorRef ref) {
05     this.ref = ref;
06   }
07 }
08  
09 public final class Queue {
10   final Object o;
11  
12   public Queue(Object o) {
13     this.o = o;
14   }
15 }
16  
17 public final Object flush = new Object();
18  
19 public final class Batch {
20   final List<Object> objects;
21  
22   public Batch(List<Object> objects) {
23     this.objects = objects;
24   }
25 }

该角色仅有两个状态 IDLE 和 ACTIVE,使他们处理相当直接在来自于基类的具体角色。

1 import akka.event.LoggingAdapter;
2 import akka.event.Logging;
3 import akka.actor.UntypedActor;

 

01 public class MyFSM extends MyFSMBase {
02  
03   private final LoggingAdapter log =
04     Logging.getLogger(getContext().system(), this);
05  
06   @Override
07   public void onReceive(Object o) {
08  
09     if (getState() == State.IDLE) {
10  
11       if (o instanceof SetTarget)
12         init(((SetTarget) o).ref);
13  
14       else
15         whenUnhandled(o);
16  
17     else if (getState() == State.ACTIVE) {
18  
19       if (o == flush)
20         setState(State.IDLE);
21  
22       else
23         whenUnhandled(o);
24     }
25   }
26  
27   @Override
28   public void transition(State old, State next) {
29     if (old == State.ACTIVE) {
30       getTarget().tell(new Batch(drainQueue()), getSelf());
31     }
32   }
33  
34   private void whenUnhandled(Object o) {
35     if (o instanceof Queue && isInitialized()) {
36       enqueue(((Queue) o).o);
37       setState(State.ACTIVE);
38  
39     else {
40       log.warning("received unknown message {} in state {}", o, getState());
41     }
42   }
43 }

这里技巧是分解出像whenUnhandled 与transition 这样的基本功能,以便获得一些定义良好方面在对于改变或插入记录做出的反应。

状态中心 VS 事件中心

在上面例子中,状态和事件的主观复杂性是大致相等的,使得它看起来是是否选择主派发的问题;在这个例子中基于状态的派发器被选中。取决于如何均衡构建状态与事件可行模型,首先处理不同事件,接着辨别状态,这中做法可能是更实际。一个例子是一个状态机,它具有多种内部状态,但只处理很少不同事件。

 

相关文章
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
JavaScript Java 测试技术
基于Java+SpringBoot+Vue实现的车辆充电桩系统设计与实现(系统源码+文档+部署讲解等)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
|
Java API Apache
Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
【10月更文挑战第29天】Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
1298 5
|
Java Linux
java读取linux服务器下某文档的内容
java读取linux服务器下某文档的内容
214 3
java读取linux服务器下某文档的内容
|
Java Apache Maven
Java中使用poi+poi-tl实现根据模板导出word文档
这个过程不仅简化了文档生成的工作,而且保证了生成文档的一致性与准确性,特别适合于那些需要生成大量文档的自动化场景。通过以上步骤,Java开发人员可以实现高效、可靠的Word文档导出功能。
4653 0
|
Java
Java计算机IT编程文档常见单词翻译(四)
Java计算机IT编程文档常见单词翻译(四)
257 0
Java计算机IT编程文档常见单词翻译(四)
|
Java
Java计算机IT编程文档常见单词翻译(三)
Java计算机IT编程文档常见单词翻译(三)
257 0
Java计算机IT编程文档常见单词翻译(三)
|
Java
Java计算机IT编程文档常见单词翻译(二)
Java计算机IT编程文档常见单词翻译(二)
234 0
Java计算机IT编程文档常见单词翻译(二)
|
Java 网络架构
Java计算机IT编程文档常见单词翻译(一)
Java计算机IT编程文档常见单词翻译(一)
354 0
Java计算机IT编程文档常见单词翻译(一)
|
10月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
441 1