2015-06-16 14 views
8

Lütfen dikkat: Scala'nın çalışma bilgisi olmayan bir Java geliştiricisiyim (üzgünüz). Cevapta verilen kod örneklerinin Akka'nın Java API'sini kullanmasını rica ediyorum.Akka Java FSM by Örnek

Aşağıdaki süper basit durum makinesini modellemek için Akka FSM API'sini kullanmaya çalışıyorum. Gerçekte, makinem çok daha karmaşıktır, ancak bu sorunun cevabı gerçek FSM'ime tahmin yapmamı sağlayacaktır. Off ve On:

enter image description here

Ve bu yüzden 2 devletleri var. Makineyi SomeObject#powerOn(<someArguments>) numaralı telefonu arayarak açarak Off -> On'a gidebilirsiniz. Makineyi SomeObject#powerOff(<someArguments>) numaralı telefonu arayarak gç vererek On -> Off'dan gidebilirsiniz.

Bu FSM'yi uygulamak için hangi oyunculara ve destekleyici sınıflara ihtiyacım olacağını merak ediyorum. I , FSM'yi temsil eden imgesinin AbstractFSM'u uzatması gerektiğine inanıyor. Ama hangi sınıflar 2 devleti temsil ediyor? Hangi kod powerOn(...) ve powerOff(...) durum geçişlerini sergiler ve uygular? Çalışan bir Java örneği, ya da sadece Java sözde kodu, benim için burada uzun bir yol giderdi.

cevap

17

bkz geçişler Bölüm içindedir. İlk olarak, kullanım durumunuzu biraz inceleyelim.

İki tetikleyiciniz (veya olaylar veya sinyaller) var - powerOn ve powerOff. Bu sinyalleri bir Aktör'e göndermek ve iki anlamlı durumun Açık ve Kapalı olduğu durumu değiştirmesini istiyorsunuz.

Şimdi, kesinlikle bir FSM'nin bir ek bileşene gereksinim duyması: geçişi gerçekleştirmek istediğiniz eylem.

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'" 

Sen İHTİYACINIZ bir eylem, ancak bir Aktör direkt muayene, ne de doğrudan değiştirilemez. Tüm mutasyon ve bildirim asenkron mesajın iletilmesiyle gerçekleşir.

Geçişte gerçekleştirilecek eylemi sağlamayan örneğinizde, temel olarak no-op olan bir durum makinesine sahipsiniz. Eylemler meydana gelir, yan etki olmadan durum geçişleri ve bu durum görünmezdir, bu yüzden çalışan bir makine kırılmış olanla aynıdır.Ve her şey eşzamanlı olarak gerçekleşmediğinden, kırık şeyin ne zaman biteceğini bile bilmiyorsunuz.

Yani bana sözleşme biraz genişletmek için izin, ve FSM tanımlarında aşağıdaki eylemleri içerir:
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 

Şimdi biz aslında test edilebilir bir FSM inşa etmek mümkün olabilir.

İki sinyaliniz için bir çift sınıf tanımlayalım. (AbstractFSM DSL sınıfına maç bekliyor):

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

senin iki devlet için çeteleler bir çift tanımlayalım:
enum LightswitchState { on, off } 

en bir AbstractFSM Erkek Oyuncu ( http://doc.akka.io/japi/akka/2.3.8/akka/actor/AbstractFSM.html) tanımlayalım. AbstractFSM'yi genişletmek, doğrudan bir onReceive() yönteminde ileti davranışını tanımlamak yerine, yukarıdakilere benzer bir FSM tanımları zincirini kullanarak bir oyuncu tanımlamamıza olanak tanır. Bu tanımlamalar için güzel bir küçük DSL sunar ve (biraz garip), tanımların statik bir başlatıcıda kurulmasını bekler.

Yine de bir hızlı sapma: AbstractFSM, derleme zaman türü denetimi sağlamak için kullanılan iki genel tanıma sahiptir.

S, kullanmak istediğimiz Durum türlerinin temelini oluşturur ve D, Veri türlerinin tabanını oluşturur. Verileri tutacak ve değiştirecek bir FSM oluşturuyorsanız (belki ışık anahtarınız için bir güç ölçer?), Bu verileri tutmanız için yeni bir üye eklemeye çalışmak yerine soyut bir sınıf oluşturursunuz. hiçbir veriye sahip olduğundan, etrafında iletilir nasıl görebilirsiniz sadece bu yüzden en sahte sınıfını tanımlayalım: bununla

public static class NoDataItsJustALightswitch {} 

Ve böylece, yoldan, bizim aktör sınıfını oluşturabilirsiniz.

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 
    } 
} 

Eminim merak ediyorum: Bunu nasıl kullanırım ?!

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 
     }}; 
    } 
} 

Ve orada mısın: Bir FSM lightswitch Yani Diyelim ki java için düz JUnit ve Akka test kiti kullanarak bir test koşum yapalım. Dürüst olmak gerekirse, bu önemsiz bir örnek, FSM'lerin gücünü göstermez, çünkü veri-içermeyen bir örnek, jenerik veya lambdası olmayan bir çok LoC'un yarısı gibi bir “davranış/karşıtlık” davranışı olarak gerçekleştirilebilir. Çok daha fazla okunabilir IMO.

PS, yalnızca diğer insanların kodunu okuyabiliyorsa Scala'yı kullanmayı düşünün! Atomic Scala kitabının ilk yarısı ücretsiz çevrimiçi olarak mevcuttur.

Eğer gerçekten istediğiniz her şey bir composable durum makinesi PPS, ben saf java statecharse dayalı bir devlet makine motoru Pulleys, korumak. Yıllar içinde sürüyor (XML ve eski kalıpların çoğu, DI entegrasyonu yok) ama eğer bir giriş makinesinin giriş ve çıkışlardan gerçeklenmesini gerçekten deşifre etmek istiyorsanız, orada biraz ilham olabilir.

+0

Çok teşekkür ederim, ben bir java örneği bulmak için tam olarak aynı durumdaydı ve bu tam olarak ne aradım –

+0

Ben bahsettiğiniz github projeyi bulamadık, bir bağlantı gönderebilir miyim? –

+0

http://github.com/datamill-io/pulleys - şimdi 3 yaş daha büyük ve daha az arzu edilir. Scala 2.12'de yazılmış, muhtemelen Aktivitelerin (asenkron olarak tetiklenen eylemler) ve Submachines'e odaklanan yeni bir versiyon tasarlıyorum. –

2

Scala'daki Aktörler hakkında biliyorum.
Bu Java Başlangıç ​​Kodu devam etmeye, size yardımcı olabilir:

Evet, AbstractFSM adresinin SimpleFSM uzanır.
Durum, AbstractFSM numaralı telefondan enum'dur.
Sizin <someArguments> AbstractFSM
Sizin powerOn ve powerOff Aktör Mesajlar/Etkinlikler olan senin içinde Data Bölüm olabilir. Ve Devlet anahtarlama

// 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(); 

    } 
} 

Gerçek çalışma Projesi Sanırım FSM docs (http://doc.akka.io/docs/akka/snapshot/java/lambda-fsm.html) den copypasta biraz daha iyi yapabileceğini düşünüyorum

Scala and Java 8 with Lambda Template for a Akka AbstractFSM

+0

Teşekkürler @Robert Halter (+1) - bu harika bir başlangıçtır, ancak maalesef hala * bunu * anlayamıyorum. Ödül için, birkaç takipçiye cevap verebilir misin? (1) Tüm elipslerinizi ('...') gerçek kod/API çağrılarıyla değiştirebilir misiniz? Bu gerçekten benim için her şeyi bir arada tutmaya yardımcı olur! (2) Lütfen “Data” ve “Uninitialized” amaçlarının (sadece javadocs/yorumları ekleyerek bile) açıklayabilir misiniz? İşe yaramaz ve gereksiz görünüyorlar! – smeeb

+0

Son olarak (3) yukarıdaki 'SimpleFSM', kafa karıştırıcıdır, çünkü tüm kodları etiketlenmemiş bir kod bloğundadır (' {...} '). Bu kodu dışarıdan nasıl arayabilirim? Bu etiketlenmemiş kod bloğunu "normal" Java yöntemlerine yeniden yerleştirebilir misiniz? (Böylece sınıfın dışından * nasıl arayacağımı anlayabilirim)? Tekrar çok teşekkürler! – smeeb

+1

@smeeb powerOn ve powerOff çağrılarında öğesinin arkasındaki amacınız nedir? –