Hai, nama saya Alexei Valyakin, saya menulis aplikasi untuk Android. Beberapa bulan yang lalu, saya berbicara pada pertemuan tim Yandex.Taxi dengan pengembang ponsel. Laporan saya dikhususkan untuk transisi ke arsitektur RIB di Taksi (RIB adalah singkatan dari tiga Router, Interactor, Builder). Ini videonya, dan di bawah potongan - ringkasan:
- Sudah waktunya untuk melompat sedikit di kereta dengan hype. Ini adalah tema klasik tentang arsitektur di Android.
Serius, hari ini saya ingin memberi tahu Anda tentang bagaimana dan mengapa kami menerapkan arsitektur ini, kesulitan apa yang kami temui dan bagaimana itu dapat membantu Anda.

Ketika saya bergabung dengan perusahaan, tim kami terdiri dari empat orang. Sudah pada saat itu kami memiliki sejumlah besar kesulitan. Proyek ini sudah tua, dimulai pada tahun 2012. Ada beberapa masalah teknis yang dikumpulkan, salah satunya adalah CI yang dibangun secara salah, banyak variabilitas dalam pendekatan, tes yang tidak mencakup semuanya. Dan secara umum, ada sejumlah besar kesulitan dan menggabungkan konflik.
Dalam dua tahun kami telah berkembang menjadi 12 orang, yang berarti bahwa kami telah meningkatkan paralelisasi pengembangan fitur. Akibatnya, ada konflik penggabungan yang lebih banyak, dan dengan banyak koherensi kode Anda memahami apa yang menyebabkan hal ini. Pada titik tertentu, kami baru saja mulai tenggelam, dan entah bagaimana kami harus mengetahuinya. Sebagian dari masalah ini diselesaikan dengan point refactoring, sebagian oleh perpustakaan komponen, yang layak dibicarakan dalam laporan terpisah.

Apa yang semua pengembang inginkan? Arsitektur yang indah, fleksibilitas pengembangan, kemudahan menambahkan fitur, dan, tentu saja, mengurangi kompleksitas penggabungan - karena pada dasarnya mereka menyebabkan beberapa bug yang dapat muncul pada tahap rilis, ketika fitur dalam isolasi diuji dan bekerja dengan baik. Dan ketika mereka berhasil dan mencapai rilis - hop, semuanya berantakan. Ini adalah contoh gambar yang ingin kami datangi.

Bagaimana Anda bisa pergi padanya? Jelas bahwa ada banyak opsi untuk melakukan sesuatu dengan baik. Saya akan berbicara tentang pendekatan utama dan kelemahannya. Tentu saja ada solusi lain.

MVP klasik. Tantangan apa yang kita hadapi di MVP klasik? Jika kita melihat contoh proyek kita, kita mendapatkan bahwa ada Kegiatan MVP, MVP Fragment, MVP View. Dan ternyata variabilitas yang sangat hebat dalam apa yang perlu ditambahkan. Dalam beberapa kasus, Anda merasa perlu menambahkan tampilan dan sebuah fragmen. Kemudian ternyata menambahkan beberapa fitur kecil dengan mana manajer datang kepada Anda cukup sulit, karena umumnya terletak di Kegiatan MVP yang terpisah.
Masalah kedua yang dimiliki MVP terkait dengan fakta bahwa router memohon. Anda ingin menghubungkan anak-anak secara fleksibel dan agar Anda memiliki semacam esensi untuk ini. Karena itu, biasanya MVP datang ke beberapa jenis router buatan sendiri atau yang lainnya. Dan pendekatan tampilan didorong adalah minus yang cukup besar. Dalam banyak pola MVP, penyaji yang disuntikkan ke tampilan, ini sudah membuatnya kurang pasif dan melanggar arsitektur bersih.

Viper lebih baik. Ia memiliki entitas seperti router, ia lebih abstrak, namun ia memiliki sejumlah minus. Itu masih memiliki pandangan didorong logika, ia memiliki lapisan penyaji yang diperlukan melalui mana logika bisnis lewat, dan ini tidak selalu benar. Lapisan View juga diperlukan, Anda tidak bisa menghilangkannya.
Masalah utama adalah bahwa arsitektur ini datang kepada kita dari dunia iOS, sehingga perlu diadaptasi dengan cara tertentu untuk Android. Saya melihat ada beberapa adaptasi, dan beberapa di antaranya bahkan tidak ada, tetapi ada kerugiannya.

Jelas bahwa dalam dunia arsitektur tidak ada peluru perak, masing-masing arsitektur memiliki pro dan kontra. RIB juga memiliki kontra. Secara umum, Uber memperkenalkan arsitektur ini untuk sebagian besar di tingkat konsep. Mereka memiliki beberapa kelas terbuka, tidak ada contoh yang rumit. Ada beberapa tutorial sederhana yang bisa Anda lalui. Dan ketika beralih ke arsitektur apa pun, sejumlah besar refactoring mengikuti, yang perlu Anda lakukan, tetapi tidak hanya RIB yang memiliki minus ini.

Terdiri dari apa arsitektur RIB? Dia menggunakan komponen belati. Kelas utamanya, Builder, menyatukan seluruh komponen ini, yang terdiri dari bagian-bagian berikut: Router, Interactor. Presenter (View) - lapisan terpisah, terkadang ada, kadang tidak ada. Pada saat yang sama, Presenter (View) dapat digabung menjadi satu kelas, atau dibagi jika Anda memiliki logika presentasi yang salah.
Apa lagi yang keren di sini? Karena Presenter (View) adalah opsional, Anda menambahkan layar baru dengan cara yang sama seperti fitur bisnis baru. Struktur Anda lebih bersih dan mudah dimengerti. Anak tidak tahu apa-apa tentang orang tua, dan orang tua tahu tentang anak-anak. Mari kita lihat bagaimana ini terlihat seperti contoh struktur yang disederhanakan.

Anda selalu memiliki semacam root. Ini adalah root RIB. Itu memutuskan apa yang akan dimasukkan dalam dirinya sendiri, tergantung pada keadaan aplikasi Anda: itu adalah negara yang berwenang atau tidak sah. Mari kita lihat contoh aplikasi kita. Mungkin Anda berada di pesanan atau tidak di pesanan.
Sebagai contoh, fitur RIB keren lainnya. Anda dapat membuat RIB sebagai layar modal dan kemudian menghubungkannya secara prinsip dari RIB apa pun. Karena RIB tidak tahu apa-apa tentang orang tua, setiap orang tua dapat memberikan dependensi yang diperlukan untuk RIB anak.

Struktur modul mungkin terlihat seperti ini. Saat ini, kami hanya berpikir tentang memecah aplikasi kami menjadi modul. Dia sendirian bersama kami. Padahal, semuanya diimplementasikan cukup klasik. Anda memiliki semacam modul umum, dapat dipecah menjadi modul yang lebih kecil tergantung pada apa yang Anda butuhkan. Anda memiliki semacam API inti, mungkin jaringan, basis data, dll. Dan dalam sistem koordinat kami, RIB tertentu adalah modul terpisah, mencakup semua Umum, dll., Apa yang dibutuhkan, termasuk RIB anak
Jika beberapa hal perlu digabungkan antara beberapa RIB, ada beberapa contoh dengan kelas fitur Bersama yang menonjol hanya di modul terpisah.

Apa kelebihan RIB? Kemudahan pengujian, isolasi kode tinggi, pendekatan aktivitas tunggal, tidak ada rasa sakit dengan fragmen (siapa pun yang bekerja akan mengerti), dan keseragaman. Ini adalah arsitektur lintas platform, ada pendekatan untuk iOS dan Android. Dan jika Anda memiliki dua tim, ini merupakan nilai tambah yang besar, karena mereka akan berbicara dalam bahasa yang sama.
Ini poin penting. Ingin sedikit hack kehidupan tentang menerapkan RIB? Misalkan Anda mentransfer dependensi ke diri Anda sendiri, maka Anda mulai menambahkan fungsi ekstensi ahli waris dan memahami bahwa semua ini tidak cukup untuk Anda, Anda perlu menyesuaikannya untuk diri Anda sendiri. Pada akhirnya, Anda hanya mengambil dan mentransfernya ke kelas Anda. Dan ada cara lain - ketika Anda segera mentransfernya ke kelas Anda, tanpa membuang waktu pada opsi pertama, dan menyesuaikannya untuk diri Anda sendiri.
Saatnya untuk melihat kode dan bagaimana tampilannya.

Mereka memiliki plug-in yang nyaman yang memungkinkan Anda untuk menghasilkan kelas yang diperlukan untuk RIB, tanpa membuang waktu untuk membuatnya. Dia menciptakan empat kelas utama - Builder, Interactor, Router dan View, yang akan saya bicarakan lebih detail pada slide berikut. Ini juga menghasilkan tes. Secara alami, ia tidak akan menulisnya untuk Anda, dan Anda harus menulisnya sendiri, tetapi bagaimanapun, itu cukup bagus. Sekarang kami berpikir tentang membuat plugin yang akan menyederhanakan pembuatan modul baru dengan RIB. Plugin ini akan segera menghubungkan semua dependensi yang diperlukan, dan akan memakan waktu lebih sedikit untuk mengkonfigurasi modul.

Jadi, Builder adalah komponen kode lem klasik, tugas utamanya adalah untuk merakit semuanya bersama-sama, merakit komponen Belati dan Tampilan. Biasanya View akan memanggil konstruktor, tidak ada yang rumit di sana. Dalam beberapa kasus, itu mungkin mengembang.

Bagian kedua, yang ada di Builder, adalah tentang kecanduan, yaitu tentang bagaimana anak mendapatkan kecanduan dari luar.

Ini memiliki antarmuka Komponen Induk yang mendefinisikan dependensi yang dibutuhkan. Dengan demikian, dalam Builder komponen anak, semua dependensi yang dibutuhkan dari atas disediakan.

Interactor pada dasarnya adalah kelas yang paling penting yaitu logika bisnis. Hanya suntikan yang diizinkan masuk. Ini praktis adalah hal terpenting yang sedang diuji. Ini menerima acara dari lapisan UI menggunakan acara Stream RX. Presenter adalah antarmuka yang mendefinisikan metode yang disediakan oleh acara saya.
Apa lagi yang nyaman untuk RIB? Dengan fakta bahwa pada lapisan Interactor dan Presenter Anda dapat mengatur interaksi yang Anda sukai. Itu bisa MVP, dan MVVM, dan MVVI. Di sini setiap orang bebas memilih apa yang dia suka. Berlangganan acara Presenter mungkin terlihat seperti ini.

Dan di sini adalah bagaimana pemrosesan acara ini terlihat.

Router - kelas yang bertanggung jawab untuk menghubungkan anak-anak. Dia tidak memiliki logika bisnis, dia sendiri tidak menyebabkan anak-anak terhubung. Ini membuat Interactor dalam konsep seperti itu. Intinya, di sini saya memberikan contoh sederhana tentang bagaimana ini terjadi. Bahkan, Builder hanya memanggil metode Build, yang mengumpulkan RIB anak dan menghubungkan anak secara langsung menggunakan anak attach, serta menambahkan tampilan. Paling sering, logika ini dapat diringkas dalam transisi yang terpisah, Anda dapat mengkonfigurasi animasi - semuanya tergantung pada kebutuhan Anda.

Lihat se-pasif mungkin dalam arsitektur ini. Dia tidak menyuntikkan sesuatu ke dalam dirinya, dia hampir tidak tahu apa-apa. Dalam kasus yang paling sederhana, itu dapat mengimplementasikan antarmuka Presenter jika Anda tidak mengalami kesulitan menyajikannya. Dalam kasus yang lebih kompleks, logika ini dibagi menjadi dua kelas. Yaitu, Anda memiliki Presenter kelas terpisah, yang hanya memetakan data bisnis - misalnya, dalam tampilan model.
Berikut adalah contoh bagaimana Interactor menerima acara UI. Lihatlah aliran Rx.

Anda tidak bisa membangun arsitektur baru. Ketika Anda melakukan ini, terutama dalam proyek besar, kesulitan tertentu dimulai. Anda perlu memahami bahwa kami memiliki proyek besar: sekitar 20 Kegiatan, jika tidak lebih, dan sekitar 60 fragmen. Semua logika ini sangat terfragmentasi. Entah bagaimana perlu menggabungkan semua ini bersama-sama.

Pertama-tama, Anda harus menggabungkan semuanya menjadi satu titik navigasi tunggal, pertama-tama membuat objek dewa - Router Aktivitas, di mana Anda juga akan mengelola tumpukan fragmen, karena Anda akan memiliki banyak kode lama. Tidak ada yang akan membiarkan Anda menerapkan arsitektur baru sepanjang hari dan menghentikan bisnis. Dengan melakukannya, Anda harus berteman dengan setumpuk RIB. RIB juga secara alami memiliki tumpukan - dapat diakses dari bawah kap. Tetapi apa yang penting di sini? Cukup banyak kode yang harus diselesaikan sendiri. Uber tidak mendukung rotasi layar, jadi itu tidak begitu banyak tentang memulihkan keadaan. Oleh karena itu, hal pertama yang harus saya lakukan ketika saya mulai mempelajari arsitektur ini adalah menambahkan pewaris router, yang mendukung memulihkan hierarki RIB dan seluruh keadaan aplikasi.
Anda harus mendukung toggling Fitur. Tidak ada satu proyek besar yang dapat melakukannya tanpanya. Sekarang salah satu pengembang kami sedang mengembangkan konsep. Jika seseorang menonton Mobius 2016, kami
berbicara tentang Plugin Factory di atasnya, yang memungkinkan Anda untuk secara dinamis menyambungkan dan memutuskan blok logika tertentu - belum tentu potongan dengan layar. Itu dapat bertindak, misalnya, tergantung pada percobaan yang datang dari server. Semuanya dilakukan secara abstrak dan interaksi disederhanakan.
Alur kerja RIB juga merupakan konsep menarik yang mungkin Anda butuhkan. Ini adalah ketika Anda memiliki beberapa RIB yang tidak tahu apa-apa tentang satu sama lain, kira-kira pada tingkat yang sama, tetapi pada saat yang sama Anda harus memulai proses dengan data di input, dan pada akhirnya Anda harus meletakkan semuanya bersama-sama.
Dan, misalnya, layar modal. Kami memiliki desain super-kustom, sehingga hampir tidak ada dialog klasik yang tersisa. Semuanya ditulis sendiri, kita harus menerapkan semuanya sendiri.
Apa yang bisa Anda dapatkan dengan RIB? Isolasi kode, arsitektur sederhana yang dapat dimengerti, cara mudah untuk modularisasi, menghilangkan fragmen, pendekatan aktivitas tunggal dan kenyamanan pengembangan fitur secara paralel.
Referensi:
-
github.com/uber/RIBs-
github.com/uber/RIBs/tree/master/android/tutorials-
habr.com/en/company/livetyping/blog/320452-
youtu.be/Q5cTT0M0YXg-
github.com/xzaleksey/Role-Playing-System-V2-
github.com/xzaleksey/DeezerSampleTautan terakhir mengarah ke proyek kesayangan saya. Enam bulan yang lalu, saya mulai mengembangkan RIB untuk mencoba arsitektur ini sebelum memperkenalkannya kepada kami. Dan ada beberapa kasus nyata yang dapat membantu Anda. Saya banyak bereksperimen, jadi ada hal-hal yang kontroversial. Namun secara umum, Anda bisa melihat cara kerjanya di sana. Dan mungkin dari sana Anda akan mengambil sesuatu untuk diri sendiri.
Kami juga berpikir tentang kemudian mengalokasikan semua ini ke perpustakaan yang terpisah, seperti yang kami lakukan pada waktu kami dengan perpustakaan komponen. Akan ada tulang rusuk Yandex tersebut. Tapi ini di masa depan. Saya katakan semuanya, terima kasih.