Mesin negara jarang digunakan oleh pengembang seluler. Meskipun mayoritas tahu prinsip kerja dan mudah mengimplementasikannya secara mandiri. Dalam artikel ini, kami akan mencari tahu tugas apa yang diselesaikan oleh mesin negara menggunakan contoh aplikasi iOS. Kisah ini diterapkan di alam dan dikhususkan untuk aspek praktis dari pekerjaan.
Di bawah potongan Anda akan menemukan transkrip pidato Alexander Sychev (
Brain89 ) yang
diperluas di
AppsConf , di mana ia membagikan opsi untuk menggunakan mesin negara dalam mengembangkan aplikasi non-game.
Tentang pembicara: Alexander Sychev telah terlibat dalam pengembangan iOS selama delapan tahun, selama itu ia berpartisipasi dalam pembuatan aplikasi sederhana dan klien kompleks untuk jejaring sosial dan sektor keuangan. Saat ini, ada petunjuk teknis di Sberbank.
Mereka datang ke pemrograman dari banyak bidang, memiliki latar belakang dan pengalaman yang berbeda, jadi pertama-tama kita mengingat teori dasar.
Pernyataan masalah

Mesin negara adalah abstraksi matematis yang terdiri dari tiga elemen utama:
- banyak keadaan internal
- set sinyal input yang menentukan transisi dari kondisi saat ini ke yang berikutnya,
- set status akhir, saat transisi yang otomat menyelesaikan tugasnya ("masukkan kata input x").
Ketentuan
Maksud kami adalah variabel atau sekelompok variabel yang menentukan perilaku objek. Misalnya, dalam aplikasi iOS standar "Pengaturan
" ada item "Tebal" ("Dasar โ Akses universal"). Nilai dari item ini memungkinkan Anda untuk beralih di antara dua opsi untuk menampilkan teks pada tampilan perangkat.

Dengan mengirimkan sinyal yang sama "Ubah nilai toggle switch
" , kami mendapatkan reaksi yang berbeda dari sistem: baik gaya font biasa atau tebal - semuanya sederhana. Berada dalam keadaan berbeda dan menerima sinyal yang sama, suatu objek bereaksi berbeda terhadap perubahan keadaan.
Tugas tradisional
Dalam prakteknya, programmer sering menemukan mesin negara yang terbatas.
Aplikasi game
Ini adalah hal pertama yang terlintas dalam pikiran - sebagai bagian dari gameplay, hampir semuanya ditentukan oleh kondisi permainan saat ini. Jadi, Apple mengasumsikan penggunaan mesin negara terutama dalam aplikasi game (kami akan membahas ini secara rinci nanti).
Perilaku sistem saat memproses sinyal yang sama tetapi dengan keadaan internal yang berbeda dapat diilustrasikan dengan contoh-contoh berikut. Sebagai contoh:
โ karakter gim dapat memiliki kekuatan yang berbeda: satu di baju zirah mekanik dan dengan senjata laser, dan yang lainnya dipompa dengan lemah. Tergantung pada kondisi ini, perilaku musuh ditentukan: mereka menyerang atau melarikan diri.

โ permainan dijeda - tidak perlu menggambar bingkai saat ini; pemain dalam mode menu atau dalam proses game - rendering sangat berbeda.
Analisis Teks
Salah satu tugas analisis teks paling populer yang terkait dengan penggunaan mesin negara adalah filter spam. Biarkan ada satu set kata berhenti dan urutan input. Anda harus memfilter urutan ini atau tidak menampilkannya sama sekali.

Secara formal, ini adalah tugas mencari substring dalam string. Untuk mengatasinya, algoritma Knut-Morris-Pratt digunakan, implementasi perangkat lunak yang merupakan mesin negara terbatas. Status adalah offset dari urutan input dan jumlah karakter yang ditemukan dalam pola - stop word.
Juga,
ketika menganalisis ekspresi reguler , mesin negara hingga sering digunakan.
Pemrosesan query paralel
Mesin negara adalah salah satu opsi untuk menerapkan pemrosesan permintaan dan melaksanakan serangkaian instruksi yang ketat.

Misalnya, di server web nginx, permintaan input dari berbagai protokol diproses menggunakan mesin negara. Tergantung pada protokol spesifik, implementasi spesifik dari mesin negara dipilih, dan karenanya, serangkaian instruksi terkenal dieksekusi.
Secara umum, dua kelas masalah diperoleh:
- mengelola logika objek yang kompleks dengan keadaan internal yang kompleks,
- pembentukan kontrol dan aliran data (deskripsi algoritma).
Jelas, tugas-tugas umum seperti itu dihadapi dalam praktik setiap programmer. Oleh karena itu, penggunaan mesin negara dimungkinkan, termasuk dalam aplikasi konten non-game, yang terlibat dalam sebagian besar pengembang seluler.
Selanjutnya, kita akan menganalisis di mana dan kapan mesin negara dapat digunakan untuk membuat aplikasi iOS yang khas.
Sebagian besar aplikasi seluler memiliki arsitektur berlapis. Ada tiga lapisan dasar.
- Lapisan presentasi.
- Lapisan logika bisnis.
- Seperangkat pembantu, klien jaringan dan sebagainya (Lapisan inti).
Seperti yang ditunjukkan di atas, mesin negara mengontrol objek dengan perilaku kompleks, mis. dengan kondisi kompleks. Objek seperti itu pasti ada di lapisan presentasi, karena membuat keputusan dengan memproses input atau pesan pengguna dari sistem operasi. Mari kita lihat berbagai pendekatan untuk pelaksanaannya.

Dalam metafora arsitektur klasik Model-View-Controller, negara akan berada di controller: ia memutuskan apa yang ditampilkan dalam View dan bagaimana menanggapi sinyal input: menekan tombol, mengubah slider, dan sebagainya. Adalah logis bahwa salah satu implementasi dari pengontrol adalah mesin negara.

Dalam VIPER, negara berada di presenter: dialah yang menentukan transisi navigasi spesifik dari layar saat ini dan tampilan data di View.

Di Model-View-ViewModel, negara bagian di dalam ViewModel. Terlepas dari apakah kita memiliki pengikat reaktif atau tidak, perilaku modul yang didefinisikan dalam metafora MVVM akan direkam dalam ViewModel. Jelas, penerapannya melalui mesin negara adalah pilihan yang dapat diterima.

Objek kompleks dengan seperangkat keadaan non-sepele juga ditemui pada lapisan logika bisnis aplikasi. Misalnya, klien jaringan, yang tergantung pada apakah atau tidak koneksi ke server dibuat, mengirim atau memblokir permintaan. Atau objek untuk bekerja dengan database yang perlu menerjemahkan fungsi bahasa ke dalam query SQL, menjalankannya, mendapatkan respons, menerjemahkannya ke dalam objek, dll.

Dalam tugas yang lebih spesifik, seperti modul pembayaran, di mana rangkaian status yang lebih luas, logika kompleks, penggunaan mesin status juga benar.
Sebagai hasilnya, kami menemukan bahwa dalam aplikasi seluler ada banyak objek yang keadaan dan logikanya perilakunya digambarkan lebih rumit daripada dengan satu kalimat. Mereka harus mampu mengelola.
Pertimbangkan
contoh nyata dan pahami pada titik apa mesin keadaan terbatas benar-benar dibutuhkan, dan di mana penerapannya tidak dibenarkan.

Pertimbangkan ViewController dari aplikasi Championship iOS, sumber daya olahraga yang populer. Pengontrol ini menampilkan serangkaian komentar dalam bentuk tabel. Pengguna memasukkan deskripsi pertandingan, melihat foto, membaca berita dan meninggalkan komentar mereka. Layarnya cukup sederhana: lapisan di bawahnya memberikan data, diproses dan ditampilkan di layar.

Data nyata atau kesalahan dapat dikirim ke layar. Jadi operator kondisional pertama muncul, cabang pertama, yang menentukan perilaku aplikasi selanjutnya.
Pertanyaan selanjutnya adalah apa yang harus dilakukan jika tidak ada data. Apakah kondisi ini salah? Kemungkinan besar tidak: tidak setiap berita memiliki komentar pengguna. Misalnya, hoki di Mesir kurang menarik bagi siapa pun, dalam artikel seperti itu biasanya tidak ada komentar. Ini adalah perilaku normal dan keadaan normal layar yang harus Anda tampilkan. Jadi operator kondisional kedua muncul.
Adalah logis untuk berasumsi bahwa ada juga keadaan awal di mana pengguna mengharapkan data (misalnya, ketika layar komentar hanya muncul di layar). Dalam hal ini, tampilkan indikator memuat dengan benar. Ini adalah pernyataan kondisional ketiga.
Jadi kita sudah memiliki empat negara pada satu layar sederhana, logika tampilan yang dijelaskan melalui konstruksi if-else-if-else.

Tetapi bagaimana jika ada lebih banyak negara seperti itu? Pengembangan berulang layar mengarah ke kusut rumit konstruksi kondisional, banyak bendera, atau beberapa switch-case rumit. Kode ini menakutkan. Bayangkan bahwa pengembang yang akan mendukungnya tahu di mana Anda tinggal dan ia memiliki gergaji yang selalu ia bawa bersamanya. Dan Anda benar-benar ingin hidup sesuai dengan pensiun Anda yang kecil namun layak.
Saya pikir dalam hal ini perlu mempertimbangkan apakah layak meninggalkan implementasi seperti itu dalam aplikasi.
Kekurangan
Mari kita pahami apa yang tidak kita sukai dari kode ini.

Pertama-tama,
sulit dibaca .
Karena kode ini kurang dibaca, itu berarti akan sulit bagi pengembang baru untuk mencari tahu apa yang sebenarnya diterapkan di tempat tertentu dalam proyek. Oleh karena itu, akan menghabiskan banyak waktu menganalisis logika perilaku aplikasi -
biaya dukungan dan pengembangan akan meningkat .
Kode ini tidak
fleksibel . Jika Anda perlu menambahkan negara baru yang tidak mengikuti dari tangga saat ini, itu mungkin tidak berhasil sama sekali! Jika Anda membutuhkan jalan lintas - tiba-tiba berhenti melewati cek di tangga ini - bagaimana melakukannya? Hampir tidak ada.
Juga, dengan pendekatan ini,
tidak ada perlindungan dari negara-negara fiktif . Ketika transisi dijelaskan melalui sakelar, kemungkinan besar perilaku default diterapkan. Keadaan ini logis dalam hal perilaku program, tetapi hampir tidak logis dalam hal logika manusia atau bisnis aplikasi.
Apa yang bisa menjadi solusi untuk kekurangan yang ditunjukkan? Tentu saja, ini adalah konstruksi dari logika setiap modul / pengontrol / objek kompleks, bukan berdasarkan intuisi, tetapi menggunakan pendekatan formal yang baik. Misalnya, mesin keadaan terbatas.
Gameplaykit
Sebagai
contoh, kami mengambil apa yang ditawarkan Apple. Dalam kerangka kerja kerangka GameplayKit, ada dua kelas yang membantu kita bekerja dengan mesin negara.
Nama kerangka kerja memperjelas bahwa Apple ingin digunakan dalam game. Namun
dalam aplikasi non-game, itu akan bermanfaat.

Kelas
GKState mendefinisikan status. Untuk menggambarkannya, Anda perlu melakukan langkah-langkah sederhana. Kami mewarisi dari kelas ini, mengatur nama negara, dan mendefinisikan tiga metode.
- isValidNextState - apakah keadaan saat ini valid berdasarkan yang sebelumnya.
- didEnterFrom - tindakan pada transisi ke kondisi ini.
- willExitTo - tindakan saat keluar dari status ini.
GKStateMachine adalah kelas mesin negara. Itu bahkan lebih mudah. Cukup dengan melakukan dua tindakan.
- Kami melewati set status input ke array yang diketik melalui initializer.
- Kami membuat transisi tergantung pada sinyal input menggunakan metode enter. Melalui itu, keadaan awal juga diatur.
Mungkin membingungkan bahwa setiap kelas dilewatkan sebagai argumen ke metode
enter . Tetapi harus dicatat bahwa objek dari kelas apa pun tidak dapat didefinisikan dalam array status
- ini melarang pengetikan yang ketat. Dengan demikian, jika Anda menetapkan kelas arbitrer sebagai kelas negara berikutnya, tidak ada yang akan terjadi, dan metode enter akan kembali salah.
Menyatakan dan transisi di antara mereka
Setelah berkenalan dengan kerangka kerja dari Apple, mari kita kembali ke contoh. Penting untuk menggambarkan keadaan dan transisi di antara mereka. Anda perlu melakukan ini dengan cara yang paling bisa dimengerti. Ada dua opsi umum: tabel atau sebagai grafik transisi. Grafik transisi, menurut pendapat saya, adalah pilihan yang lebih dimengerti. Itu dalam UML dengan cara standar. Oleh karena itu, kami memilihnya.
Dalam grafik transisi ada negara yang dijelaskan oleh nama, dan panah yang menghubungkan negara-negara ini untuk menjelaskan transisi. Dalam contoh, ada kondisi awal
- kami mengharapkan data
- dan ada tiga status yang dapat dicapai dari yang awal: data yang diterima, tidak ada data, dan kesalahan.

Dalam implementasinya, kami mendapat empat kelas kecil.

Mari kita menganalisis keadaan "Data Pending". Di pintu masuk, ada baiknya menampilkan indikator unduhan. Dan ketika Anda keluar dari kondisi ini
, sembunyikan. Untuk melakukan ini, Anda harus memiliki tautan yang lemah ke ViewController, yang dikendalikan oleh mesin status yang dibuat.

Parameter mesin
Langkah kedua yang perlu dilakukan
adalah mengatur parameter mesin negara. Untuk melakukan ini, buat status dan transfer ke objek otomat.

Pastikan juga untuk mengatur kondisi awal

Pada prinsipnya, semuanya, mesin siap. Sekarang perlu untuk memproses reaksi terhadap peristiwa eksternal, mengubah keadaan otomat.

Ingat kembali pernyataan masalahnya. Kami mendapat tangga dari if-else, atas dasar itu diputuskan tindakan apa yang harus dilakukan. Sebagai kontrol untuk robot sederhana, opsi implementasi seperti itu mungkin (pada kenyataannya, saklar sederhana
- ini adalah implementasi primitif dari mesin keadaan terbatas), tapi kami praktis tidak menyingkirkan kelemahan yang disebutkan sebelumnya.
Ada pendekatan lain yang akan memungkinkan Anda untuk menjauh dari tangga ini. Diusulkan oleh klasik pemrograman
- yang disebut "geng empat."

Ada pola desain khusus, yang disebut "Status".

Ini adalah pola perilaku yang mirip dengan strategi yang menggambarkan abstraksi mesin negara. Ini memungkinkan objek untuk mengubah perilakunya tergantung pada negara. Tujuan utama aplikasi
adalah untuk merangkum perilaku dan data yang terkait dengan keadaan tertentu di kelas yang terpisah. Dengan demikian, mesin negara, yang awalnya membuat keputusan yang menyebabkan negara, sekarang akan mengirimkan sinyal, menerjemahkannya ke dalam keadaan, dan negara akan membuat keputusan. Jadi turunkan sebagian tangga, dan kodenya akan menjadi lebih menyenangkan untuk digunakan.
Kerangka kerja standar tidak tahu caranya. Dia menyarankan bahwa
GKStateMachine akan membuat keputusan. Oleh karena itu, kami memperluas mesin keadaan terbatas dengan metode baru, di mana sebagai konfigurasi, kami meneruskan deskripsi semua variabel kondisional yang secara unik menentukan status berikutnya. Di dalam metode ini, Anda dapat mendelegasikan pemilihan negara bagian berikutnya ke yang sekarang.

Ini adalah praktik yang baik untuk menggambarkan keadaan dengan satu objek dan selalu meneruskannya, daripada menulis banyak, banyak parameter input. Selanjutnya, kami mendelegasikan pilihan negara bagian berikutnya ke yang sekarang. Itu seluruh upgrade.
Keuntungan dari GameplayKit.- Perpustakaan standar. Tidak perlu mengunduh apa pun, gunakan cocoapods atau carthage.
- Perpustakaannya cukup mudah dipelajari.
- Ada dua implementasi sekaligus: pada Objective-C dan on Swift.
Kekurangan:- Realisasi keadaan dan transisi terkait erat.
Prinsip tanggung jawab tunggal dilanggar: negara tahu ke mana ia pergi dan bagaimana. - Status rangkap tidak dikontrol dengan cara apa pun.
Array dilewatkan ke mesin negara, bukan banyak negara. Jika Anda mentransfer beberapa status yang identik, yang terakhir dari daftar akan digunakan.
Apa lagi implementasi mesin negara terbatas? Lihatlah GitHub.
Implementasi Objective-C

TransitionKit
Ini adalah pustaka Objective-C yang paling populer untuk waktu yang lama, tanpa kekurangan yang diidentifikasi di GamePlayKit. Ini memungkinkan kita untuk mengimplementasikan mesin negara dan semua tindakan yang terkait dengannya di blok.
Negara terpisah dari transisi .
Dalam TransitionKit ada 2 kelas.
- TKState - untuk mengatur status dan input, tindakan keluaran.
- TKEvent adalah kelas untuk menggambarkan transisi.
TKEvent mengikat beberapa negara bagian ke negara lain. Peristiwa itu sendiri didefinisikan hanya dengan string.
Selain itu, ada manfaat tambahan.
Anda dapat mentransfer data yang berguna selama masa transisi . Ini berfungsi sama seperti ketika menggunakan NSNotificationCenter. Semua muatan berguna datang dalam bentuk kamus userInfo, dan pengguna mem-parsing informasi.
Transisi yang salah memiliki deskripsi . Ketika kami mencoba membuat transisi yang tidak ada - tidak mungkin - kami mendapatkan tidak hanya nilai NO saat kembali dari metode transisi, tetapi juga deskripsi terperinci dari kesalahan, yang berguna saat men-debug mesin negara.

TransitionKit digunakan dalam pemanen jaringan RestKit yang populer. Ini adalah contoh yang cukup bagus tentang bagaimana mesin negara dapat digunakan dalam kernel aplikasi ketika mengimplementasikan operasi jaringan.

RestKit memiliki kelas khusus - RKOperationStateMachine - untuk mengelola operasi bersamaan. Pada input, ia menerima operasi yang sedang diproses dan antrian untuk pelaksanaannya.

Secara internal, mesin negara sangat sederhana: tiga negara (siap, dieksekusi, selesai) dan dua transisi: memulai dan mengakhiri eksekusi. Setelah memulai pemrosesan (dan pada transisi apa pun), mesin negara mulai mengontrol blok kode pengguna yang telah ditentukan dalam antrian yang ditentukan saat membuat antrian.
Operasi yang terkait dengan automatonnya mentransfer peristiwa eksternal ke automaton, dan melakukan transisi antara status dan semua tindakan terkait. Mesin negara merawat
- eksekusi kode asinkron,
- eksekusi kode atom selama transisi,
- kontrol transisi
- Pembatalan operasi
- kebenaran dari perubahan variabel status operasi: isReady, isExecuting, isFinished.
Bergeser
Selain TransitionKit, ada baiknya menyebutkan
Shift secara terpisah - perpustakaan kecil yang diimplementasikan sebagai kategori di atas NSObject. Pendekatan ini memungkinkan Anda untuk mengubah objek apa pun menjadi mesin keadaan, menjelaskan kondisinya dalam bentuk konstanta string dan tindakan dalam blok selama transisi. Tentu saja, ini lebih merupakan proyek pelatihan, tetapi cukup menarik dan memungkinkan Anda untuk mencoba apa mesin negara dengan biaya minimal.
Implementasi Swift

Ada banyak implementasi mesin negara terbatas pada Swift. Saya akan memilih satu (
komentar : sayangnya, proyek belum berkembang dua tahun terakhir setelah laporan, tetapi ide-ide yang terkandung di dalamnya layak diceritakan dalam artikel).
SwiftyStateMachine
Di SwiftyStateMachine, mesin status diwakili oleh struktur yang tidak stabil, melalui metode didSet dari properti, Anda dapat dengan mudah menangkap perubahan status.
Di perpustakaan ini, mesin negara didefinisikan melalui tabel korespondensi negara dan transisi di antara mereka. Skema ini dijelaskan secara terpisah dari objek yang akan dikendalikan mesin. Ini diimplementasikan melalui switch-case bersarang.

, .
- .
, . - .
state machine , state machine. - , , .
- , DOT.
state- โ DOT. , , .

Kesimpulan
.
- .
, . , . , .
- .
( ).
- .
, , . - . , SwiftyStateMachine , , . .
- .
, . , , . .
.

. , . , , switch case: , , โ .

. . , . , , , . .

, , . .

โ
. : , โ .


ยซ-ยป
.

, . .
app coordinators โ , , : , . , .
, app coordinator , state machine. . , app coordinators state machine, , , ,
. , , , . .

, state machine , , .
state machine , if-else. , .
Tahun ini di Apps Conf 2018, yang akan diadakan pada tanggal 8 dan 9 Oktober, Alexander berencana untuk membahas lima prinsip dasar pemrograman berorientasi objek dan batas penerapannya.
Untuk laporan pengembangan seluler lainnya, lihat saluran YouTube kami . Dan jika Anda ingin menerima informasi tentang transkrip baru dan laporan menarik, berlangganan buletin .