Menahan sifat buruk imperatif

Paradigma berorientasi objek sangat nyaman untuk bisnis: memungkinkan Anda untuk menerapkan hampir semua ide, memberikan kinerja produk yang dapat diterima. Dalam hal ini, berdasarkan produk yang kami maksud adalah aplikasi iOS, oleh karena itu, dalam kesimpulan, kami akan melanjutkan dari pengembangan khusus pada platform ini.

Dengan menutup mata terhadap kekurangan paradigma populer yang terkenal ini, daftar minusnya mencakup keunggulannya yang paling penting - fleksibilitas pembangunan. Mengapa ini minus? Sangat jelas bahwa fleksibilitas, di samping kemampuan dasar untuk menyelesaikan masalah bisnis, memungkinkan untuk melakukan ini dalam berbagai cara. Memang benar bahwa ada selusin yang salah untuk satu pendekatan yang benar, terlepas dari kenyataan bahwa tugas bisnis akan diselesaikan dengan benar dalam setiap kasus, tetapi dengan perbedaan dalam implementasi, ekstensibilitas dan transparansi yang sudah akan bergantung pada kebenaran dari pendekatan yang diterapkan.

Dengan mempertimbangkan hukum Murphy, kesimpulannya adalah bahwa, jika tidak ada batasan arsitektur yang tepat, lebih mungkin untuk mengikuti jalur kekacauan, yaitu kualitas kode akan menurun dengan cepat, hanya karena paradigma memungkinkannya. Artikel ini akan membahas salah satu keterbatasan arsitektur yang mungkin yang akan membantu menjaga keseimbangan kekuatan kebaikan dan kejahatan, dengan kata lain, dinamika pertumbuhan entropi dalam basis kode proyek. Penting bahwa dinamika ini berkorelasi langsung dengan jumlah orang yang terlibat dalam penulisan kode, oleh karena itu, untuk proyek-proyek besar, ketepatan pembatasan yang dipilih sangat penting. Apa gunanya

Intinya sederhana. Mari kita beralih dari aksioma berikutnya - semakin banyak properti dalam suatu objek, semakin "buruk" itu. Pernyataan ini dapat dijelaskan sebagai berikut: dengan peningkatan jumlah keadaan internal, semua indikator positif objek - ekstensibilitas, modularitas, transparansi, testabilitas - penurunan. Seseorang dapat menolak, mengatakan bahwa kompleksitas objek dan fungsinya saling berhubungan dan tanpa yang pertama, yang kedua tidak terjadi. Semuanya benar, tetapi, mengikuti jalur "stateful", pertumbuhan kompleksitas terjadi secara eksponensial, terlepas dari kenyataan bahwa idealnya pertumbuhan harus linier, atau, lebih sederhana, fitur baru tidak boleh menyulitkan fitur yang ada.

PS Di sini perlu diperjelas bahwa fitur dipahami sebagai lapisan logika bisnis yang terkait secara semantik, oleh karena itu keputusan sering dibuat untuk memperumit kelas yang ada, daripada membuat yang baru. Dalam kasus seperti itu, sulit untuk menemukan kontradiksi dengan prinsip SOLID pertama.

Contohnya adalah layar yang sepenuhnya standar dengan daftar entitas dengan pilihan. Beberapa properti dapat dibuat tidak stabil, tetapi IB mengikat kita ke konstruktor default controller dan ini memaksa kita untuk "membuat kotoran." Akses ke properti ini secara implisit diperoleh oleh setiap metode kelas dalam file yang sama. Dan momen yang paling tidak menyenangkan adalah bahwa kemampuan mereka untuk berubah juga tidak tercakup oleh apa pun, dan ini entah bagaimana mengarah pada konsekuensi yang tidak dapat diubah dalam bentuk koneksi yang kuat dan, akibatnya, pada fakta bahwa fiksasi beberapa cacat menyebabkan munculnya yang baru:



Kita dapat menyimpulkan bahwa peningkatan linear dalam kompleksitas suatu objek dengan keadaan umum secara praktis tidak dapat dicapai. Fungsionalitas dengan pendekatan ini menjadi monolitik dan sulit untuk segala jenis pemisahan. Contoh yang baik adalah Massive-View-Controller antipattern, yang cukup umum dan jelas menunjukkan hasil dari tidak adanya batasan pada proyek.



Dengan menggunakan UIKit sebagai contoh, Anda dapat melihat dengan tepat bagaimana gaya pengembangan imperatif memengaruhi kompleksitas kode dan pada titik mana kerangka kerja β€œmemaksa” properti yang akan dibuat di kelas.
Kasus paling sederhana - pemrosesan menekan tombol - secara standar dilakukan hanya dengan mendefinisikan "metode kotor," yaitu, dengan fungsi yang tidak dapat menerima apa pun yang berguna, jadi Anda harus keluar untuk properti:



Dengan demikian, "fitur" tombol secara tirani akan menyulitkan seluruh fungsionalitas objek, karena semua penghuni kelas akan memiliki akses ke data ini. Bahkan, hampir semua kontrol iOS UI bekerja dengan cara yang sama. Oleh karena itu, terlintas dalam pikiran untuk menerapkan semacam pembungkus atas elemen UI, misalnya, yang memerlukan penutupan, sebagai pemrosesan dari "operasi" elemen tertentu, tetapi kesulitannya adalah bagaimana membuat pembungkus tersebut ringkas dan dapat diandalkan. Bagaimanapun, masalahnya tidak hanya di input, tetapi juga di output informasi, misalnya, tabel di mana-mana dengan data juga berfungsi "kotor" dan tidak tahu tentang data yang ditampilkan, sehingga Anda harus menyimpannya di objek:



Apakah itu nyaman? Semua orang selalu memiliki akses ke data. Fleksibel, cepat, penting. Tetapi semuanya berubah seiring waktu, sebagai suatu peraturan, menjadi sebuah obelisk konkret untuk seribu baris kode dan menjadi tangisan mereka yang mendapat tugas bekerja di kelas ini.

Kembali ke keterbatasan, prinsip berikut dapat dibentuk dari kode di atas tanpa properti di kelas jauh lebih bersih, dan lebih menguntungkan dengan dukungan dan ekstensi. Idealnya, logika objek harus dalam metode "murni", yang mengambil semua dependensinya sebagai input dan memiliki hasil aktivitasnya pada output.
Idenya adalah untuk menurunkan keadaan privat objek satu tingkat lebih rendah. Artinya, jika swasta menutup properti dari dunia luar, maka tugas kita adalah melangkah lebih jauh dan memperkuat enkapsulasi ke tingkat metode itu sendiri. Pada pandangan pertama, untuk semua sifat platform yang tidak sinkron dan ketidaksopanan kerangka kerja utama, mungkin tampak bahwa seluruh gagasan ini tidak hanya mustahil, tetapi setidaknya implementasinya akan terasa tidak wajar. Dan, kemungkinan besar akan begitu, tetapi ini adalah salah satu masalah yang dapat diselesaikan dengan memperkenalkan tingkat abstraksi tambahan.

Jika kita ingin mencocokkan semua logika kelas ke dalam metode mereka tanpa menggunakan properti, kita perlu bekerja sama dengan penutupan. IOS memiliki alat standar untuk mengelola operasi asinkron, seperti GCD dan OperationQueue. Tampaknya itu sudah cukup, tetapi ketika Anda mencoba menghidupkan gagasan itu, semuanya akan berubah menjadi tidak semerah yang Anda inginkan. Dengan pendekatan ini, terlepas dari kenyataan bahwa ada peluang besar untuk mendapatkan panggilan balik-neraka, kode itu sendiri akan berubah menjadi rumit, itu akan memiliki banyak lubang logis dan akan sangat terhubung. Ada kemungkinan bahwa kode seperti itu akan lebih rumit daripada apa yang kita coba dengan cepat untuk menghindarinya.

Jika Anda melihat-lihat, Anda dapat melihat bahwa ada banyak cara yang lebih indah dan fungsional untuk mencapai tujuan ini. Pemrograman reaktif telah lama digunakan dalam pengembangan perangkat lunak komersial dan sangat ideal untuk dunia front-end yang tidak sinkron. Dalam hal ini, kami akan mempertimbangkan satu (agak berhasil) implementasi dari paradigma reaktif untuk Swift - Rx.



Menawarkan entitas sederhana yang disebut Observable. Ini adalah semacam abstraksi atas aliran peristiwa, dapat berlangganan, dan setelah itu pelanggan akan menerima peristiwa ini dari waktu ke waktu:



Cara termudah untuk membayangkan aliran acara adalah dengan menghadirkan tombol biasa. Kejadian operasinya ada di sini streaming, sehingga objek apa pun dapat berlangganan dan menerima acara mengkliknya, dan yang terpenting, tombol tidak tahu apa-apa tentang pelanggannya sendiri. Secara nyaman, hampir semua tindakan dapat diubah menjadi urutan nilai yang serupa, dan ini penting, karena Dapat diamati dapat dikombinasikan satu sama lain, karena tidak ada kerangka kerja standar yang memungkinkan.

Misalnya, Anda dapat mengirim beberapa permintaan dengan menekan tombol (memfilter ketuk dua kali), menunggu masing-masing untuk menjawab, kemudian menggabungkan jawaban dengan apa yang dimasukkan pengguna di layar, menjalankan permintaan lain dan pergi ke layar berikutnya, mentransfer hasilnya ke sana, ketika Rx ini akan memungkinkan untuk menangani kesalahan secara singkat dan menyelesaikan rantai ini (membatalkan permintaan) saat keluar dari layar, dan semua logika ini akan mengambil dua lusin baris kode yang diketik:



Perlu berbicara tentang satu-satunya properti disposeBag. Seperti yang dapat dilihat dari tangkapan layar, setiap langganan ditempatkan di dalamnya, dan ini diperlukan untuk mengontrol masa pakai mereka. Artinya, langganan berlaku selama "tas" hidup di mana mereka ditempatkan, dalam hal ini, selama pengontrol hidup.

Selain kekompakan, sulit untuk membuat kesalahan dalam kode di atas, karena setiap penutupan menghasilkan sesuatu dan tidak mengandung efek samping. Inilah kekuatan yang kami cari.

Anda dapat melihat satu hal penting lagi: karena kelas tidak memiliki properti, tidak perlu menulis [diri lemah], yang secara positif mempengaruhi keterbacaan kode. Semua fungsi dapat dan lebih baik didefinisikan secara lokal dalam metode di mana mereka digunakan, atau diambil dalam kelas yang terpisah. Omong-omong, tautan ke dependensi (ViewModel, Presenter, dll.) Dalam hal ini dapat diteruskan sebagai argumen ke metode pengontrol, dalam hal ini tidak perlu menyimpannya di properti. Ini benar

Setelah ditinjau, sekarang saatnya menggunakan Observable untuk menyederhanakan pengembangan. Bagaimana tepatnya? Mari kita kembali ke ide metode "bersih" dan mencoba menerapkan logika layar kecil dalam satu-satunya metode .Untuk kejelasan, kita akan memilih metode untuk mengakhiri pemuatan tampilan (viewDidLoad). Tentu saja, jika layar dibuat di IB, maka kita harus membuat properti untuk outlet, tetapi ini tidak menakutkan, karena elemen-elemen itu sendiri tidak mewakili logika bisnis, oleh karena itu mereka tidak akan sangat mempengaruhi kompleksitas layar. Tetapi jika layar terdiri dari kode, maka Anda dapat melakukannya tanpa properti sama sekali (kecuali untuk disposeBag), membuat elemen dalam metode kami dan menggunakannya di sana. Bagaimana dengan sifat imperatif elemen UIKit yang dijelaskan sebelumnya? Rx, di samping pendekatan itu sendiri, menyediakan pembungkus reaktif untuk komponen UI standar, sehingga dalam kebanyakan kasus Anda bisa mendapatkan urutan peristiwa yang diperlukan di tempat. Atau, sebaliknya, ikat Observable yang ada dengan, misalnya, sebuah tabel - bawalah permintaan sehingga ia memperbarui kontennya segera setelah selesai:



Mengikat koleksi cukup fleksibel, tetapi secara default, ia hanya bekerja melalui reloadData. Untuk pembaruan poin, ada pustaka debug yang bagus dari penulis yang sama - RxDataSources. Dengan itu, Anda bisa melupakan crash selama batchUpdates.

Apa yang akan terjadi selanjutnya? Metode layar tunggal akan tumbuh dan dalam kasus yang agak rumit, itu akan menjadi sulit untuk dipertahankan. Dan ketika ini terjadi, tiba-tiba menjadi jelas bahwa metode ini tidak bergantung pada apa pun selain dari dirinya sendiri, dan pendekatan reaktif telah membagi kode menjadi blok logis yang dapat dengan mudah dimasukkan ke objek terpisah dengan mendesainnya dengan cara yang sama. Tapi kali ini, metode sudah akan mengambil beberapa dependensi di layar dan mengembalikan beberapa hasil. Poin positifnya adalah bahwa tanda tangan dalam hal ini diperoleh sebagai konten yang mungkin, dapat dilihat bahwa fungsi tersebut membutuhkan kerjanya dan apa hasilnya. Mungkin terlihat seperti ini:



Struktur terpisah membantu menjaga tajuk metode tetap mudah dibaca dan rapi, karena ada banyak dependensi.

Penting untuk dipahami bahwa metode ini tidak harus menjadi satu-satunya di seluruh objek, esensi kemerdekaan mereka satu sama lain. Berkat Rx, input dan output mereka dapat sinkron dan mewakili satu atau lebih yang dapat diamati, memberikan dimensi lain untuk manipulasi data.

Pendekatan ini membuka ikatan tangan Anda dan memungkinkan Anda untuk menerapkan layar dari hampir semua kompleksitas, sambil mempertahankan kode mereka secara eksplisit dan longgar digabungkan.

Source: https://habr.com/ru/post/id435724/


All Articles