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.
Çok teşekkür ederim, ben bir java örneği bulmak için tam olarak aynı durumdaydı ve bu tam olarak ne aradım –
Ben bahsettiğiniz github projeyi bulamadık, bir bağlantı gönderebilir miyim? –
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. –