https://github.com/dotnet-state-machine/stateless
Stateless (Durum Bilgisiz / Durumsuz )
Doğrudan .NET kodunda durum makineleri ve hafif durum tabanlı makine tabanlı iş akışları oluşturun:
var phoneCall = new StateMachine<State, Trigger>(State.OffHook);
phoneCall.Configure(State.OffHook)
.Permit(Trigger.CallDialled, State.Ringing);
phoneCall.Configure(State.Ringing)
.Permit(Trigger.CallConnected, State.Connected);
phoneCall.Configure(State.Connected)
.OnEntry(() => StartCallTimer())
.OnExit(() => StopCallTimer())
.Permit(Trigger.LeftMessage, State.OffHook)
.Permit(Trigger.PlacedOnHold, State.OnHold);
// ...
phoneCall.Fire(Trigger.CallDialled);
Assert.AreEqual(State.Ringing, phoneCall.State);
Yukarıdaki projenin yanı sıra bu proje de
Basit Durum Makinesi'nden esinlenmiştir.
Özellikler
Çoğu standart durum makinesi yapıları desteklenir:
- Herhangi bir .NET türünün durumları ve tetikleyicileri için genel destek (sayılar, dizeler, numaralar vb.)
- Hiyerarşik durumlar
- Durumlar için giriş / çıkış olayları
- Koşullu geçişleri desteklemek için koruma maddeleri
- İçgözlem
Bazı faydalı uzantılar da sağlanmıştır:
- Durumu harici olarak depolayabilme (örneğin, bir ORM tarafından izlenen bir özellik)
- Parametreli tetikleyiciler
- Yeniden girişli durum
- DOT grafiğine aktarma
Hiyerarşik Durumlar
Aşağıdaki örnekte, OnHold durumu, Connected durumun bir alt-durumudur. Bu, bir OnHold çağrısının hala bağlı olduğu anlamına gelir.
phoneCall.Configure(State.OnHold)
.SubstateOf(State.Connected)
.Permit(Trigger.TakenOffHold, State.Connected)
.Permit(Trigger.PhoneHurledAgainstWall, State.PhoneDestroyed);
StateMachine.State özelliğine ek olarak, kesin mevcut durumu bildirecek olan bir
IsInState (State) yöntemi sağlanmıştır.
IsInState (State) alt durumları dikkate alacaktır, böylece yukarıdaki örnek
OnHold durumundaysa,
IsInState (State.Connected) da
true olarak değerlendirilecektir.
Durumlar İçin Giriş/Çıkış Olayları
Örnekte, bir çağrı bağlandığında StartCallTimer () metotu çalıştırılacaktır. Çağrı tamamlandığında StopCallTimer () çalıştırılacaktır.
Çağrı,
OnHold durumunun
Connected durumun bir durumu olduğu için tekrar tekrar çağrılan
StartCallTimer () ve
StopCallTimer () metotları kullanılmadan
Connected ve
OnHold durumları arasında hareket edebilir.
Giriş / Çıkış olay işleyicileri, tetikleyici, kaynak ve hedef durumları açıklayan bir
Transition türü parametresi ile birlikte sağlanabilir.
Dış Durum Depolama
Durum Bilgisiz, çeşitli uygulama modellerine gömülecek şekilde tasarlanmıştır. Örneğin, bazı ORM'ler eşlenmiş verilerin nerede depolanabileceğine dair gereksinimleri yerine getirir ve UI çerçeveler genellikle özel "bağlanabilir" özelliklerinde depolanmak üzere durum gerektirir.Bu amaçla, StateMachine yapıcısı, durum değerlerini okumak ve yazmak için kullanılacak işlev argümanlarını kabul edebilir:
var stateMachine = new StateMachine<State, Trigger>(
() => myState.Value,
s => myState.Value = s);
Bu örnekte durum makinesi, durum depolama için myState nesnesini kullanacaktır.
Başka bir örnek, örnek klasörde bulunan
JsonExample çözümünde bulunabilir.
İçgözlem
Durum makinesi, tetikleyicilerin bir listesini, StateMachine.PermittedTriggers özelliği aracılığıyla geçerli durumda başarılı bir şekilde tetiklenebilen bir liste sağlayabilir. Durum yapılandırması hakkında bilgi almak için StateMachine.GetInfo () metotudunu kullanın.
Güvenlik maddeleri
Durum makinesi, koruma maddelerine dayalı çoklu geçişler arasında seçim yapar;
phoneCall.Configure(State.OffHook)
.PermitIf(Trigger.CallDialled, State.Ringing, () => IsValidNumber)
.PermitIf(Trigger.CallDialled, State.Beeping, () => !IsValidNumber);
Bir durum içindeki güvenlik maddelerinin karşılıklı olarak özel olması şarttır (aynı anda birden fazla güvenlik maddesi geçerli olamaz). Alt durumlar bunları tekrar belirterek geçişleri geçersiz kılar, ancak alt durumlar, üst-durumlar (superstate) tarafından izin verilerin geçişlere izin veremez.
Bir tetikleyici tetiklendiğinde koruma maddeleri değerlendirilecektir. Güvenlikler, yan etkisiz hale getirilmelidir.
Parametreli tetikleyiciler
Güçlü yazılan(Strongly-typed) parametreler tetikleyicilere atanabilir:
var assignTrigger = stateMachine.SetTriggerParameters<string>(Trigger.Assign);
stateMachine.Configure(State.Assigned)
.OnEntryFrom(assignTrigger, email => OnAssigned(email));
stateMachine.Fire(assignTrigger, "joe@example.com");
Tetik parametreleri,
PermitDynamic () yapılandırma metodunu kullanarak hedef durumu dinamik olarak seçmek için kullanılabilir.
Gözardı Edilen Geçişler ve Yeniden Girişli Durumlar
Bununla ilişkili izin verilen bir geçişi olmayan bir tetikleyici tetiklemek, bir istisnanın atılmasına neden olur.
Belirli durumlar içinde tetikleyicileri yok saymak için
Ignore(TTrigger) direktifini kullanın:
phoneCall.Configure(State.Connected)
.Ignore(Trigger.CallDialled);
Alternatif olarak, bir durum, yeniden girişli olarak işaretlenebilir, böylece giriş ve çıkış olayları, kendisinden / kendisinden geçerken bile tetiklenecektir:
stateMachine.Configure(State.Assigned)
.PermitReentry(Trigger.Assigned)
.OnEntry(() => SendEmailToAssignee());
Varsayılan olarak, tetikleyiciler açıkça göz ardı edilmelidir. İşlenmemiş bir tetikleyici tetiklendiğinde, Durumsuz'un varsayılan bir istisna fırlatma davranışını geçersiz kılmak için,
OnUnhandledTrigger yöntemini kullanarak durum makinesini yapılandırın:
stateMachine.OnUnhandledTrigger((state, trigger) => { });
DOT grafiğine aktarma
Durum makinelerini çalışma zamanında görselleştirmek faydalı olabilir. Bu yaklaşımla, kod yetkili kaynak ve durum diyagramları her zaman güncel olan yan ürünlerdir.
phoneCall.Configure(State.OffHook)
.PermitIf(Trigger.CallDialled, State.Ringing, IsValidNumber);
string graph = UmlDotGraph.Format(phoneCall.GetInfo());
UmlDotGraph.Format () metodu,
DOT grafiği dilinde durum makinesinin dize temsilini döndürür, örn .:
digraph {
OffHook -> Ringing [label="CallDialled [IsValidNumber]"];
}
PDF dosyası oluşturmak için komut satırı örneği:
dot -T pdf -o phoneCall.pdf phoneCall.dot.
Asenkron Tetikleyiciler
Task<T> sağlayan platformlarda, StateMachine, async giriş/çıkış eylemlerini ve benzerlerini destekler:
stateMachine.Configure(State.Assigned)
.OnEntryAsync(async () => await SendEmailToAssignee());
Eşzamansız/Asenkron işleyiciler bu durumlarda
* Async () metodları kullanılarak kaydedilmelidir.
Eşzamansız/Asenkron eylemleri tetikleyen bir tetikleyiciyi çalıştırmak için,
FireAsync () meotdu kullanılmalıdır:
await stateMachine.FireAsync(Trigger.Assigned);
Not:
StateMachine eşzamansız olarak kullanılabilirken, tek iş parçacıklı olarak kalır ve aynı anda birden çok iş parçacığı tarafından
eşzamanlı kullanılamaz.
Yapı
Durumsuz, .NET 4.0 + 'da ve hemen hemen tüm modern .NET platformlarında .NET Standard 1.0'ı hedefleyerek çalışır. Visual Studio 2017, çözümü kurmak için gereklidir.
Proje hedefleri
Bu sayfa Durumsuz'un (Stateless) neredeyse tamamlanmış bir tanımlamasıdır ve açık amacı asgari düzeyde kalmaktır.