MVVM adalah bantuan pita untuk lapisan pengikat data yang dirancang dengan buruk. Secara khusus, ia telah melihat banyak digunakan di dunia WPF / silverlight / WP7 karena keterbatasan dalam pengikatan data di WPF / XAML.
Mulai sekarang, saya akan berasumsi kita berbicara tentang WPF / XAML karena ini akan membuat semuanya lebih jelas. Mari kita lihat beberapa kekurangan yang ingin diselesaikan oleh MVVM di WPF / XAML.
Bentuk data vs bentuk UI
The 'VM' di MVVM menciptakan satu set objek yang didefinisikan dalam C # yang memetakan ke set objek presentasi yang didefinisikan dalam XAML. Objek C # ini biasanya terhubung ke XAML melalui properti DataContext pada objek presentasi.
Akibatnya, grafik objek viewmodel perlu dipetakan ke grafik objek presentasi aplikasi Anda. Itu bukan untuk mengatakan bahwa pemetaan harus satu-ke-satu, tetapi jika kontrol daftar terkandung oleh kontrol jendela, maka harus ada cara untuk mendapatkan dari objek DataContext jendela ke objek yang menggambarkan data daftar itu.
Grafik objek viewmodel berhasil memisahkan model objek grafik dari grafik objek ui, tetapi dengan mengorbankan lapisan viewmodel tambahan yang harus dibangun dan dipelihara.
Jika saya ingin memindahkan beberapa data dari layar A ke layar B, saya perlu dipusingkan dengan model tampilan. Dalam benak seorang pebisnis, ini adalah perubahan UI. Itu harus terjadi murni di dunia XAML. Sayangnya, itu jarang bisa. Lebih buruk lagi, tergantung pada bagaimana viewmodels terstruktur dan seberapa aktif perubahan data, cukup banyak data re-routing yang diperlukan untuk mencapai perubahan ini.
Mengatasi ikatan data yang tidak ekspresif
Binding WPF / XAML tidak cukup ekspresif. Pada dasarnya Anda bisa menyediakan cara untuk sampai ke objek, jalur properti untuk dilalui, dan mengikat konverter untuk menyesuaikan nilai properti data dengan apa yang diminta objek presentasi.
Jika Anda perlu mengikat properti di C # ke sesuatu yang lebih kompleks dari itu, Anda pada dasarnya kurang beruntung. Saya belum pernah melihat aplikasi WPF tanpa konverter mengikat yang mengubah true / false menjadi Visible / Collapsed. Banyak aplikasi WPF juga cenderung memiliki sesuatu yang disebut NegatingVisibilityConverter atau sejenisnya yang membalik polaritas. Ini harus mematikan lonceng alarm.
MVVM memberi Anda panduan untuk menyusun kode C # yang dapat digunakan untuk memperhalus batasan ini. Anda dapat mengekspos properti pada model tampilan Anda yang disebut SomeButtonVisibility dan hanya mengikatnya ke visibilitas tombol itu. XAML Anda bagus dan cantik sekarang ... tetapi Anda telah menjadikan diri Anda sebagai pegawai toko - sekarang Anda harus mengekspos + memperbarui binding di dua tempat (UI dan kode dalam C #) saat UI Anda berkembang. Jika Anda perlu tombol yang sama berada di layar lain, Anda harus mengekspos properti serupa pada model tampilan yang dapat diakses layar itu. Lebih buruk lagi, saya tidak bisa hanya melihat XAML dan melihat kapan tombol akan terlihat lagi. Segera setelah binding menjadi sedikit tidak trivial, saya harus melakukan pekerjaan detektif dalam kode C #.
Akses ke data dibatasi secara agresif
Karena data umumnya masuk ke UI melalui properti DataContext, sulit untuk merepresentasikan data global atau sesi secara konsisten di seluruh aplikasi Anda.
Gagasan "pengguna yang saat ini masuk" adalah contoh yang bagus - ini seringkali benar-benar hal global dalam contoh aplikasi Anda. Dalam WPF / XAML sangat sulit untuk memastikan akses global ke pengguna saat ini secara konsisten.
Yang ingin saya lakukan adalah menggunakan kata "CurrentUser" di binding data secara bebas untuk merujuk ke pengguna yang saat ini masuk. Sebagai gantinya, saya harus memastikan bahwa setiap DataContext memberi saya cara untuk sampai ke objek pengguna saat ini. MVVM dapat mengakomodasi hal ini, tetapi model tampilan akan berantakan karena semuanya harus menyediakan akses ke data global ini.
Contoh di mana MVVM jatuh
Katakanlah kita memiliki daftar pengguna. Di sebelah setiap pengguna, kami ingin menampilkan tombol "hapus pengguna", tetapi hanya jika pengguna yang saat ini masuk adalah admin. Selain itu, pengguna tidak diizinkan untuk menghapus diri mereka sendiri.
Objek model Anda seharusnya tidak tahu tentang pengguna yang saat ini login - mereka hanya akan mewakili catatan pengguna dalam database Anda, tetapi entah bagaimana pengguna yang saat ini masuk perlu terkena bindings data dalam baris daftar Anda. MVVM menentukan bahwa kita harus membuat objek viewmodel untuk setiap baris daftar yang menyusun pengguna yang saat ini masuk dengan pengguna yang diwakili oleh baris daftar itu, lalu memaparkan properti yang disebut "DeleteButtonVisibility" atau "CanDelete" pada objek viewmodel itu (tergantung pada perasaan Anda) tentang mengikat konverter).
Objek ini akan terlihat sangat mirip dengan objek Pengguna dalam banyak cara lain - mungkin perlu mencerminkan semua properti objek model pengguna dan meneruskan pembaruan ke data tersebut saat ia berubah. Ini terasa sangat menjengkelkan - sekali lagi, MVVM membuat Anda menjadi pegawai dengan memaksa Anda untuk mempertahankan objek yang mirip dengan pengguna ini.
Pertimbangkan - Anda mungkin juga harus mewakili properti pengguna Anda dalam database, model, dan tampilan. Jika Anda memiliki API antara Anda dan database Anda, maka itu lebih buruk - mereka diwakili dalam database, server API, klien API, model, dan tampilan. Saya akan sangat ragu untuk mengadopsi pola desain yang menambahkan lapisan lain yang perlu disentuh setiap kali properti ditambahkan atau diubah.
Lebih buruk lagi, lapisan ini berskala dengan kompleksitas UI Anda, bukan dengan kompleksitas model data Anda. Seringkali data yang sama diwakili di banyak tempat dan di UI Anda - ini tidak hanya menambahkan lapisan, tetapi juga menambahkan lapisan dengan banyak area permukaan tambahan!
Bagaimana hal bisa terjadi
Dalam kasus yang dijelaskan di atas, saya ingin mengatakan:
<Button Visibility="{CurrentUser.IsAdmin && CurrentUser.Id != Id}" ... />
CurrentUser akan diekspos secara global ke semua XAML di aplikasi saya. Id akan merujuk ke properti di DataContext untuk baris daftar saya. Visibilitas akan dikonversi dari boolean secara otomatis. Setiap pembaruan untuk Id, CurrentUser.IsAdmin, CurrentUser, atau CurrentUser.Id akan memicu pembaruan visibilitas tombol ini. Mudah-peasy.
Sebaliknya, WPF / XAML memaksa penggunanya untuk membuat kekacauan lengkap. Sejauh yang saya tahu, beberapa blogger kreatif menampar nama pada kekacauan itu dan nama itu adalah MVVM. Jangan tertipu - ini tidak berada di kelas yang sama dengan pola desain GoF. Ini adalah hack jelek untuk bekerja di sekitar sistem pengikatan data yang jelek.
(Pendekatan ini kadang-kadang disebut sebagai "Pemrograman Reaktif Fungsional" jika Anda mencari bacaan lebih lanjut).
Kesimpulannya
Jika Anda harus bekerja di WPF / XAML, saya masih tidak merekomendasikan MVVM.
Anda ingin kode Anda disusun seperti contoh "bagaimana segala sesuatu bisa terjadi" di atas - model yang diekspos langsung untuk dilihat, dengan ekspresi pengikatan data yang kompleks + paksaan nilai yang fleksibel. Ini jauh lebih baik - lebih mudah dibaca, lebih bisa ditulis, dan lebih bisa dipelihara.
MVVM memberi tahu Anda untuk menyusun kode Anda dengan cara yang lebih verbose dan kurang dapat dikelola.
Alih-alih MVVM, buat beberapa hal untuk membantu Anda memperkirakan pengalaman yang baik: Kembangkan konvensi untuk mengekspos keadaan global ke UI Anda secara konsisten. Bangun diri Anda beberapa alat dari konverter mengikat, MultiBinding, dll yang memungkinkan Anda untuk mengekspresikan ekspresi mengikat yang lebih kompleks. Bangun sendiri perpustakaan pengonversi yang mengikat untuk membantu membuat kasus pemaksaan yang umum menjadi tidak terlalu menyakitkan.
Bahkan lebih baik - ganti XAML dengan sesuatu yang lebih ekspresif. XAML adalah format XML yang sangat sederhana untuk membuat instance objek C # - tidak akan sulit untuk menghasilkan varian yang lebih ekspresif.
Rekomendasi saya yang lain: jangan gunakan toolkit yang memaksa kompromi semacam ini. Mereka akan merusak kualitas produk akhir Anda dengan mendorong Anda ke arah omong kosong seperti MVVM alih-alih berfokus pada domain masalah Anda.