Apa yang masuk ke "Controller" di "MVC"?

186

Saya pikir saya mengerti konsep dasar MVC - Model berisi data dan perilaku aplikasi, View bertanggung jawab untuk menampilkannya kepada pengguna dan Pengontrol berkaitan dengan input pengguna. Apa yang saya tidak yakin adalah apa yang ada di Controller.

Katakanlah misalnya saya memiliki aplikasi yang cukup sederhana (saya secara khusus berpikir Java, tapi saya kira prinsip yang sama berlaku di tempat lain). Saya mengatur kode saya ke dalam 3 paket yang disebut app.model, app.viewdan app.controller.

Di dalam app.modelpaket, saya memiliki beberapa kelas yang mencerminkan perilaku aplikasi yang sebenarnya. Ini extends Observabledan gunakan setChanged()dan notifyObservers()untuk memicu pandangan untuk memperbarui bila perlu.

The app.viewpaket memiliki kelas (atau beberapa kelas untuk berbagai jenis display) yang menggunakan javax.swingkomponen untuk menangani layar. Beberapa komponen ini perlu dimasukkan kembali ke dalam Model. Jika saya mengerti dengan benar, View seharusnya tidak ada hubungannya dengan umpan balik - yang harus ditangani oleh Controller.

Jadi apa yang sebenarnya saya masukkan ke Controller? Apakah saya menempatkan tampilan public void actionPerformed(ActionEvent e)dalam hanya dengan panggilan ke metode di Controller? Jika demikian, haruskah validasi dll dilakukan di Controller? Jika demikian, bagaimana cara saya memberi umpan balik pesan kesalahan kembali ke Tampilan - haruskah itu melalui Model lagi, atau haruskah Pengontrol langsung mengirimkannya kembali ke Lihat?

Jika validasi dilakukan di View, apa yang harus saya tempatkan di Controller?

Maaf untuk pertanyaan panjang, saya hanya ingin mendokumentasikan pemahaman saya tentang proses dan mudah-mudahan seseorang dapat mengklarifikasi masalah ini untuk saya!

Paul Walker
sumber

Jawaban:

520

Dalam contoh yang Anda sarankan, Anda benar: "pengguna mengklik tombol 'hapus item ini'" di antarmuka pada dasarnya hanya memanggil fungsi "hapus" pengontrol. Pengontrol, bagaimanapun, tidak tahu seperti apa tampilan itu, dan karenanya pandangan Anda harus mengumpulkan beberapa informasi seperti, "item mana yang diklik?"

Dalam bentuk percakapan:

Lihat : "Hei, controller, pengguna baru saja mengatakan dia ingin item 4 dihapus."
Pengendali : "Hmm, setelah memeriksa kredensial, dia diizinkan untuk melakukan itu ... Hei, model, saya ingin Anda mendapatkan item 4 dan melakukan apa pun yang Anda lakukan untuk menghapusnya."
Model : "Item 4 ... mengerti. Itu dihapus. Kembali kepada Anda, Controller."
Pengendali : "Ini, saya akan mengumpulkan set data baru. Kembali kepada Anda, lihat."
Lihat : "Keren, saya akan menunjukkan set baru kepada pengguna sekarang."

Di akhir bagian itu, Anda memiliki opsi: tampilan dapat membuat permintaan terpisah, "berikan saya set data terbaru", dan dengan demikian lebih murni, atau controller secara implisit mengembalikan set data baru dengan "hapus " operasi.

Andres Jaan Tack
sumber
90
Dialog itu adalah penjelasan terbaik dari MVC yang pernah saya temui, terima kasih!
Paul Walker
13
Semua baik-baik saja, tetapi tidak ada yang salah dengan tampilan pembacaan dari model secara langsung. "Pengontrol bukan polisi data". Ada juga doktrin yang mengatakan untuk menjaga pengendali tetap tipis. View Helpers adalah tempat yang sempurna untuk mengumpulkan data yang siap dikonsumsi oleh view Anda. Seseorang seharusnya tidak harus mengirim tumpukan pengontrol lengkap untuk menggunakan kembali beberapa logika akses data. Lebih detail: rmauger.co.uk/2009/03/...
Pengecualian
1
Saya setuju dengan "Pengecualian e". Data dalam model dapat diperbarui oleh banyak peristiwa, tidak harus pengendali, dan oleh karena itu dalam beberapa desain MVC M memberi sinyal pada V bahwa data tersebut kotor dan V dapat menyegarkan sendiri. C tidak memiliki peran untuk dimainkan dalam kasus itu.
Mishax
68

Masalahnya MVCadalah bahwa orang berpikir pandangan, pengontrol, dan model harus se independen mungkin dari satu sama lain. Mereka tidak - pandangan dan pengontrol sering terjalin - menganggapnya sebagai M(VC).

Kontroler adalah mekanisme input antarmuka pengguna, yang sering terjerat dalam tampilan, terutama dengan GUI. Meskipun demikian, view adalah output dan controller adalah input. Suatu tampilan seringkali dapat bekerja tanpa pengontrol yang sesuai, tetapi sebuah pengontrol biasanya jauh lebih tidak berguna tanpa suatu penglihatan. Pengontrol yang ramah pengguna menggunakan tampilan untuk menafsirkan input pengguna dengan cara yang lebih bermakna, intuitif. Inilah yang membuatnya sulit memisahkan konsep pengontrol dari tampilan.

Pikirkan robot yang dikendalikan radio pada bidang deteksi dalam kotak tertutup sebagai model.

Modelnya adalah semua tentang transisi keadaan dan keadaan tanpa konsep keluaran (tampilan) atau apa yang memicu transisi keadaan. Saya bisa mendapatkan posisi robot di lapangan dan robot itu tahu bagaimana melakukan transisi posisi (mengambil langkah maju / mundur / kiri / kanan. Mudah untuk membayangkan tanpa tampilan atau pengontrol, tetapi tidak ada gunanya

Pikirkan sebuah tampilan tanpa pengontrol, mis. Seseorang di ruangan lain di jaringan di ruangan lain mengawasi posisi robot sebagai (x, y) koordinat yang mengalir di konsol gulir. Pandangan ini hanya menampilkan keadaan model, tetapi orang ini tidak memiliki pengontrol. Sekali lagi, mudah untuk membayangkan tampilan ini tanpa pengontrol.

Pikirkan pengontrol tanpa melihat, misalnya seseorang terkunci di lemari dengan pengontrol radio disetel ke frekuensi robot. Pengontrol ini mengirimkan input dan menyebabkan transisi status tanpa tahu apa yang mereka lakukan pada model (jika ada). Mudah dibayangkan, tetapi tidak terlalu berguna tanpa semacam umpan balik dari tampilan.

Sebagian besar UI yang ramah pengguna mengoordinasikan tampilan dengan pengontrol untuk menyediakan antarmuka pengguna yang lebih intuitif. Misalnya, bayangkan tampilan / pengontrol dengan layar sentuh yang menunjukkan posisi robot saat ini dalam 2-D dan memungkinkan pengguna untuk menyentuh titik di layar yang kebetulan berada di depan robot. Pengontrol membutuhkan detail tentang tampilan, misalnya posisi dan skala viewport, dan posisi piksel tempat yang disentuh relatif terhadap posisi piksel robot di layar) untuk menafsirkan ini dengan benar (tidak seperti orang yang terkunci di dalam lemari dengan pengontrol radio).

Sudahkah saya menjawab pertanyaan Anda? :-)

Pengontrol adalah segala sesuatu yang mengambil input dari pengguna yang digunakan untuk menyebabkan model ke keadaan transisi. Cobalah untuk menjaga pandangan dan pengontrol tetap terpisah, tetapi sadari bahwa mereka sering saling bergantung satu sama lain, jadi tidak apa-apa jika batas di antara keduanya tidak jelas, yaitu memiliki tampilan dan pengontrol sebagai paket terpisah mungkin tidak sebersih yang Anda inginkan. suka, tapi tidak apa-apa. Anda mungkin harus menerima bahwa pengontrol tidak akan dipisahkan dengan jelas dari tampilan karena tampilan dari model.

... haruskah validasi dll dilakukan di Controller? Jika demikian, bagaimana cara saya memberi umpan balik pesan kesalahan kembali ke Tampilan - haruskah itu melalui Model lagi, atau haruskah Pengontrol langsung mengirimkannya kembali ke Lihat?

Jika validasi dilakukan di View, apa yang harus saya tempatkan di Controller?

Saya mengatakan bahwa view dan controller yang terhubung harus berinteraksi secara bebas tanpa melalui model. Kontroler mengambil input pengguna dan harus melakukan validasi (mungkin menggunakan informasi dari model dan / atau tampilan), tetapi jika validasi gagal, controller harus dapat memperbarui tampilan terkait secara langsung (misalnya pesan kesalahan).

Tes asam untuk ini adalah dengan bertanya pada diri sendiri apakah pandangan independen (yaitu pria di ruangan lain mengawasi posisi robot melalui jaringan) harus melihat sesuatu atau tidak sebagai akibat dari kesalahan validasi orang lain (misalnya pria di lemari mencoba memberitahu robot untuk turun lapangan). Secara umum, jawabannya adalah tidak - kesalahan validasi mencegah transisi keadaan. Jika tidak ada keadaan transisi (robot tidak bergerak), tidak perlu memberi tahu pandangan lain. Lelaki di lemari hanya tidak mendapatkan umpan balik bahwa ia mencoba menyebabkan transisi ilegal (tanpa tampilan - antarmuka pengguna yang buruk), dan tidak ada orang lain yang perlu mengetahuinya.

Jika pria dengan layar sentuh mencoba mengirim robot keluar lapangan, dia mendapat pesan ramah pengguna yang meminta agar dia tidak membunuh robot dengan mengirimkannya dari bidang deteksi, tetapi sekali lagi, tidak ada orang lain yang perlu mengetahui hal ini.

Jika pandangan lain memang perlu tahu tentang kesalahan ini, maka Anda secara efektif mengatakan bahwa input dari pengguna dan kesalahan yang dihasilkan adalah bagian dari model dan semuanya sedikit lebih rumit ...

Bert F
sumber
23

Berikut adalah artikel bagus tentang dasar-dasar MVC.

Ini menyatakan ...

Kontroler - Kontroler menerjemahkan interaksi dengan tampilan menjadi tindakan yang harus dilakukan oleh model.

Dengan kata lain, logika bisnis Anda. Kontroler merespons tindakan yang diambil pengguna dalam tampilan dan merespons. Anda meletakkan validasi di sini dan pilih tampilan yang sesuai jika validasi gagal atau berhasil (halaman kesalahan, kotak pesan, apa pun).

Ada artikel bagus lainnya di Fowler .

JP Alioto
sumber
MVP adalah opsi lain yang dibahas dalam artikel yang Anda referensi, lihat martinfowler.com/eaaDev/ModelViewPresenter.html
Jon
Terima kasih atas tautannya, mereka pasti membuat bacaan yang menarik.
Paul Walker
18

Pola MVC hanya ingin Anda memisahkan presentasi (= tampilan) dari logika bisnis (= model). Bagian pengontrol ada hanya untuk menimbulkan kebingungan.

Dimitri C.
sumber
1
Persis, apa yang selalu saya pikirkan sampai sekarang tetapi tidak pernah punya keberanian untuk memberi tahu siapa pun .... atau mungkin tidak dapat menghasilkan kata-kata yang tepat.
user1451111
1
Model-View-Confusion
Raining
10

Secara praktis, saya tidak pernah menemukan konsep controller menjadi sangat berguna. Saya menggunakan model ketat / pemisahan tampilan dalam kode saya tetapi tidak ada controller yang jelas. Tampaknya menjadi abstraksi yang tidak perlu.

Secara pribadi, MVC full-blown nampak seperti pola desain pabrik karena mudah mengarah ke desain yang membingungkan dan terlalu rumit. Jangan menjadi astronot arsitektur .

John Kugelman
sumber
9

Berdasarkan pertanyaan Anda, saya mendapat kesan bahwa Anda agak kabur tentang peran Model. Model terpaku pada data yang terkait dengan aplikasi; jika aplikasi memiliki database, tugas Model adalah berbicara dengannya. Ini juga akan menangani logika sederhana apa pun yang terkait dengan data itu; jika Anda memiliki aturan yang mengatakan bahwa untuk semua kasus di mana TABLE.foo == "Hore!" dan TABLE.bar == "Huzzah!" kemudian atur TABLE.field = "W00t!", maka Anda menginginkan Model untuk mengatasinya.

Pengontrol adalah yang seharusnya menangani sebagian besar perilaku aplikasi. Jadi untuk menjawab pertanyaan Anda:

Apakah saya meletakkan public void actionPerformed (ActionEvent e) di View hanya dengan pemanggilan metode di Controller?

Saya akan mengatakan tidak. Saya akan mengatakan bahwa harus hidup di Controller; Lihat harus hanya memberi makan data yang datang dari antarmuka pengguna ke Controller, dan biarkan Controller memutuskan metode mana yang harus dipanggil sebagai respons.

Jika demikian, haruskah validasi dll dilakukan di Controller?

Sebagian besar validasi Anda benar-benar harus dilakukan oleh Pengendali; harus menjawab pertanyaan apakah data tersebut valid atau tidak, dan jika tidak, masukkan pesan kesalahan yang sesuai ke tampilan. Dalam praktiknya, Anda dapat memasukkan beberapa pemeriksaan kewarasan sederhana ke dalam layer View demi meningkatkan pengalaman pengguna. (Saya terutama memikirkan lingkungan web, tempat Anda mungkin ingin memiliki pesan kesalahan muncul saat pengguna mengklik "Kirim" daripada menunggu seluruh pengiriman -> proses -> memuat siklus halaman sebelum memberi tahu mereka bahwa mereka mengacaukan .) Hati-hati; Anda tidak ingin menduplikasi usaha lebih dari yang Anda harus, dan di banyak lingkungan (sekali lagi, saya memikirkan web) Anda sering harus memperlakukan data yang datang dari antarmuka pengguna sebagai paket kotor kotor terletak sampai kamu

Jika demikian, bagaimana cara saya memberi umpan balik pesan kesalahan kembali ke Tampilan - haruskah itu melalui Model lagi, atau haruskah Pengontrol langsung mengirimkannya kembali ke Lihat?

Anda harus memiliki beberapa protokol yang diatur di mana View tidak perlu tahu apa yang terjadi selanjutnya sampai Controller mengatakannya. Layar apa yang Anda tunjukkan setelah pengguna memukul tombol itu? View mungkin tidak tahu, dan Controller mungkin tidak tahu sampai melihat data yang baru saja didapat. Bisa jadi "Buka layar lain ini, seperti yang diharapkan" atau "Tetap di layar ini, dan tampilkan pesan kesalahan ini".

Dalam pengalaman saya, komunikasi langsung antara Model dan Tampilan harus sangat, sangat terbatas, dan Tampilan tidak boleh secara langsung mengubah data Model mana pun; itu seharusnya menjadi pekerjaan Pengendali.

Jika validasi dilakukan di View, apa yang harus saya tempatkan di Controller?

Lihat di atas; validasi sebenarnya harus di Controller. Dan semoga Anda memiliki ide tentang apa yang harus dimasukkan ke Controller sekarang. :-)

Perlu dicatat bahwa semuanya bisa sedikit buram di tepinya; seperti kebanyakan hal serumit rekayasa perangkat lunak, panggilan penilaian akan berlimpah. Cukup gunakan penilaian terbaik Anda, cobalah untuk tetap konsisten dalam aplikasi ini, dan cobalah menerapkan pelajaran yang Anda pelajari ke proyek berikutnya.

BlairHippo
sumber
7

Controller benar-benar bagian dari View. Tugasnya adalah untuk mencari tahu layanan mana yang diperlukan untuk memenuhi permintaan, nilai-nilai tidak jelas dari View menjadi objek yang diperlukan antarmuka layanan, menentukan Tampilan berikutnya, dan menyusun respons kembali ke bentuk yang dapat digunakan Tampilan berikutnya. . Ini juga menangani pengecualian apa pun yang dilemparkan dan mengubahnya menjadi Tampilan yang dapat dipahami pengguna.

Lapisan layanan adalah hal yang mengetahui kasus penggunaan, unit kerja, dan objek model. Pengontrol akan berbeda untuk setiap jenis tampilan - Anda tidak akan memiliki pengontrol yang sama untuk desktop, berbasis browser, Flex, atau UI seluler. Jadi saya katakan itu benar-benar bagian dari UI.

Berorientasi layanan: di situlah pekerjaan dilakukan.

Duffymo
sumber
3

Controller terutama untuk koordinasi antara tampilan dan model.

Sayangnya, kadang-kadang berakhir berbaur dengan tampilan - dalam aplikasi kecil meskipun ini tidak terlalu buruk.

Saya sarankan Anda meletakkan:

public void actionPerformed(ActionEvent e)

di controller. Maka pendengar tindakan Anda dalam tampilan Anda harus mendelegasikan ke controller.

Adapun bagian validasi, Anda dapat meletakkannya di tampilan atau controller, saya pribadi pikir itu milik controller.

Saya pasti akan merekomendasikan untuk melihat Passive View dan Supervising Presenter (yang pada dasarnya adalah apa yang dipecah menjadi Model View Presenter - setidaknya oleh Fowler). Lihat:

http://www.martinfowler.com/eaaDev/PassiveScreen.html

http://www.martinfowler.com/eaaDev/SupervisingPresenter.html

Jon
sumber
3

Berikut ini adalah aturan praktis yang saya gunakan: jika ini adalah prosedur yang akan saya gunakan khusus untuk tindakan pada halaman ini , itu termasuk dalam controller, bukan model. Model harus hanya menyediakan abstraksi yang koheren untuk penyimpanan data.

Saya datang dengan ini setelah bekerja dengan webapp ish besar yang ditulis oleh pengembang yang berpikir mereka mengerti MVC tetapi sebenarnya tidak. "Pengontrol" mereka direduksi menjadi delapan baris metode panggilan kelas statis yang biasanya tidak disebut di tempat lain: - / membuat model mereka sedikit lebih dari cara membuat ruang nama. Refactoring ini benar melakukan tiga hal: menggeser semua SQL ke dalam lapisan akses data (alias model), membuat kode pengontrol sedikit lebih verbose tetapi jauh lebih dimengerti, dan mengurangi file "model" lama menjadi tidak ada. :-)

ahli statika
sumber
1

juga perhatikan bahwa setiap widget Swing dapat dianggap mengandung tiga komponen MVC: masing-masing memiliki Model (yaitu ButtonModel), Tampilan (BasicButtonUI), dan Kontrol (JButton sendiri).

akf
sumber
1

Anda pada dasarnya benar tentang apa yang Anda masukkan ke dalam controller. Ini adalah satu-satunya cara Model berinteraksi dengan Tampilan. Actionperformed dapat ditempatkan di View, tetapi fungsi sebenarnya dapat ditempatkan di kelas lain yang akan bertindak sebagai Controller. Jika Anda akan melakukan ini, saya sarankan melihat ke dalam pola Command, yang merupakan cara abstrak semua perintah yang memiliki penerima yang sama. Maaf atas penyimpangannya.

Bagaimanapun, implementasi MVC yang tepat hanya akan memiliki interaksi berikut ini: Model -> View View -> Controller Controller -> View

Satu-satunya tempat di mana mungkin ada interaksi lain adalah jika Anda menggunakan pengamat untuk memperbarui tampilan, maka tampilan perlu meminta pengontrol untuk informasi yang dibutuhkan.

mnuzzo
sumber
0

Seperti yang saya pahami, Pengendali menerjemahkan dari tindakan antarmuka pengguna ke tindakan tingkat aplikasi. Misalnya, dalam permainan video Controller mungkin menerjemahkan "memindahkan mouse begitu banyak piksel" ke "ingin melihat ke arah ini dan itu. Dalam aplikasi CRUD, terjemahan mungkin" diklik pada tombol ini dan itu "untuk "cetak benda ini", tetapi konsepnya sama.

David Seiler
sumber
0

Kami melakukannya dengan demikian, menggunakan Pengontrol terutama untuk menangani dan bereaksi terhadap input / tindakan yang didorong oleh pengguna (dan _Logic untuk semua hal lain, kecuali tampilan, data, dan hal-hal _Model yang jelas):

(1) (respon, reaksi - apa yang webapp "lakukan" dalam menanggapi pengguna) Blog_Controller

-> main ()

-> handleSubmit_AddNewCustomer ()

-> memverifikasiUser_HasProperAuth ()

(2) (logika "bisnis", apa dan bagaimana webapp "berpikir") Blog_Logic

-> sanityCheck_AddNewCustomer ()

-> handleUsernameChange ()

-> sendEmail_NotifyRequestedUpdate ()

(3) (tampilan, portal, bagaimana aplikasi web "muncul") Blog_View

-> genWelcome ()

-> genForm_AddNewBlogEntry ()

-> genPage_DataEntryForm ()

(4) (hanya objek data, diperoleh dalam _ construct () dari setiap Blog kelas *, digunakan untuk menyimpan semua data webapp / memori bersama sebagai satu objek) Blog_Meta

(5) (lapisan data dasar, baca / tulis ke DB) Blog_Model

-> saveDataToMemcache ()

-> saveDataToMongo ()

-> saveDataToSql ()

-> loadData ()

Kadang-kadang kita menjadi sedikit bingung di mana harus meletakkan metode, di C atau L. Tapi Model itu solid, jernih, dan karena semua data dalam-memori berada di _Meta, itu tidak ada otak di sana, juga . Lompatan maju terbesar kami adalah mengadopsi penggunaan _Meta, karena hal ini menghapus semua kesalahan dari berbagai objek _C, _L dan _Model, menjadikan semuanya mudah dikelola secara mental, ditambah, dalam satu gerakan, itu memberi kami apa yang ada disebut "Dependency Injection", atau cara untuk menyebarkan seluruh lingkungan bersama dengan semua data (yang bonusnya adalah pembuatan lingkungan "tes" yang mudah).

FYA
sumber