簡體   English   中英

Akka Java FSM 示例

[英]Akka Java FSM by Example

請注意:我是一名 Java 開發人員,沒有 Scala 的工作知識(很遺憾)。 我會要求答案中提供的任何代碼示例都將使用 Akka 的 Java API。

我正在嘗試使用 Akka FSM API 對以下超簡單狀態機進行建模。 實際上,我的機器要復雜得多,但這個問題的答案將使我能夠推斷出我的實際 FSM。

在此處輸入圖像描述

所以我有兩種狀態: OffOn 您可以通過調用SomeObject#powerOn(<someArguments>)打開機器電源來Off -> On 您可以通過調用SomeObject#powerOff(<someArguments>)關閉機器電源,從On -> Off開始。

我想知道為了實現這個 FSM,我需要哪些演員和支持類。 相信代表 FSM 的演員必須擴展AbstractFSM 但是什么類代表這兩個州呢? 哪些代碼公開並實現了powerOn(...)powerOff(...)狀態轉換? 一個有效的 Java 示例,甚至只是 Java 偽代碼,對我來說會有很長的路要走。

我認為我們可以比 FSM 文檔 ( http://doc.akka.io/docs/akka/snapshot/java/lambda-fsm.html ) 中的 copypasta 做得更好。 首先,讓我們稍微探索一下您的用例。

您有兩個觸發器(或事件或信號)——powerOn 和 powerOff。 您希望將這些信號發送到 Actor 並讓它改變狀態,其中兩個有意義的狀態是 On 和 Off。

現在,嚴格來說,FSM 需要一個額外的組件:您希望在過渡時采取的行動。

FSM:
State (S) x Event (E) -> Action (A), State (S')

Read: "When in state S, if signal E is received, produce action A and advance to state S'"

不需要一個動作,但是一個 Actor 不能被直接檢查,也不能被直接修改。 所有的變異和確認都是通過異步消息傳遞發生的。

在您的示例中,它沒有提供在轉換時執行的操作,您基本上有一個無操作的狀態機。 動作發生,狀態轉換沒有副作用,狀態是不可見的,所以一台工作的機器和一台壞掉的機器是一樣的。 而且由於這一切都是異步發生的,所以您甚至不知道損壞的東西何時完成。

因此,請允許我稍微擴展您的合同,並在您的 FSM 定義中包含以下操作:

 When in Off, if powerOn is received, advance state to On and respond to the caller with the new state
 When in On, if powerOff is received, advance state to Off and respond to the caller with the new state

現在我們也許能夠構建一個實際上可測試的 FSM。

讓我們為您的兩個信號定義一對類。 (AbstractFSM DSL 期望在類上匹配):

public static class PowerOn {}
public static class PowerOff {}

讓我們為您的兩個狀態定義一對枚舉:

 enum LightswitchState { on, off }

讓我們定義一個 AbstractFSM Actor ( http://doc.akka.io/japi/akka/2.3.8/akka/actor/AbstractFSM.html )。 擴展 AbstractFSM 允許我們使用與上述類似的 FSM 定義鏈來定義參與者,而不是直接在 onReceive() 方法中定義消息行為。 它為這些定義提供了一個不錯的小 DSL,並且(有點奇怪)期望在靜態初始化程序中設置這些定義。

不過,快速繞道:AbstractFSM 定義了兩個泛型,用於提供編譯時類型檢查。

S 是我們希望使用的 State 類型的基礎,D 是 Data 類型的基礎。 如果您正在構建一個將保存和修改數據的 FSM(可能是您的電燈開關的功率計?),您將構建一個單獨的類來保存這些數據,而不是嘗試向 AbstractFSM 的子類中添加新成員。 由於我們沒有數據,讓我們定義一個虛擬類,這樣您就可以看到它是如何傳遞的:

public static class NoDataItsJustALightswitch {}

所以,有了這個,我們可以構建我們的actor類。

public class Lightswitch extends AbstractFSM<LightswitchState, NoDataItsJustALightswitch> {
    {  //static initializer
        startWith(off, new NoDataItsJustALightswitch()); //okay, we're saying that when a new Lightswitch is born, it'll be in the off state and have a new NoDataItsJustALightswitch() object as data

        //our first FSM definition
        when(off,                                //when in off,
            matchEvent(PowerOn.class,            //if we receive a PowerOn message,
                NoDataItsJustALightswitch.class, //and have data of this type,
                (powerOn, noData) ->             //we'll handle it using this function:
                    goTo(on)                     //go to the on state,
                        .replying(on);           //and reply to the sender that we went to the on state
            )
        );

        //our second FSM definition
        when(on, 
            matchEvent(PowerOff.class, 
                NoDataItsJustALightswitch.class, 
                (powerOn, noData) -> {
                    goTo(off)
                        .replying(off);
                    //here you could use multiline functions,
                    //and use the contents of the event (powerOn) or data (noData) to make decisions, alter content of the state, etc.
                }
            )
        );

        initialize(); //boilerplate
    }
}

我敢肯定你想知道:我該如何使用它?! 因此,讓我們使用直接的 JUnit 和適用於 java 的 Akka Testkit 為您制作一個測試工具:

public class LightswitchTest { 
    @Test public void testLightswitch() {
        ActorSystem system = ActorSystem.create("lightswitchtest");//should make this static if you're going to test a lot of things, actor systems are a bit expensive
        new JavaTestKit(system) {{ //there's that static initializer again
            ActorRef lightswitch = system.actorOf(Props.create(Lightswitch.class)); //here is our lightswitch. It's an actor ref, a reference to an actor that will be created on 
                                                                                    //our behalf of type Lightswitch. We can't, as mentioned earlier, actually touch the instance 
                                                                                    //of Lightswitch, but we can send messages to it via this reference.

            lightswitch.tell(    //using the reference to our actor, tell it
                new PowerOn(),   //to "Power On," using our message type
                getRef());       //and giving it an actor to call back (in this case, the JavaTestKit itself)

            //because it is asynchronous, the tell will return immediately. Somewhere off in the distance, on another thread, our lightbulb is receiving its message

            expectMsgEquals(LightswitchState.on);   //we block until the lightbulb sends us back a message with its current state ("on.")
                                                     //If our actor is broken, this call will timeout and fail.

            lightswitch.tell(new PowerOff(), getRef());

            expectMsgEquals(LightswitchState.off);   

            system.stop(lightswitch); //switch works, kill the instance, leave the system up for further use
        }};
    }
}

你就在那里:一個 FSM 電燈開關。 老實說,這個微不足道的例子並沒有真正展示 FSM 的力量,因為一個無數據的例子可以作為一組“成為/不成為”的行為,在沒有泛型或 lambda 的情況下在一半的 LoC 中執行。 更具可讀性的 IMO。

PS考慮學習Scala,如果只是為了能夠閱讀其他人的代碼! Atomic Sc​​ala 一書的前半部分可在線免費獲得。

PPS 如果您真正想要的是一個可組合的狀態機,我維護Pulleys ,這是一個基於純 Java 狀態圖的狀態機引擎。 它已經持續了好幾年(很多 XML 和舊模式,沒有 DI 集成),但是如果您真的想將狀態機的實現與輸入和輸出分離,那么可能會有一些靈感。

我知道 Scala 中的 Actors。
此 Java 開始代碼可以幫助您繼續:

是的,從AbstractFSM擴展您的SimpleFSM
State 是AbstractFSM中的一個enum
您的<someArguments>可以是AbstractFSM中的Data部分
您的powerOnpowerOff是 Actor 消息/事件。 並且狀態切換在transitions部分

// states
enum State {
  Off, On
}

enum Uninitialized implements Data {
  Uninitialized
}

public class SimpleFSM extends AbstractFSM<State, Data> {
    {
        // fsm body
        startWith(Off, Uninitialized);

        // transitions
        when(Off,
            matchEvent(... .class ...,
            (... Variable Names ...) ->
              goTo(On).using(...) ); // powerOn(<someArguments>)

        when(On,
            matchEvent(... .class ...,
            (... Variable Names ...) ->
              goTo(Off).using(...) ); // powerOff(<someArguments>)

        initialize();

    }
}

實際工作項目見

Scala 和 Java 8 以及用於 Akka AbstractFSM 的 Lambda 模板

好吧,這是一個非常老的問題,但是如果您受到 Google 的歡迎,但如果您仍然對使用 Akka 實現 FSM 感興趣,我建議您查看文檔的這一部分。

如果你想看看一個實用的模型驅動狀態機如何實現,可以查看我的blog1blog2

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM