Penafian: Artikel ini menjelaskan solusi yang tidak jelas untuk masalah yang tidak jelas. Sebelum bergegas telur mempraktikkannya, saya sarankan membaca artikel sampai akhir dan berpikir dua kali.

Halo semuanya! Ketika bekerja dengan kode, kita sering harus berurusan dengan keadaan . Salah satu contohnya adalah siklus hidup benda. Mengelola objek dengan beberapa status yang mungkin bisa menjadi tugas yang sangat sepele. Tambahkan eksekusi asinkron di sini dan tugas menjadi rumit oleh urutan besarnya. Ada solusi yang efektif dan alami. Pada artikel ini saya akan berbicara tentang mesin acara dan cara menerapkannya di Go.
Mengapa mengatur negara?
Untuk memulainya, mari kita mendefinisikan konsep itu sendiri. Contoh paling sederhana dari keadaan: file dan berbagai koneksi. Anda tidak bisa hanya mengambil dan membaca file. Pertama harus dibuka, dan pada akhirnya lebih disukai pastikan untuk menutup. Ternyata tindakan saat ini tergantung pada hasil dari tindakan sebelumnya: membaca tergantung pada pembukaan. Hasil yang disimpan adalah keadaan.
Masalah utama dengan negara adalah kompleksitas. Setiap negara secara otomatis menyulitkan kode. Anda harus menyimpan hasil tindakan dalam memori dan menambahkan berbagai pemeriksaan ke logika. Itulah mengapa arsitektur tanpa kewarganegaraan begitu menarik bagi para programmer - tidak ada yang mau masalah kesulitan. Jika hasil tindakan Anda tidak mempengaruhi logika eksekusi, Anda tidak perlu status.
Namun, ada satu properti yang membuat Anda memperhitungkan kesulitannya. Suatu negara mengharuskan Anda untuk mengikuti urutan tindakan tertentu. Secara umum, situasi seperti itu harus dihindari, tetapi ini tidak selalu memungkinkan. Contohnya adalah siklus hidup objek program. Berkat manajemen keadaan yang baik, seseorang dapat memperoleh perilaku objek yang dapat diprediksi dengan siklus hidup yang kompleks.
Sekarang mari kita cari tahu cara melakukannya dengan dingin .
Otomatis sebagai cara untuk menyelesaikan masalah

Ketika orang berbicara tentang negara, mesin negara yang terbatas segera muncul dalam pikiran. Itu logis, karena otomat adalah cara paling alami untuk mengelola keadaan.
Saya tidak akan mempelajari teori automata , ada lebih dari cukup informasi di Internet.
Jika Anda mencari contoh mesin negara berhingga untuk Go, Anda pasti akan bertemu dengan lexer dari Rob Pike . Sebuah contoh yang bagus dari otomat di mana data yang diproses adalah alfabet input. Ini berarti bahwa keadaan transisi disebabkan oleh teks yang diproses oleh lexer. Solusi elegan untuk masalah tertentu.
Hal utama yang harus dipahami adalah bahwa robot adalah solusi untuk masalah yang sangat spesifik. Karena itu, sebelum mempertimbangkannya sebagai obat untuk semua masalah, Anda harus sepenuhnya memahami tugas tersebut. Khususnya, entitas yang ingin Anda kontrol:
- negara - siklus hidup;
- peristiwa - apa yang sebenarnya menyebabkan transisi ke masing-masing negara;
- hasil kerja - data keluaran;
- mode eksekusi (sinkron / tidak sinkron);
- kasus penggunaan utama.
Lexer itu indah, tetapi hanya berubah karena data yang diprosesnya sendiri. Tetapi bagaimana dengan situasi ketika pengguna mengaktifkan transisi? Di sinilah mesin acara dapat membantu.
Contoh nyata
Untuk membuatnya lebih jelas, saya akan menganalisis contoh dari perpustakaan phono
.
Untuk perendaman lengkap dalam konteks, Anda dapat membaca artikel pengantar . Ini tidak perlu untuk topik ini, tetapi akan membantu untuk lebih memahami apa yang kami kelola.
Dan apa yang kita kelola?
phono
didasarkan pada pipa DSP. Ini terdiri dari tiga tahap pemrosesan. Setiap tahap dapat mencakup dari satu hingga beberapa komponen:

pipe.Pump
(pompa Inggris) adalah tahap wajib menerima suara, selalu hanya satu komponen.pipe.Processor
(pengendali bahasa Inggris) - tahap opsional pemrosesan suara, dari komponen 0 hingga N.pipe.Sink
(English sink) - tahap wajib transmisi suara, dari komponen 1 ke N.
Sebenarnya, kami akan mengatur siklus hidup konveyor.
Siklus hidup
Ini adalah seperti apa bentuk pipe.Pipe
Diagram keadaan pipe.Pipe
.

Miring menunjukkan transisi yang disebabkan oleh logika eksekusi internal. Tebal - transisi yang disebabkan oleh peristiwa. Diagram menunjukkan bahwa keadaan dibagi menjadi 2 jenis:
- status diam -
ready
dan paused
, Anda hanya dapat melompat darinya berdasarkan acara - status aktif -
running
dan pausing
, transisi berdasarkan peristiwa dan karena logika eksekusi
Sebelum analisis rinci kode, contoh yang jelas tentang penggunaan semua negara:
Sekarang, hal pertama yang pertama.
Semua kode sumber tersedia di repositori .
Negara dan Acara
Mari kita mulai dengan hal yang paling penting.
Berkat jenis yang terpisah, transisi juga dideklarasikan secara terpisah untuk setiap negara. Ini menghindari yang besar sosis fungsi transisi dengan switch
bersarang. Negara bagian itu sendiri tidak mengandung data atau logika apa pun. Bagi mereka, Anda dapat mendeklarasikan variabel di tingkat paket sehingga Anda tidak melakukan ini setiap saat. Antarmuka state
diperlukan untuk polimorfisme. activeState
idleState
berbicara tentang activeState
dan idleState
sedikit kemudian.
Bagian terpenting kedua dari mesin kami adalah acara.
Untuk memahami mengapa jenis target
diperlukan, pertimbangkan contoh sederhana. Kami telah membuat conveyor baru, sudah ready
. Sekarang jalankan dengan p.Run()
. Acara run
dikirim ke mesin, pipa masuk ke keadaan running
. Bagaimana cara mengetahui kapan konveyor selesai? Di sinilah jenis target
akan membantu kami. Ini menunjukkan keadaan istirahat apa yang diharapkan setelah acara. Dalam contoh kami, setelah pekerjaan selesai, pipa kembali akan memasuki status ready
. Hal yang sama pada diagram:

Sekarang lebih banyak tentang jenis negara. Lebih tepatnya, tentang activeState
dan activeState
. Mari kita lihat fungsi listen(*Pipe, target) (state, target)
untuk berbagai jenis tahapan:
pipe.Pipe
memiliki fungsi yang berbeda untuk menunggu transisi! Ada apa disana
Dengan demikian, kita dapat mendengarkan saluran yang berbeda di berbagai negara. Misalnya, ini memungkinkan Anda untuk tidak mengirim pesan saat jeda - kami hanya tidak mendengarkan saluran yang sesuai.
Konstruktor dan mulai dari mesin

Selain opsi inisialisasi dan fungsional , ada awal goroutine terpisah dengan siklus utama. Nah, lihat dia:
Konveyor dibuat dan membeku untuk mengantisipasi peristiwa.
Waktunya bekerja
Hubungi p.Run()
!

running
menghasilkan pesan dan berjalan sampai pipa selesai.
Jeda
Selama pelaksanaan conveyor, kita dapat menghentikannya. Dalam keadaan ini, pipeline tidak akan menghasilkan pesan baru. Untuk melakukan ini, panggil metode p.Pause()
.

Segera setelah semua penerima menerima pesan, saluran pipa akan paused
. Jika pesan adalah yang terakhir, maka transisi ke status ready
akan terjadi.
Kembali bekerja!
Untuk keluar dari negara yang paused
, panggil p.Resume()
.

Semuanya sepele di sini, saluran pipa kembali running
.
Meringkuk
Konveyor dapat dihentikan dari kondisi apa pun. Ada p.Close()
.

Siapa yang butuh ini?
Tidak untuk semua orang. Untuk memahami persis bagaimana mengelola negara, Anda harus memahami tugas Anda. Tepat ada dua keadaan di mana Anda dapat menggunakan mesin asinkron berbasis peristiwa:
- Siklus hidup yang kompleks - ada tiga negara atau lebih dengan transisi non-linear.
- Eksekusi asinkron digunakan.
Meskipun mesin acara menyelesaikan masalah, itu adalah pola yang agak rumit. Karena itu, harus digunakan dengan sangat hati-hati dan hanya setelah pemahaman yang lengkap tentang semua pro dan kontra.
Referensi