Що входить у “Контролер” у “MVC”?


186

Я думаю, що я розумію основні поняття MVC - Модель містить дані та поведінку програми, View відповідає за її відображення користувачеві, а Controller займається введенням користувача. У чому я не впевнений - це саме те , що йде в Контролер.

Скажімо, наприклад, у мене досить просте додаток (я спеціально думаю Java, але я вважаю, що ті ж принципи діють і в інших місцях). Я впорядковую свій код в 3 пакети, що називаються app.model, app.viewі app.controller.

У межах app.modelпакету у мене є кілька класів, які відображають реальну поведінку програми. Вони extends Observableтакож використовують setChanged()і notifyObservers()запускають подання для оновлення, коли це доречно.

У app.viewпакеті є клас (або кілька класів для різних типів відображення), який використовує javax.swingкомпоненти для обробки дисплея. Деякі з цих компонентів потрібно повернути в модель. Якщо я правильно розумію, Перегляд не повинен мати нічого спільного із зворотним зв'язком - цим повинен займатися Контролер.

Отже, що я насправді поміщаю в контролер? Чи потрібно помістити public void actionPerformed(ActionEvent e)в «Вид» просто виклик методу в контролері? Якщо так, чи слід проводити будь-яку перевірку тощо в Контролері? Якщо так, то як я повертаю повідомлення про помилки назад до Перегляду - чи повинні вони повторно пройти через Модель, або ж Контролер повинен просто повернути його назад до Перегляду?

Якщо перевірка виконана в представленні, що я можу вставити в контролер?

Вибачте за довге запитання, я просто хотів задокументувати своє розуміння процесу і, сподіваюся, хтось може роз'яснити це питання для мене!

Відповіді:


520

У запропонованому вами прикладі ви маєте рацію: "користувач натиснув кнопку" видалити цей елемент "в інтерфейсі, в основному слід просто викликати функцію" видалити "контролера. Однак контролер поняття не має, як виглядає вид, і тому ваш погляд повинен збирати інформацію, наприклад, "на який елемент було натиснуто?"

У формі розмови:

Вид : "Ей, контролер, користувач щойно сказав мені, що він хоче видалити пункт 4".
Контролер : "Гм, перевіривши свої повноваження, йому дозволяється це зробити ... Ей, модель, я хочу, щоб ти взяв пункт 4 і зробив усе, що ти робиш, щоб видалити його".
Модель : "Елемент 4 ... отримав його. Він видалений. Назад до вас, контролер."
Контролер : "Тут я зберу новий набір даних. Назад до вас, перегляньте."
Вид : "Класно, я зараз покажу користувачеві новий набір".

Наприкінці цього розділу у вас є можливість: або представлення може зробити окремий запит, "надіслати мені останній набір даних", і таким чином, бути більш чистим, або контролер неявно повертає новий набір даних із "видалити "операція.


90
Цей діалог - найкраще пояснення MVC, з яким я зіткнувся, дякую!
Пол Уокер

13
Все нормально, але немає нічого поганого в тому, що вигляд читається безпосередньо з моделі. "Контролери - це не поліція даних". Існує також вчення, яке говорить про те, щоб стримувати контролерів. Переглядачі помічників - ідеальне місце для збору даних, готових до споживання вашим оглядом. Не слід відправляти повний стек контролера, щоб повторно використовувати деяку логіку доступу до даних. Більш детально: rmauger.co.uk/2009/03/…
виняток e

1
Я погоджуюсь з "Винятком е". Дані в моделі можуть бути оновлені багатьма подіями, не обов'язково контролером, і тому в деяких моделях MVC M сигналює V, що дані брудні і V може оновлюватись. У цьому випадку C не грає жодної ролі.
Мішакс

68

Проблема MVCполягає в тому, що люди думають, що погляд, контролер і модель повинні бути максимально незалежними один від одного. Вони ні - погляд і контролер часто переплітаються - думають про це як M(VC).

Контролер - це механізм введення користувальницького інтерфейсу, який часто переплутується у зображенні, особливо з графічними інтерфейсами. Тим не менш, вигляд - це вихід, а контролер - вхід. Перегляд часто може працювати без відповідного контролера, але контролер, як правило, набагато менш корисний без перегляду. Зручні користувачі-контролери використовують погляд, щоб інтерпретувати вклад користувача в більш змістовному, інтуїтивно зрозумілому вигляді. Саме це ускладнює відокремлення концепції контролера від погляду.

Подумайте про радіокерований робот на полі виявлення в герметичній коробці як модель.

Ця модель стосується переходів стану та стану, без поняття виведення (відображення) або того, що викликає переходи стану. Я можу отримати позицію робота на полі, і робот знає, як перейти в положення (зробіть крок вперед / назад / вліво / вправо. Легко передбачити без виду або контролера, але нічого корисного не робить

Подумайте про вид без контролера, наприклад, хтось в іншій кімнаті в мережі в іншій кімнаті, спостерігаючи за положенням робота, як (x, y) координати, що протікають вниз консолі прокрутки. Цей погляд просто відображає стан моделі, але у цього хлопця немає контролера. Знову ж таки, легко уявити собі цей погляд без контролера.

Подумайте про контролер без виду, наприклад, хтось замкнений у шафі з радіоконтролером, налаштованим на частоту робота. Цей контролер надсилає вхід і викликає переходи стану, не маючи уявлення про те, що вони роблять для моделі (якщо вона є). Легко передбачити, але не дуже корисно без якогось зворотного зв'язку з погляду.

Більшість зручних користувальницьких інтерфейсів узгоджують подання з контролером, щоб забезпечити більш інтуїтивний інтерфейс користувача. Наприклад, уявіть собі перегляд / контролер з сенсорним екраном, що показує поточне положення робота в 2-D і дозволяє користувачеві торкатися точки на екрані, яка щойно перебуває перед роботом. Контролеру потрібні детальні відомості про вид, наприклад, положення та масштаб вікна перегляду, а також положення пікселя місця, яке торкнулося відносно положення пікселя робота на екрані), щоб інтерпретувати це правильно (на відміну від хлопця, зачиненого в шафі з радіоконтролер).

Я вже відповів на ваше запитання? :-)

Контролер - це все, що приймає дані від користувача, що використовується для приведення моделі в перехідний стан. Постарайтеся, щоб вигляд і контролер були відокремленими, але розумійте, що вони часто взаємозалежні один від одного, тож добре, якщо межа між ними нечітка, тобто мати перегляд і контролер як окремі пакети, можливо, не так чітко, як ви б розділили як, але це нормально. Можливо, вам доведеться прийняти, що контролер не буде чітко відокремлений від подання, оскільки подання від моделі.

... чи слід проводити перевірку тощо у контролері? Якщо так, то як я повертаю повідомлення про помилки назад до Перегляду - чи повинні вони повторно пройти через Модель, або ж Контролер повинен просто повернути його назад до Перегляду?

Якщо перевірка виконана в представленні, що я можу вставити в контролер?

Я кажу, що пов'язаний погляд і контролер повинні вільно взаємодіяти, не переходячи через модель. Контролер приймає введення користувача і повинен виконати перевірку (можливо, використовуючи інформацію з моделі та / або перегляду), але якщо перевірка не вдалася, контролер повинен мати можливість оновити пов'язаний з ним перегляд (наприклад, повідомлення про помилку).

Тест на кислотність для цього полягає в тому, щоб запитати себе в тому, чи повинен незалежний погляд (тобто хлопець в іншій кімнаті, який спостерігає за робочим положенням через мережу) бачити щось чи ні внаслідок чужої помилки перевірки (наприклад, хлопця в шафі спробував сказати роботові піти з поля). Як правило, відповідь - ні - помилка перевірки перешкоджала переходу стану. Якщо не було державних потягів (робот не рухався), немає потреби розповідати інші погляди. Хлопець у шафі просто не отримав жодного відгуку про те, що він намагався викликати незаконний перехід (немає виду - поганий користувальницький інтерфейс), і нікому більше не потрібно цього знати.

Якщо хлопець із сенсорним екраном спробував відправити робота з поля, йому надійшло приємне користувачеві повідомлення з проханням не вбивати робота, відправляючи його з поля виявлення, але знову ж таки, ніхто більше не повинен цього знати.

Якщо інші види дійсно потрібно знати про ці помилки, то ви фактично говорите , що входи від користувача і будь-які результуючі помилки частину моделі , і все це є трохи більш складним ...


23

Ось хороша стаття з основ MVC.

У ньому зазначено ...

Контролер - контролер переводить взаємодію з видом на дії, які слід виконати моделлю.

Іншими словами, ваша логіка бізнесу. Контролер реагує на дії користувача, зроблені у перегляді, і реагує. Ви розміщуєте тут валідацію і вибираєте відповідний вид, якщо перевірка завершилася невдалою або успішною (сторінка помилок, поле повідомлень, будь-що інше).

Є ще одна хороша стаття про Фоулера .


MVP інший варіант обговорюється в статті ви посилаєтеся см martinfowler.com/eaaDev/ModelViewPresenter.html
Джон

Дякую за посилання, вони, безумовно, цікаві для читання.
Пол Уокер

18

Шаблон MVC просто хоче, щоб ви відділили презентацію (= перегляд) від бізнес-логіки (= модель). Частина контролера існує лише для того, щоб викликати плутанину.


1
Точно те, про що я завжди думав, але ніколи не мав сміливості сказати комусь .... або, можливо, не міг придумати належних слів.
user1451111

1
Перегляд моделей-плутанина
дощ

10

Практично кажучи, я ніколи не вважав концепцію контролера особливо корисною. Я використовую в своєму коді суворе розділення моделі / перегляду, але немає чітко визначеного контролера. Здається, це зайва абстракція.

Особисто повномасштабний MVC здається фабричним дизайном, оскільки він легко призводить до заплутаного та надскладного дизайну. Не будьте космонавтом архітектури .


9

Виходячи з вашого запитання, у мене складається враження, що ви трохи туманні щодо ролі моделі. Модель фіксується на даних, пов’язаних із програмою; якщо у додатку є база даних, завданням моделі буде говорити з нею. Він також буде обробляти будь-яку просту логіку, пов'язану з цими даними; якщо у вас є правило, яке говорить, що для всіх випадків, коли TABLE.foo == "Ура!" і TABLE.bar == "Гузза!" потім встановіть TABLE.field = "W00t!", тоді ви хочете, щоб Модель подбала про це.

Контролер - це те, що має вирішувати основну частину поведінки програми. Отже, щоб відповісти на ваші запитання:

Чи потрібно помістити публічний недійсний actionPerformed (ActionEvent e) у Перегляд лише за допомогою виклику методу в Контролері?

Я б сказав, що ні. Я б сказав, що слід жити в Контролері; Перегляд повинен просто подати дані, що надходять з користувальницького інтерфейсу в Контролер, і дозволити Контролеру вирішити, які методи слід викликати у відповідь.

Якщо так, чи слід проводити будь-яку перевірку тощо в Контролері?

Основну частину вашої перевірки дійсно повинен здійснити Контролер; він повинен відповісти на питання про те, чи дійсні дані чи ні, і якщо їх немає, подайте відповідні повідомлення про помилки в Перегляд. На практиці ви можете включити кілька простих перевірок на корисність у шар Перегляду для покращення роботи користувачів. (Я думаю, перш за все, про веб-середовища, де, можливо, ви хочете, щоб повідомлення про помилку спливало в момент, коли користувач натисне "Надіслати", а не чекати, коли весь цикл подання -> процес -> завантажить, перш ніж сказати їм, що вони накручені .) Просто будьте обережні; ви не хочете дублювати зусилля більше, ніж вам доводиться, і в багатьох середовищах (знову ж, я думаю про Інтернет) вам часто доводиться трактувати будь-які дані, що надходять з користувальницького інтерфейсу, як пакет брудних брудних лежить, поки ти '

Якщо так, то як я можу повернути повідомлення про помилки назад до Перегляду - чи повинні вони повторно пройти через Модель, або ж Контролер повинен просто повернути його назад до Перегляду?

У вас повинен бути налаштований протокол, де View не обов'язково знає, що буде далі, поки контролер не скаже про це. На якому екрані ви їх показуєте, коли користувач натискає цю кнопку? Перегляд може не знати, і Контролер може не знати жодного, доки він не перегляне дані, які тільки що отримав. Це може бути "Перейти до цього іншого екрану, як очікувалося" або "Залишайтеся на цьому екрані та відображайте це повідомлення про помилку".

На мій досвід, прямий зв'язок між Моделею та Поглядом повинен бути дуже, дуже обмеженим, і Вид не повинен прямо змінювати будь-які дані Моделі; це має бути завданням контролера.

Якщо перевірка виконана в представленні, що я можу вставити в контролер?

Дивись вище; реальна перевірка повинна бути в Контролері. І, сподіваємось, у вас є якесь уявлення про те, що вже слід поставити в контролер. :-)

Варто зазначити, що все це може трохи розмитися по краях; як і у більшості всього, що є таким складним, як інженерія програмного забезпечення, виклики суджень будуть рясні. Просто використовуйте найкраще рішення, намагайтеся залишатися послідовними в цьому додатку та намагайтеся застосувати уроки, які ви дізнаєтесь, до наступного проекту.


7

Контролер дійсно є частиною Погляду. Його завдання полягає в тому, щоб визначити, які послуги потрібні для виконання запиту, не маршальовані значення з перегляду об'єктів, необхідних для інтерфейсу служби, визначити наступний перегляд та перевести відповідь назад у форму, яку може використовувати наступний перегляд . Він також обробляє будь-які викиди, які викидаються, і робить їх переглядами, які користувачі можуть зрозуміти.

Сервісний рівень - це те, що знає випадки використання, одиниці роботи та модельні об’єкти. Контролер буде різним для кожного типу перегляду - у вас не буде одного і того ж контролера для настільних, браузерних, Flex або мобільних інтерфейсів. Тому я кажу, що це дійсно частина інтерфейсу користувача.

Сервіс-орієнтований: саме тут і робиться робота.


3

Контролер в першу чергу призначений для координації між видом і моделлю.

На жаль, іноді це піддається змішуванню з поданням - у невеликих додатках, хоча це не дуже погано.

Я пропоную вам поставити:

public void actionPerformed(ActionEvent e)

в контролері. Тоді ваш слухач дій, на ваш погляд, повинен делегувати контролер.

Що стосується валідаційної частини, ви можете помістити її у подання чи контролер, я особисто думаю, що вона належить до контролера.

Я б напевно рекомендував поглянути на пасивний перегляд та нагляд за презентатором (що по суті є тим, на що розбито Presenter Model View - принаймні Фоулером). Побачити:

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

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


3

Ось головне правило, яке я використовую: якщо це процедура, яку я буду використовувати спеціально для дії на цій сторінці, вона належить до контролера, а не до моделі. Ця модель повинна забезпечувати лише узгоджене абстрагування для зберігання даних.

Я придумав це після роботи з великим веб-сайтом, написаним розробниками, які думали, що вони розуміють MVC, але насправді цього не роблять. Їх "контролери" зводяться до восьми рядків виклику методів статичного класу, які зазвичай не називаються більше ніде: - / роблячи свої моделі трохи більше, ніж способи створення просторів імен. Рефакторинг цього належним чином робить три речі: переміщує весь SQL у рівень доступу до даних (він же модель), робить код контролера дещо більш детальним, але набагато зрозумілішим і зводить старі файли «моделі» ні до чого. :-)


1

також зауважте, що кожен віджет Swing може вважати, що він містить три компоненти MVC: кожен має модель (тобто ButtonModel), вид (BasicButtonUI) та управління (сам JButton).


1

Ви по суті маєте рацію щодо того, що ставите в контролер. Це єдиний спосіб, коли Модель повинна взаємодіяти з Видом. Виконання дій може бути розміщено у представленні, але фактична функціональність може бути розміщена в іншому класі, який буде виконувати роль контролера. Якщо ви збираєтесь це робити, рекомендую вивчити шаблон Command, який є способом абстрагування всіх команд, що мають однаковий приймач. Вибачте за відступ.

У будь-якому випадку, належна реалізація MVC матиме лише такі взаємодії: Модель -> Вид подання -> Контролер контролера -> Перегляд

Єдине місце, де може бути інша взаємодія, якщо ви використовуєте спостерігача для оновлення Перегляду, то Перегляд повинен буде запитати у Контролера інформацію, яка йому потрібна.


0

Як я розумію, Контролер перекладає дії користувача з інтерфейсу в дії на рівні додатків. Наприклад, у відеоігри контролер може перевести "перемістив мишу стільки пікселів" на "хоче подивитися в такому і такому напрямку. У додатку CRUD переклад може бути" натиснутий на таку і таку кнопку " "надрукуйте цю річ", але концепція та сама.


0

Ми робимо це таким чином, використовуючи контролери в основному для обробки та реагування на керовані користувачем введення / дії (і _Logic на все інше, крім перегляду, даних та очевидних матеріалів _Model):

(1) (відповідь, реакція - те, що робить "webapp" у відповідь на користувача) Blog_Controller

-> головний ()

-> handleSubmit_AddNewCustomer ()

-> verifyUser_HasProperAuth ()

(2) ("бізнес" логіка, що і як веб-сайт "думає") Blog_Logic

-> sanityCheck_AddNewCustomer ()

-> handleUsernameChange ()

-> sendEmail_NotifyRequestedUpdate ()

(3) (перегляди, портали, як веб-сайт "з'являється") Blog_View

-> genWelcome ()

-> genForm_AddNewBlogEntry ()

-> genPage_DataEntryForm ()

(4) (лише об'єкт даних, придбаний у _ construct () кожного класу Blog *, який використовується для збереження всіх даних webapp / пам’яті разом як один об’єкт) Blog_Meta

(5) (базовий рівень даних, читає / записує в БД) Blog_Model

-> saveDataToMemcache ()

-> saveDataToMongo ()

-> saveDataToSql ()

-> loadData ()

Іноді ми трохи заплутуємось у тому, куди поставити метод, на C або L. Але Модель є твердою, кришталево чистою, і оскільки всі дані в пам'яті знаходяться в _Meta, це теж не є мозком . Нашим найбільшим стрибком вперед було прийняття використання _Meta, до речі, оскільки це очистило всю сировину від різних об’єктів _C, _L та _Model, зробило все це подумки легким для управління, а також одним махом воно дало нам те, що є називається "Ін'єкція залежностей", або спосіб пройти навколо цілого середовища разом із усіма даними (чий бонус - це легке створення "тестового" середовища).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.