Memperbarui:
Sekali lagi terima kasih atas contohnya, mereka sangat membantu dan dengan yang berikut ini saya tidak bermaksud mengambil apa pun dari mereka.
Bukankah contoh yang diberikan saat ini, sejauh yang saya mengerti mereka & mesin negara, hanya setengah dari apa yang biasanya kita mengerti oleh mesin negara?
Dalam arti bahwa contoh tidak mengubah keadaan tapi itu hanya diwakili oleh mengubah nilai variabel (dan memungkinkan nilai yang berbeda - perubahan di berbagai negara), sementara biasanya mesin negara juga harus mengubah perilaku itu, dan perilaku tidak (hanya) di arti memungkinkan perubahan nilai yang berbeda untuk variabel tergantung pada negara, tetapi dalam arti memungkinkan metode yang berbeda dieksekusi untuk negara yang berbeda.
Atau apakah saya memiliki kesalahpahaman tentang mesin negara dan penggunaan umum mereka?
salam Hormat
Pertanyaan asli:
Saya menemukan diskusi ini tentang mesin negara & blok iterator di c # dan alat untuk membuat mesin negara dan apa yang tidak untuk C #, jadi saya menemukan banyak hal abstrak tetapi sebagai noob semua ini agak membingungkan.
Jadi akan lebih baik jika seseorang dapat memberikan contoh kode sumber C # yang mewujudkan mesin keadaan sederhana dengan mungkin 3,4 negara, hanya untuk mendapatkan intinya.
sumber
Jawaban:
Mari kita mulai dengan diagram keadaan sederhana ini:
Kita punya:
Anda dapat mengonversikan ini ke C # dalam beberapa cara, seperti melakukan pernyataan beralih pada status dan perintah saat ini, atau mencari transisi dalam tabel transisi. Untuk mesin keadaan sederhana ini, saya lebih suka tabel transisi, yang sangat mudah direpresentasikan menggunakan
Dictionary
:Sebagai pilihan pribadi, saya suka mendesain mesin negara saya dengan
GetNext
fungsi untuk mengembalikan negara berikutnya secara deterministik , danMoveNext
fungsi untuk bermutasi mesin negara.sumber
GetHashCode()
penggunaan bilangan prima yang benar.StateTransition
Kelas digunakan sebagai kunci dalam kamus dan persamaan kunci adalah penting. Dua contoh berbeda dariStateTransition
harus dianggap sama selama mereka mewakili transisi yang sama (misalnyaCurrentState
danCommand
sama). Untuk menerapkan kesetaraan, Anda harus menimpaEquals
jugaGetHashCode
. Khususnya kamus akan menggunakan kode hash dan dua objek yang sama harus mengembalikan kode hash yang sama. Anda juga mendapatkan kinerja yang baik jika tidak terlalu banyak objek yang tidak sama memiliki kode hash yang sama, itulah sebabnyaGetHashCode
diimplementasikan seperti yang ditunjukkan.Anda mungkin ingin menggunakan salah satu Mesin Finite State open source yang ada. Misalnya bbv.Common.StateMachine ditemukan di http://code.google.com/p/bbvcommon/wiki/StateMachine . Ini memiliki sintaks fasih yang sangat intuitif dan banyak fitur seperti, tindakan masuk / keluar, tindakan transisi, penjaga, hirarki, implementasi pasif (dijalankan pada utas penelepon) dan implementasi aktif (memiliki utas di mana fsm berjalan, acara ditambahkan ke antrian).
Contoh Juliet, definisi mesin negara menjadi sangat mudah:
Pembaruan : Lokasi proyek telah pindah ke: https://github.com/appccelerate/statemachine
sumber
Berikut adalah contoh dari mesin keadaan terbatas yang sangat klasik, pemodelan perangkat elektronik yang sangat sederhana (seperti TV)
sumber
private void DoNothing() {return;}
dan mengganti semua instance dari null denganthis.DoNothing
. Memiliki efek samping yang menyenangkan mengembalikan keadaan saat ini.States
toUnpowered, Standby, On
. Alasan saya adalah jika seseorang bertanya kepada saya di negara mana televisi saya berada, saya akan mengatakan "Tidak Aktif" dan bukan "Mulai". Saya juga berubahStandbyWhenOn
danStandbyWhenOff
keTurnOn
danTurnOff
. Itu membuat kode dibaca lebih intuitif, tetapi saya bertanya-tanya apakah ada konvensi atau faktor lain yang membuat terminologi saya kurang tepat.Beberapa promo diri yang tidak tahu malu di sini, tetapi beberapa waktu yang lalu saya membuat perpustakaan bernama YieldMachine yang memungkinkan mesin keadaan kompleksitas terbatas dijelaskan dengan cara yang sangat bersih dan sederhana. Misalnya, pertimbangkan lampu:
Perhatikan bahwa mesin status ini memiliki 2 pemicu dan 3 status. Dalam kode YieldMachine, kami menulis metode tunggal untuk semua perilaku yang terkait dengan keadaan, di mana kami melakukan kekejaman mengerikan menggunakan
goto
untuk setiap negara. Pemicu menjadi properti atau bidang tipeAction
, dihiasi dengan atribut yang disebutTrigger
. Saya telah mengomentari kode negara bagian pertama dan transisinya di bawah ini; negara bagian berikutnya mengikuti pola yang sama.Pendek dan bagus, eh!
Mesin negara ini dikendalikan hanya dengan mengirim pemicu ke sana:
Hanya untuk memperjelas, saya telah menambahkan beberapa komentar ke status pertama untuk membantu Anda memahami cara menggunakan ini.
Ini berfungsi karena kompiler C # benar-benar membuat mesin keadaan secara internal untuk setiap metode yang digunakan
yield return
. Konstruk ini biasanya digunakan untuk membuat urutan data dengan malas, tetapi dalam kasus ini kami sebenarnya tidak tertarik dengan urutan yang dikembalikan (yang semuanya adalah nol), tetapi dalam perilaku negara yang dibuat di bawah tenda.Kelas
StateMachine
dasar melakukan beberapa refleksi pada konstruksi untuk menetapkan kode untuk setiap[Trigger]
tindakan, yang mengaturTrigger
anggota dan menggerakkan mesin negara ke depan.Tetapi Anda tidak benar-benar perlu memahami internal untuk dapat menggunakannya.
sumber
goto
metode antara.goto
untuk beralih di antara metode? Saya tidak melihat bagaimana itu bisa berhasil. Tidak,goto
apakah bermasalah karena menghasilkan pemrograman prosedural (ini dengan sendirinya menyulitkan hal-hal baik seperti pengujian unit), mempromosikan pengulangan kode (memperhatikan bagaimanaInvalidTrigger
perlu dimasukkan untuk setiap negara?) Dan akhirnya membuat aliran program lebih sulit untuk diikuti. Bandingkan ini dengan (kebanyakan) solusi lain di utas ini dan Anda akan melihat bahwa ini adalah satu-satunya di mana seluruh FSM terjadi dalam satu metode tunggal. Itu biasanya cukup untuk menimbulkan kekhawatiran.goto
cukup baik.goto
untuk beralih antar fungsi, tetapi tidak mendukung fungsi? :) Anda benar, komentar "sulit untuk diikuti" lebih merupakangoto
masalah umum , memang tidak banyak masalah dalam kasus ini.Anda bisa membuat kode blok iterator yang memungkinkan Anda mengeksekusi blok kode dengan cara yang diatur. Bagaimana blok kode dihancurkan benar-benar tidak harus sesuai dengan apa pun, itu hanya bagaimana Anda ingin kode itu. Sebagai contoh:
Dalam hal ini, saat Anda memanggil CountToTen, belum ada yang benar-benar dijalankan. Apa yang Anda dapatkan secara efektif merupakan generator mesin negara, yang Anda dapat membuat mesin instance negara baru. Anda melakukan ini dengan memanggil GetEnumerator (). IEnumerator yang dihasilkan secara efektif mesin negara yang dapat Anda kendarai dengan memanggil MoveNext (...).
Jadi, dalam contoh ini, pertama kali Anda memanggil MoveNext (...) Anda akan melihat "1" ditulis ke konsol, dan saat berikutnya Anda memanggil MoveNext (...) Anda akan melihat 2, 3, 4, dan kemudian 5, 6, 7 dan kemudian 8, dan kemudian 9, 10. Seperti yang Anda lihat, ini adalah mekanisme yang berguna untuk mengatur bagaimana sesuatu harus terjadi.
sumber
Saya memposting jawaban lain di sini karena ini adalah mesin negara dari perspektif yang berbeda; sangat visual.
Jawaban asli saya adalah kode imperatif klasik. Saya pikir ini cukup visual sebagai kode karena array yang membuat memvisualisasikan mesin negara sederhana. Kelemahannya adalah Anda harus menulis semua ini. Jawaban Remos mengurangi upaya menulis kode boiler-plate tetapi jauh lebih sedikit visual. Ada alternatif ketiga; benar-benar menggambar mesin negara.
Jika Anda menggunakan .NET dan dapat menargetkan versi 4 waktu berjalan, maka Anda memiliki opsi untuk menggunakan aktivitas mesin keadaan kerja dari workflow . Ini pada dasarnya membiarkan Anda menggambar mesin negara (seperti dalam diagram Juliet ) dan memiliki WF run-time menjalankannya untuk Anda.
Lihat artikel MSDN Membangun Mesin Negara dengan Windows Workflow Foundation untuk lebih jelasnya, dan situs CodePlex ini untuk versi terbaru.
Itulah opsi yang selalu saya sukai ketika menargetkan .NET karena mudah dilihat, diubah, dan dijelaskan kepada yang bukan programmer; gambar bernilai ribuan kata seperti yang mereka katakan!
sumber
Penting untuk diingat bahwa mesin negara adalah abstraksi, dan Anda tidak memerlukan alat khusus untuk membuatnya, namun alat dapat berguna.
Misalnya Anda dapat mewujudkan mesin negara dengan fungsi:
Mesin ini akan berburu burung camar dan mencoba memukulnya dengan balon air. Jika meleset, ia akan mencoba menembakkannya hingga menyentuh (bisa dilakukan dengan harapan realistis;)), jika tidak, ia akan menertawakan konsol. Terus berburu sampai keluar dari camar untuk melecehkan.
Setiap fungsi sesuai dengan masing-masing negara; status awal dan akhir (atau terima ) tidak ditampilkan. Mungkin ada lebih banyak status di sana daripada yang dimodelkan oleh fungsi. Sebagai contoh setelah menembakkan balon mesin itu benar-benar dalam keadaan lain daripada sebelumnya, tapi saya memutuskan perbedaan ini tidak praktis untuk dibuat.
Cara yang umum adalah dengan menggunakan kelas untuk mewakili negara, dan kemudian menghubungkannya dengan cara yang berbeda.
sumber
Menemukan tutorial hebat ini online dan itu membantu saya membungkus kepala saya di sekitar mesin negara yang terbatas.
http://gamedevelopment.tutsplus.com/tutorials/finite-state-machines-theory-and-implementation--gamedev-11867
Tutorialnya adalah agnostik bahasa, sehingga dapat dengan mudah disesuaikan dengan kebutuhan C # Anda.
Juga, contoh yang digunakan (semut yang mencari makanan) mudah dimengerti.
Dari tutorial:
sumber
Hari ini saya jauh di Pola Desain Negara. Saya melakukan dan menguji ThreadState, yang sama (+/-) dengan Threading di C #, seperti yang dijelaskan dalam gambar dari Threading di C #
Anda dapat dengan mudah menambahkan status baru, mengkonfigurasi perpindahan dari satu kondisi ke kondisi lainnya sangat mudah karena ini diimplementasikan dalam implementasi negara
Implementasi dan penggunaan di: Implements .NET ThreadState oleh State Design Pattern
sumber
Saya belum mencoba mengimplementasikan FSM di C #, tetapi semua ini terdengar (atau terlihat) sangat rumit dengan cara saya menangani FSM di masa lalu dalam bahasa tingkat rendah seperti C atau ASM.
Saya percaya metode yang selalu saya kenal disebut sesuatu seperti "Iterative Loop". Di dalamnya, Anda pada dasarnya memiliki loop 'sementara' yang keluar secara berkala berdasarkan peristiwa (interupsi), lalu kembali ke loop utama lagi.
Di dalam interrupt handler, Anda akan melewati CurrentState dan mengembalikan NextState, yang kemudian menimpa variabel CurrentState di loop utama. Anda melakukan ini hingga tak terbatas hingga program ditutup (atau mikrokontroler diatur ulang).
Apa yang saya lihat di jawaban lain semuanya terlihat sangat rumit dibandingkan dengan bagaimana FSM, dalam pikiran saya, dimaksudkan untuk diterapkan; keindahannya terletak pada kesederhanaannya dan FSM bisa sangat rumit dengan banyak, banyak negara dan transisi, tetapi mereka memungkinkan proses rumit dengan mudah dipecah dan dicerna.
Saya menyadari bahwa tanggapan saya seharusnya tidak mencakup pertanyaan lain, tetapi saya terpaksa bertanya: mengapa solusi yang diusulkan ini tampak begitu rumit?
Mereka sepertinya mirip dengan memukul paku kecil dengan palu godam raksasa.
sumber
Apa yang StatePattern pertarungan. Apakah itu sesuai dengan kebutuhan Anda?
Saya pikir konteksnya terkait, tetapi nilainya patut dicoba.
http://en.wikipedia.org/wiki/State_pattern
Ini membiarkan negara Anda memutuskan ke mana harus pergi dan bukan kelas "objek".
Bruno
sumber
Menurut pendapat saya mesin keadaan tidak hanya dimaksudkan untuk mengubah keadaan tetapi juga (sangat penting) untuk menangani pemicu / peristiwa dalam keadaan tertentu. Jika Anda ingin memahami pola desain mesin keadaan lebih baik, deskripsi yang baik dapat ditemukan dalam buku Head First Design Patterns, halaman 320 .
Ini bukan hanya tentang status dalam variabel tetapi juga tentang penanganan pemicu dalam status berbeda. Bab yang hebat (dan tidak, tidak ada biaya bagi saya untuk menyebutkan ini :-) yang berisi penjelasan yang mudah dimengerti.
sumber
Saya baru saja berkontribusi ini:
https://code.google.com/p/ysharp/source/browse/#svn%2Ftrunk%2FStateMachinesPoC
Berikut adalah salah satu contoh demoing pengiriman perintah langsung dan tidak langsung, dengan status sebagai IObserver (sinyal), sehingga responden ke sumber sinyal, IObservable (sinyal):
Catatan: contoh ini agak buatan dan sebagian besar dimaksudkan untuk mendemonstrasikan sejumlah fitur ortogonal. Seharusnya jarang ada kebutuhan nyata untuk mengimplementasikan domain nilai keadaan itu sendiri oleh kelas penuh, menggunakan CRTP (lihat: http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern ) seperti ini.
Berikut ini untuk kasus penggunaan implementasi yang tentu lebih sederhana dan mungkin jauh lebih umum (menggunakan tipe enum sederhana sebagai domain nilai status), untuk mesin status yang sama, dan dengan case uji yang sama:
https://code.google.com/p/ysharp/source/browse/trunk/StateMachinesPoC/WatchingTVSample.cs
'HTH
sumber
StateChange
diselesaikan? Melalui Refleksi? Apakah itu benar-benar perlu?private Television(string moniker, Television value) { Handler<Television, TvOperation, DateTime> myHandler = StateChange; // (code omitted) new { From = Television.Unplugged, When = TvOperation.Plug, Goto = Television.Off, With = myHandler } }
Saya membuat mesin keadaan umum ini dari kode Juliet. Ini bekerja luar biasa bagi saya.
Inilah manfaatnya:
TState
danTCommand
,TransitionResult<TState>
untuk memiliki kontrol lebih besar atas hasil keluaran[Try]GetNext()
metodeStateTransition
hanya melaluiAddTransition(TState, TCommand, TState)
membuatnya lebih mudah untuk bekerja dengannyaKode:
Ini adalah jenis pengembalian metode TryGetNext:
Cara Penggunaan:
Ini adalah bagaimana Anda dapat membuat
OnlineDiscountStateMachine
dari kelas generik:Tentukan enum
OnlineDiscountState
untuk statusnya dan enumOnlineDiscountCommand
untuk perintahnya.Tentukan kelas yang
OnlineDiscountStateMachine
berasal dari kelas generik menggunakan kedua enum tersebutTurunkan konstruktor dari
base(OnlineDiscountState.InitialState)
sehingga keadaan awal diatur keOnlineDiscountState.InitialState
Gunakan
AddTransition
sebanyak yang diperlukanmenggunakan mesin negara turunan
sumber
Saya pikir mesin negara yang diusulkan oleh Juliet memiliki kesalahan: metode GetHashCode dapat mengembalikan kode hash yang sama untuk dua transisi yang berbeda, misalnya:
Untuk menghindari kesalahan ini, metodenya harus seperti ini:
Alex
sumber
int
). Itu sebabnyaHashCode
selalu diimplementasikan bersamaEquals
. Jika kode hash adalah sama, maka objek diperiksa untuk eqaulity yang tepat menggunakanEquals
metode ini.FiniteStateMachine adalah Mesin Status Sederhana, ditulis dalam C # Link
Keuntungan tu menggunakan perpustakaan saya, FiniteStateMachine:
Unduh DLL Unduh
Contoh pada LINQPad:
sumber
Saya akan merekomendasikan state.cs . Saya pribadi menggunakan state.js (versi JavaScript) dan saya sangat senang dengannya. Versi C # itu bekerja dengan cara yang serupa.
Anda memberi contoh status:
Anda instantiate beberapa transisi:
Anda mendefinisikan tindakan pada negara bagian dan transisi:
Dan itu (cukup banyak) itu. Lihatlah situs web untuk informasi lebih lanjut.
sumber
Ada 2 paket mesin negara yang populer di NuGet.
Appccelerate.StateMachine (13.6K unduhan + 3.82K versi lawas (bbv.Common.StateMachine))
StateMachineToolkit (1.56K unduhan)
Lib Appccelerate memiliki dokumentasi yang baik , tetapi tidak mendukung .NET 4, jadi saya memilih StateMachineToolkit untuk proyek saya.
sumber
Alternatif lain dalam repo ini https://github.com/lingkodsoft/StateBliss menggunakan sintaks yang lancar, mendukung pemicu.
sumber
Anda dapat menggunakan solusi saya, ini adalah cara yang paling nyaman. Ini juga gratis.
Buat mesin negara dalam tiga langkah:
1. Buat skema dalam editor simpul🔗 dan muat dalam proyek Anda menggunakan perpustakaan📚
2. Jelaskan logika aplikasi Anda pada acara⚡
3. Jalankan mesin negara🚘
Tautan:
Editor simpul: https://github.com/SimpleStateMachine/SimpleStateMachineNodeEditor
Perpustakaan: https://github.com/SimpleStateMachine/SimpleStateMachineLibrary
sumber