893
iPhone_iPad_Mac_apple
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;
|
3 |
import akka.actor.ActorRef;
|
01 |
public abstract class MyFSMBase extends UntypedActor {
|
04 |
* This is the mutable state of this state machine.
|
06 |
protected enum State {
|
10 |
private State state = State.IDLE;
|
11 |
private ActorRef target;
|
12 |
private List<Object> queue;
|
15 |
* Then come all the mutator methods:
|
17 |
protected void init(ActorRef target) {
|
19 |
queue = new ArrayList<Object>();
|
22 |
protected void setState(State s) {
|
29 |
protected void enqueue(Object o) {
|
34 |
protected List<Object> drainQueue() {
|
35 |
final List<Object> q = queue;
|
37 |
throw new IllegalStateException("drainQueue(): not yet initialized");
|
38 |
queue = new ArrayList<Object>();
|
43 |
* Here are the interrogation methods:
|
45 |
protected boolean isInitialized() {
|
46 |
return target != null;
|
49 |
protected State getState() {
|
53 |
protected ActorRef getTarget() {
|
55 |
throw new IllegalStateException("getTarget(): not yet initialized");
|
60 |
* And finally the callbacks (only one in this example: react to state change)
|
62 |
abstract protected void transition(State old, State next);
|
上麵方法好處是狀態改變可以表現在一個中心位置之上,當添加到FSM機器時,這使得它不可能忘記插入代碼對於狀態轉變的反應。
消息集束器的例子
上麵顯示的基類被設計支持一個類似例子作為Scala FSM 文檔:一個接收和排隊消息的角色,分批交付給一個可配置的目標角色。涉及的消息是:
01 |
public final class SetTarget {
|
04 |
public SetTarget(ActorRef ref) {
|
09 |
public final class Queue {
|
12 |
public Queue(Object o) {
|
17 |
public final Object flush = new Object();
|
19 |
public final class Batch {
|
20 |
final List<Object> objects;
|
22 |
public Batch(List<Object> objects) {
|
23 |
this .objects = objects;
|
該角色僅有兩個狀態 IDLE 和 ACTIVE,使他們處理相當直接在來自於基類的具體角色。
1 |
import akka.event.LoggingAdapter;
|
2 |
import akka.event.Logging;
|
3 |
import akka.actor.UntypedActor;
|
01 |
public class MyFSM extends MyFSMBase {
|
03 |
private final LoggingAdapter log =
|
04 |
Logging.getLogger(getContext().system(), this );
|
07 |
public void onReceive(Object o) {
|
09 |
if (getState() == State.IDLE) {
|
11 |
if (o instanceof SetTarget)
|
12 |
init(((SetTarget) o).ref);
|
17 |
} else if (getState() == State.ACTIVE) {
|
28 |
public void transition(State old, State next) {
|
29 |
if (old == State.ACTIVE) {
|
30 |
getTarget().tell( new Batch(drainQueue()), getSelf());
|
34 |
private void whenUnhandled(Object o) {
|
35 |
if (o instanceof Queue && isInitialized()) {
|
36 |
enqueue(((Queue) o).o);
|
37 |
setState(State.ACTIVE);
|
40 |
log.warning( "received unknown message {} in state {}" , o, getState());
|
這裏技巧是分解出像whenUnhandled 與transition 這樣的基本功能,以便獲得一些定義良好方麵在對於改變或插入記錄做出的反應。
狀態中心 VS 事件中心
在上麵例子中,狀態和事件的主觀複雜性是大致相等的,使得它看起來是是否選擇主派發的問題;在這個例子中基於狀態的派發器被選中。取決於如何均衡構建狀態與事件可行模型,首先處理不同事件,接著辨別狀態,這中做法可能是更實際。一個例子是一個狀態機,它具有多種內部狀態,但隻處理很少不同事件。
最後更新:2017-05-23 11:31:49