MVC: Apakah Pengendali melanggar Prinsip Tanggung Jawab Tunggal?

16

Prinsip Tanggung Jawab Tunggal menyatakan bahwa "kelas harus memiliki satu alasan perubahan".

Dalam pola MVC, tugas Pengontrol adalah memediasi antara View dan Model. Ini menawarkan antarmuka untuk tampilan untuk melaporkan tindakan yang dibuat oleh pengguna pada GUI (misalnya memungkinkan tampilan untuk memanggil controller.specificButtonPressed()), dan dapat memanggil metode yang sesuai pada Model untuk memanipulasi data itu atau menjalankan operasi itu (misalnya model.doSomething()) .

Ini berarti:

  • Pengontrol perlu mengetahui tentang GUI, untuk menawarkan tampilan antarmuka yang sesuai untuk melaporkan tindakan pengguna.
  • Ini juga perlu tahu tentang logika dalam Model, agar dapat memanggil metode yang sesuai pada Model.

Itu berarti ada dua alasan untuk berubah : perubahan dalam GUI, dan perubahan dalam logika bisnis.

Jika GUI berubah, misalnya tombol baru ditambahkan, Pengontrol mungkin perlu menambahkan metode baru untuk memungkinkan Tampilan melaporkan pengguna yang menekan tombol ini.

Dan jika logika bisnis dalam Model berubah, Controller mungkin harus berubah untuk memanggil metode yang benar pada Model.

Oleh karena itu, Pengendali memiliki dua kemungkinan alasan untuk berubah . Apakah itu merusak SRP?

Aviv Cohn
sumber
2
Kontroler adalah jalan 2 arah, ini bukan pendekatan top-down atau bottom-up Anda. Tidak ada kemungkinan baginya untuk mengabstraksikan salah satu dependensinya karena controller adalah abstraksi itu sendiri. Karena sifat polanya, tidak mungkin untuk mematuhi SRP di sini. Singkatnya: ya, itu melanggar SRP tapi itu tidak bisa dihindari.
Jeroen Vannevel
1
Apa gunanya pertanyaan itu? Jika kita semua menjawab "ya, benar", lalu bagaimana? Bagaimana jika jawabannya "tidak"? Apa masalah sebenarnya yang Anda coba selesaikan dengan pertanyaan ini?
Bryan Oakley
1
"alasan untuk berubah" tidak berarti "kode yang berubah". Jika Anda membuat tujuh kesalahan ketik dalam nama variabel Anda untuk kelas, apakah kelas itu sekarang memiliki 7 tanggung jawab? Tidak. Jika Anda memiliki lebih dari satu variabel atau lebih dari satu fungsi, Anda juga mungkin hanya memiliki satu tanggung jawab.
Bob

Jawaban:

14

Jika Anda melanjutkan dengan alasan tentang SRP, Anda akan mengetahui bahwa "satu tanggung jawab" sebenarnya adalah istilah yang kenyal. Otak manusia kita entah bagaimana dapat membedakan antara tanggung jawab yang berbeda dan beberapa tanggung jawab dapat diabstraksikan menjadi satu tanggung jawab "umum". Misalnya, bayangkan dalam keluarga 4 orang yang umum ada satu anggota keluarga yang bertanggung jawab untuk membuat sarapan. Sekarang, untuk melakukan ini, kita harus merebus telur dan roti panggang dan tentu saja menyiapkan secangkir teh hijau yang sehat (ya, teh hijau adalah yang terbaik). Dengan cara ini Anda dapat memecah "membuat sarapan" menjadi potongan-potongan kecil yang secara bersamaan disarikan menjadi "membuat sarapan". Perhatikan bahwa setiap karya juga merupakan tanggung jawab yang dapat misalnya didelegasikan kepada orang lain.

Kembali ke MVC: jika memediasi antara model dan tampilan bukan hanya satu tanggung jawab tetapi dua, lalu apa lapisan abstraksi berikutnya di atas, menggabungkan keduanya? Jika Anda tidak dapat menemukannya, Anda tidak bisa abstrak dengan benar atau tidak ada yang berarti Anda benar. Dan saya merasa itu adalah kasus dengan controller, menangani tampilan dan model.

valenterri
sumber
1
Sama seperti catatan tambahan. Jika Anda memiliki controller.makeBreakfast () dan controller.wakeUpFamily () maka controller itu akan merusak SRP, tetapi bukan karena itu adalah controller, hanya karena ia memiliki lebih dari satu tanggung jawab.
Bob
Terima kasih telah menjawab, tidak yakin saya mengikuti Anda. Apakah Anda setuju bahwa pengontrol memiliki lebih dari satu tanggung jawab? Alasan saya berpikir demikian adalah karena ia memiliki dua alasan untuk berubah (saya pikir): perubahan dalam model dan perubahan dalam pandangan. Apakah kamu setuju dengan ini?
Aviv Cohn
1
Ya, saya dapat setuju bahwa controller memiliki lebih dari satu tanggung jawab. Namun ini adalah tanggung jawab "lebih rendah" dan itu bukan masalah karena pada tingkat abstraksi tertinggi itu hanya memiliki satu tanggung jawab (menggabungkan yang lebih rendah yang Anda sebutkan) sehingga tidak melanggar SRP.
valenterri
1
Menemukan tingkat abstraksi yang tepat jelas penting. Contoh "membuat sarapan" adalah yang baik - untuk menyelesaikan satu tanggung jawab sering kali ada sejumlah tugas yang harus diselesaikan. Selama controller hanya mengatur tugas-tugas ini, ia mengikuti SRP. Tetapi jika tahu terlalu banyak tentang merebus telur, membuat roti panggang, atau menyeduh teh, maka itu akan melanggar SRP.
Allan
Jawaban ini masuk akal bagi saya. Valenterry terima kasih.
J86
9

Jika kelas memiliki "dua kemungkinan alasan untuk berubah", maka ya, itu melanggar SRP.

Pengontrol biasanya harus ringan dan memiliki tanggung jawab tunggal untuk memanipulasi domain / model sebagai respons terhadap beberapa peristiwa yang didorong GUI. Kita dapat menganggap masing-masing manipulasi ini pada dasarnya menggunakan case atau fitur.

Jika tombol baru ditambahkan pada GUI, pengontrol hanya harus mengubah jika tombol baru itu mewakili beberapa fitur baru (yaitu berlawanan dengan tombol yang sama yang ada pada layar 1 tetapi tidak ada di layar 2, dan kemudian ditambahkan ke layar 2). Perlu ada perubahan baru yang sesuai dalam model juga, untuk mendukung fungsi / fitur baru ini. Pengontrol masih memiliki tanggung jawab untuk memanipulasi domain / model dalam menanggapi beberapa peristiwa yang didorong GUI.

Jika logika bisnis dalam model berubah karena bug diperbaiki, dan itu memerlukan pengontrol untuk berubah, maka ini adalah kasus khusus (atau mungkin model melanggar prinsip buka-tutup). Jika logika bisnis dalam model berubah untuk mendukung beberapa fungsionalitas / fitur baru, maka itu tidak serta-merta berdampak pada pengontrol - hanya jika pengontrol perlu memaparkan fitur tersebut (yang hampir selalu merupakan kasusnya, jika tidak mengapa akan ditambahkan ke model domain jika tidak akan digunakan). Jadi dalam hal ini pengontrol harus dimodifikasi juga, untuk mendukung memanipulasi model domain dengan cara baru ini sebagai respons terhadap beberapa peristiwa yang didorong oleh GUI.

Jika controller harus berubah karena, katakanlah, layer persistence diubah dari flat file ke database, maka controller tersebut tentunya melanggar SRP. Jika controller selalu bekerja pada lapisan abstraksi yang sama, maka itu dapat membantu dalam mencapai SRP.

Yordania
sumber
4

Pengontrol tidak melanggar SRP. Saat Anda menyatakan, tanggung jawabnya adalah memediasi antara model dan tampilan.

Yang sedang berkata, masalah dengan contoh Anda adalah bahwa Anda mengikat metode pengontrol untuk logika dalam pandangan, yaitu controller.specificButtonPressed. Memberi nama metode dengan cara ini mengikat controller ke GUI Anda, Anda telah gagal untuk hal-hal abstrak dengan benar. Pengontrol harus tentang melakukan tindakan spesifik, yaitu controller.saveDataatau controller.retrieveEntry. Menambahkan tombol baru di GUI tidak berarti menambahkan metode baru ke controller.

Menekan tombol dalam tampilan, berarti melakukan sesuatu tetapi apa pun yang dapat dengan mudah dipicu dalam sejumlah cara lain atau bahkan tidak melalui tampilan.

Dari artikel Wikipedia tentang SRP

Martin mendefinisikan tanggung jawab sebagai alasan untuk berubah, dan menyimpulkan bahwa kelas atau modul harus memiliki satu, dan hanya satu, alasan untuk berubah. Sebagai contoh, pertimbangkan modul yang mengkompilasi dan mencetak laporan. Modul semacam itu dapat diubah karena dua alasan. Pertama, isi laporan dapat berubah. Kedua, format laporan bisa berubah. Dua hal ini berubah untuk tujuan yang sangat berbeda; satu substantif, dan satu kosmetik. Prinsip tanggung jawab tunggal mengatakan bahwa kedua aspek masalah ini adalah dua tanggung jawab yang terpisah, dan karenanya harus berada dalam kelas atau modul yang terpisah. Ini akan menjadi desain yang buruk untuk memasangkan dua hal yang berubah karena alasan yang berbeda pada waktu yang berbeda.

Pengontrol tidak peduli dengan apa yang ada dalam tampilan hanya bahwa ketika salah satu metodenya disebut itu memberikan data yang ditentukan untuk tampilan. Ia hanya perlu tahu tentang fungsionalitas model sejauh ia tahu bahwa ia perlu memanggil metode yang akan mereka miliki. Tidak tahu apa-apa lebih dari itu.

Mengetahui bahwa suatu objek memiliki metode yang tersedia untuk dipanggil tidak sama dengan mengetahui fungsinya.

Schleis
sumber
1
Alasan saya pikir controller harus menyertakan metode seperti specificButtonsPressed()ini adalah karena saya membaca bahwa view tidak tahu apa-apa tentang fungsi tombol-tombolnya dan elemen GUI lainnya. Saya telah diajarkan bahwa ketika sebuah tombol ditekan, tampilan harus melapor ke controller, dan controller harus memutuskan 'apa artinya' (dan kemudian memanggil metode yang sesuai pada model). Membuat panggilan panggilan controller.saveData()berarti pandangan harus tahu tentang apa arti tombol ini ditekan, selain fakta bahwa itu ditekan.
Aviv Cohn
1
Jika saya membuang ide pemisahan lengkap antara tombol tekan dan artinya (yang mengakibatkan controller memiliki metode seperti specificButtonPressed()), memang controller tidak akan terlalu terikat pada GUI. Haruskah saya membuang specificButtonPressed()metode? Apakah keuntungan saya pikir memiliki metode ini masuk akal bagi Anda? Atau memiliki buttonPressed()metode di controller tidak sepadan dengan masalahnya?
Aviv Cohn
1
Dengan kata lain (maaf untuk komentar panjang): Saya pikir keuntungan dari memiliki specificButtonPressed()metode di controller, adalah bahwa ia memisahkan View dari arti penekanan tombol sepenuhnya . Namun, kerugiannya adalah bahwa ia mengikat controller ke GUI dalam arti tertentu. Pendekatan mana yang lebih baik?
Aviv Cohn
@prog IMO Kontroler harus buta terhadap tampilan. Sebuah tombol akan memiliki semacam fungsi tetapi tidak perlu mengetahui detailnya. Hanya perlu tahu bahwa itu mengirim beberapa data ke metode pengontrol. Dalam hal itu namanya tidak masalah. Itu bisa disebut foo, atau dengan mudah fireZeMissiles. Ia hanya akan tahu bahwa ia harus melapor ke fungsi tertentu. Tidak tahu apa fungsinya hanya yang akan memanggilnya. Pengontrol tidak peduli dengan bagaimana metodenya dipanggil hanya karena akan merespons dengan sopan santun ketika mereka melakukannya.
Schleis
1

Satu tanggung jawab pengendali adalah menjadi kontrak yang memediasi antara pandangan dan model. Tampilan hanya bertanggung jawab atas tampilan, model hanya bertanggung jawab atas logika bisnis. Tanggung jawab pengendali untuk menjembatani kedua tanggung jawab itu.

Itu semua baik dan bagus, tetapi untuk sedikit menjauhkan diri dari akademisi; sebuah pengontrol di MVC umumnya terdiri dari banyak metode aksi yang lebih kecil. Tindakan ini umumnya sesuai dengan hal-hal yang dapat dilakukan oleh sesuatu. Jika saya menjual produk, saya mungkin akan memiliki ProductController. Pengontrol itu akan memiliki tindakan seperti GetReviews, ShowSpecs, AddToCart dll ...

Tampilan memiliki SRP untuk menampilkan UI, dan bagian dari UI itu termasuk tombol yang mengatakan AddToCart.

Pengendali memiliki SRP untuk mengetahui semua Tampilan dan Model yang terlibat dalam proses.

Kontroler AddToCart pengendali memiliki SRP khusus untuk mengetahui semua orang yang perlu dilibatkan saat item ditambahkan ke troli.

Model Produk memiliki SRP pemodelan logika produk, dan Model ShoppingCart memiliki SRP pemodelan bagaimana item disimpan untuk checkout berikutnya. Model Pengguna memiliki SRP pemodelan pengguna yang menambahkan barang ke keranjang mereka.

Anda dapat dan harus menggunakan kembali model untuk menyelesaikan bisnis Anda dan model-model itu perlu digabungkan pada beberapa titik dalam kode Anda. Pengontrol mengontrol setiap cara unik yang terjadi kopling.

Daftar putihJ
sumber
0

Pengendali sebenarnya hanya memiliki satu tanggung jawab: mengubah status aplikasi berdasarkan input pengguna.

Sebuah kontroler dapat mengirimkan perintah ke model untuk memperbarui negara model (misalnya, mengedit dokumen). Ia juga dapat mengirim perintah ke tampilan terkait untuk mengubah presentasi tampilan model (misalnya, dengan menggulir dokumen).

source: wikipedia

Jika sebaliknya Anda memiliki "pengendali" gaya Rails (yang menyulap instance rekaman aktif dan templat bisu) , maka tentu saja keluar melanggar SRP.

Kemudian lagi, aplikasi Rails-style sebenarnya bukan MVC.

mefisto
sumber