Что входит в «Контроллер» в «MVC»?

186

Я думаю, что понимаю основные концепции MVC - модель содержит данные и поведение приложения, представление отвечает за его отображение пользователю, а контроллер обрабатывает ввод данных пользователем. То, в чем я не уверен, это именно то , что происходит в контроллере.

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

Внутри app.modelпакета у меня есть несколько классов, которые отражают реальное поведение приложения. Они extends Observableи используют setChanged()и notifyObservers()для запуска представлений для обновления при необходимости.

app.viewПакет имеет класс (или несколько классов для различных типов дисплея) , которая использует javax.swingкомпоненты для обработки дисплея. Некоторые из этих компонентов должны быть возвращены в модель. Если я правильно понимаю, представление не должно иметь ничего общего с обратной связью - с этим должен справиться Контроллер.

Так, что я фактически помещаю в Контроллер? Поместить ли я public void actionPerformed(ActionEvent e)в представление только с помощью вызова метода в контроллере? Если да, должна ли проводиться какая-либо проверка и т. Д. В контроллере? Если да, то как я могу отправить сообщения об ошибках обратно в View - это должно пройти через Модель снова, или Контроллер должен просто отправить его обратно в View?

Если проверка выполняется в представлении, что я помещаю в контроллер?

Извините за длинный вопрос, я просто хотел документировать свое понимание процесса, и, надеюсь, кто-то может прояснить этот вопрос для меня!

Пауль Волкер
источник

Ответы:

520

В примере, который вы предложили, вы правы: «пользователь нажал кнопку« удалить этот элемент »в интерфейсе, в основном должен просто вызвать функцию контроллера« удалить ». Контроллер, однако, не имеет представления о том, как выглядит представление, и поэтому ваше представление должно собирать некоторую информацию, например, "какой элемент был нажат?"

В форме разговора:

Вид : «Эй, контроллер, пользователь только что сказал мне, что хочет удалить элемент 4».
Контролер : «Хм, проверив свои учетные данные, ему разрешено сделать это ... Эй, модель, я хочу, чтобы ты получил элемент 4 и сделал все, что ты делаешь, чтобы удалить его».
Модель : "Пункт 4 ... получил его. Он удален. Обратно к вам, Контроллер."
Контроллер : «Здесь я соберу новый набор данных. Обратно к вам, просмотр».
Вид : «Круто, я покажу новый набор пользователю сейчас».

В конце этого раздела у вас есть опция: либо представление может сделать отдельный запрос, «дать мне самый последний набор данных» и, следовательно, быть более чистым, либо контроллер неявно возвращает новый набор данных с помощью «delete» "операция.

Андрес Яан Тэк
источник
90
Этот диалог - лучшее объяснение MVC, с которым я сталкивался, спасибо!
Пол Уокер
13
Все хорошо, но нет ничего плохого в том, чтобы считывать данные с модели напрямую. «Контролеры не являются полицией данных». Существует также доктрина, которая говорит, чтобы держать контролеров тонкими. View Helpers - идеальное место для сбора данных, готовых к использованию вашим представлением. Не нужно отправлять весь стек контроллера, чтобы повторно использовать логику доступа к данным. Более подробно: rmauger.co.uk/2009/03/...
Exception е
1
Я согласен с «Исключением е». Данные в модели могут обновляться многими событиями, необязательно контроллером, и поэтому в некоторых конструкциях MVC М сигнализирует V, что данные грязные, и V может обновляться самостоятельно. C не играет никакой роли в этом случае.
Mishax
68

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

Контроллер является механизмом ввода пользовательского интерфейса, который часто запутывается в представлении, особенно в графических интерфейсах. Тем не менее, представление выводится, а контроллер - вводится. Представление может часто работать без соответствующего контроллера, но контроллер обычно гораздо менее полезен без представления. Удобные для пользователя контроллеры используют представление для интерпретации ввода пользователя более осмысленным и интуитивно понятным способом. Это то, что затрудняет отделение концепции контроллера от представления.

Представьте себе в качестве модели радиоуправляемого робота на поле обнаружения в запечатанном ящике.

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

Представьте себе вид без контроллера, например, кто-то в другой комнате в сети в другой комнате, наблюдая за положением робота, когда координаты (x, y) текут по консоли прокрутки. Это представление просто отображает состояние модели, но у этого парня нет контроллера. Опять же, легко представить это представление без контроллера.

Представьте себе контроллер без обзора, например, кто-то заперт в шкафу с радиоконтроллером, настроенным на частоту робота. Этот контроллер отправляет входные данные и вызывает переходы состояний, не имея представления о том, что они делают с моделью (если что-либо). Легко представить, но не очень полезно без какой-либо обратной связи с точки зрения.

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

Я уже ответил на ваш вопрос? :-)

Контроллер - это все, что требует ввода от пользователя и используется для перехода модели в состояние перехода. Постарайтесь держать представление и контроллер в отдельности, но понимайте, что они часто взаимозависимы друг с другом, так что все в порядке, если граница между ними нечеткая, т. Е. Представление вида и контроллера в качестве отдельных пакетов может быть не таким четким, как вы нравится, но это нормально. Возможно, вам придется согласиться с тем, что контроллер не будет чисто отделен от вида, поскольку вид от модели.

... должна ли проводиться какая-либо проверка и т. д. в контроллере? Если да, то как я могу отправить сообщения об ошибках обратно в View - это должно пройти через Модель снова, или Контроллер должен просто отправить его обратно в View?

Если проверка выполняется в представлении, что я помещаю в контроллер?

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

Кислотный тест для этого состоит в том, чтобы спросить себя, должно ли независимое представление (то есть парень в другой комнате, наблюдающий положение робота через сеть) видеть что-либо или нет в результате чьей-либо ошибки проверки (например, парень в шкафу) пытался сказать роботу сойти с поля). Обычно ответ отрицательный: ошибка проверки помешала переходу состояния. Если не было государственного перехода (робот не двигался), нет необходимости сообщать другим мнения. Парень из шкафа просто не получил никаких отзывов о том, что он пытался вызвать нелегальный переход (отсутствие просмотра - плохой пользовательский интерфейс), и никто другой не должен это знать.

Если парень с сенсорным экраном попытался отправить робота с поля, он получил приятное сообщение, в котором просил не убивать робота, отправив его за пределы поля обнаружения, но, опять же, больше никто не должен это знать.

Если другие виды действительно нужно знать об этих ошибках, то вы фактически говорите , что входы от пользователя и любые результирующие ошибки часть модели , и все это является немного более сложным ...

Берт F
источник
23

Вот хорошая статья об основах MVC.

Здесь утверждается ...

Контроллер - Контроллер преобразует взаимодействия с представлением в действия, которые должны быть выполнены моделью.

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

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

JP Alioto
источник
MVP - это еще один вариант, обсуждаемый в статье, на которую вы ссылаетесь, см. Martinfowler.com/eaaDev/ModelViewPresenter.html
Jon
Спасибо за ссылки, они, безусловно, делают для интересного чтения.
Пол Уокер
18

Шаблон MVC просто хочет, чтобы вы отделили представление (= представление) от бизнес-логики (= модель). Контроллер часть только для того, чтобы вызвать путаницу.

Димитрий С.
источник
1
Точно, что я всегда думал до сих пор, но никогда не набирался смелости сказать кому-либо ... или, может быть, не смог придумать подходящих слов.
user1451111
1
Модель-представление-путаница
дождь
10

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

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

Джон Кугельман
источник
9

Исходя из вашего вопроса, у меня складывается впечатление, что вы немного помутнели над ролью Модели. Модель фиксируется на данных, связанных с приложением; если у приложения есть база данных, работа модели будет заключаться в том, чтобы общаться с ней. Он также будет обрабатывать любую простую логику, связанную с этими данными; если у вас есть правило, которое говорит, что для всех случаев, когда TABLE.foo == "Ура!" и TABLE.bar == "Huzzah!" затем установите TABLE.field = "W00t!", тогда вы хотите, чтобы модель позаботилась об этом.

Контроллер - это то, что должно обрабатывать основную часть поведения приложения. Итак, чтобы ответить на ваши вопросы:

Поместить ли я открытый void actionPerformed (ActionEvent e) в представление с помощью простого вызова метода в контроллере?

Я бы сказал нет. Я бы сказал, что должен жить в контроллере; представление должно просто передавать данные, поступающие из пользовательского интерфейса, в контроллер и позволить контроллеру решать, какие методы следует вызывать в ответ.

Если да, должна ли проводиться какая-либо проверка и т. Д. В контроллере?

Большая часть вашей проверки действительно должна быть сделана Контроллером; он должен ответить на вопрос о том, являются ли данные действительными, а если нет, направить соответствующие сообщения об ошибках в представление. На практике вы можете включить несколько простых проверок работоспособности в слой View для улучшения взаимодействия с пользователем. (Я думаю в первую очередь о веб-средах, где вы можете захотеть, чтобы сообщение об ошибке появлялось в тот момент, когда пользователь нажимает «Отправить», а не ждать полного цикла отправки -> процесса -> загрузки страницы, прежде чем сообщить им, что они облажались .) Просто будь осторожен; Вы не хотите дублировать усилия больше, чем должны, и во многих средах (опять же, я думаю о сети) вам часто приходится рассматривать любые данные, поступающие из пользовательского интерфейса, как пачку грязной грязи ложь, пока ты

Если да, то как я могу отправить сообщения об ошибках обратно в View - это должно пройти через Модель снова, или Контроллер должен просто отправить его обратно в View?

У вас должен быть установлен некоторый протокол, в котором View не обязательно будет знать, что будет дальше, пока контроллер не сообщит об этом. Какой экран вы показываете после того, как пользователь нажимает эту кнопку? Представление может не знать, и Контроллер может не знать, пока не просмотрит только что полученные данные. Это может быть «Перейти к другому экрану, как и ожидалось» или «Оставаться на этом экране и отображать это сообщение об ошибке».

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

Если проверка выполняется в представлении, что я помещаю в контроллер?

Смотри выше; реальная проверка должна быть в контроллере. И, надеюсь, у вас есть некоторое представление о том, что уже нужно поместить в контроллер. :-)

Стоит отметить, что все это может стать немного размытым по краям; как и в большинстве сложных программ, таких как разработка программного обеспечения, будет достаточно мнений. Просто прислушайтесь к своему усмотрению, постарайтесь быть последовательными в этом приложении и постарайтесь применить полученные уроки к следующему проекту.

BlairHippo
источник
7

Контроллер действительно является частью View. Его задача состоит в том, чтобы выяснить, какие службы необходимы для выполнения запроса, разархивировать значения из представления в объекты, необходимые для интерфейса службы, определить следующее представление и преобразовать ответ обратно в форму, которую может использовать следующее представление. , Он также обрабатывает любые возникающие исключения и отображает их в представления, понятные пользователям.

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

Сервис-ориентированный: вот где работа сделана.

duffymo
источник
3

Контроллер предназначен прежде всего для координации между видом и моделью.

К сожалению, это иногда заканчивается смешиванием с представлением - в небольших приложениях, хотя это не так уж и плохо.

Я предлагаю вам поставить:

public void actionPerformed(ActionEvent e)

в контроллере. Тогда ваш слушатель действия, по вашему мнению, должен делегировать контроллеру.

Что касается части проверки, вы можете поместить ее в представление или контроллер, лично я думаю, что она принадлежит контроллеру.

Я бы определенно рекомендовал взглянуть на Passive View и Supervising Presenter (по сути, именно на это разделен Model View Presenter - по крайней мере, Фаулер). Видеть:

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

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

Джон
источник
3

Вот эмпирическое правило, которое я использую: если это процедура, которую я буду использовать специально для действий на этой странице, она принадлежит контроллеру, а не модели. Модель должна обеспечивать только последовательную абстракцию для хранения данных.

Я придумал это после работы с большим веб-приложением, написанным разработчиками, которые думали, что они понимают MVC, но на самом деле этого не сделали. Их «контроллеры» сводятся к восьми строкам вызова методов статического класса, которые обычно нигде не вызываются: / / делают свои модели немного больше, чем способы создания пространств имен. Правильное рефакторинг делает три вещи: переводит весь SQL на уровень доступа к данным (он же модель), делает код контроллера более многословным, но гораздо более понятным и сводит на нет старые файлы «модели». :-)

staticsan
источник
1

Также обратите внимание, что каждый виджет Swing может рассматриваться как содержащий три компонента MVC: каждый имеет Model (т.е. ButtonModel), View (BasicButtonUI) и Control (сам JButton).

АКФ
источник
1

Вы по существу правы относительно того, что вы положили в контроллер. Это единственный способ взаимодействия модели с представлением. Выполненное действие может быть помещено в представление, но фактическая функциональность может быть помещена в другой класс, который будет действовать как контроллер. Если вы собираетесь это сделать, я рекомендую изучить шаблон Command, который является способом абстрагирования всех команд, имеющих один и тот же получатель. Извините за отступление.

В любом случае, правильная реализация MVC будет иметь только следующие взаимодействия: Модель -> Вид -> Контроллер -> Вид

Единственное место, где может быть другое взаимодействие, - это если вы используете наблюдателя для обновления представления, тогда представление должно будет запросить у контроллера информацию, которая ему необходима.

mnuzzo
источник
0

Насколько я понимаю, Контроллер переводит из действий пользовательского интерфейса в действия на уровне приложений. Например, в видеоигре Контроллер может перевести «переместил мышь на столько пикселей» в «хочет смотреть в том или ином направлении». В приложении CRUD перевод может быть «нажат на такой-то кнопке», чтобы «напечатать эту вещь», но концепция та же.

Дэвид Сейлер
источник
0

Мы делаем это таким образом, используя контроллеры в основном для обработки и реагирования на пользовательский ввод / действия (и _Logic для всего остального, кроме представления, данных и очевидных вещей _Model):

(1) (ответ, реакция - что веб-приложение «делает» в ответ на пользователя) 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_Meta

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

-> saveDataToMemcache ()

-> saveDataToMongo ()

-> saveDataToSql ()

-> LoadData ()

Иногда мы немного запутываемся в том, куда поместить метод, в C или L. Но модель очень прочная, кристально чистая, и, поскольку все данные в памяти находятся в _Meta, там тоже нет ничего сложного , Между прочим, нашим самым большим шагом вперед было принятие использования _Meta, поскольку это очистило весь сырой от различных объектов _C, _L и _Model, сделало его умственно простым для управления, плюс, одним махом, оно дало нам то, что происходит. называется «Внедрение зависимостей», или способ обойти всю среду вместе со всеми данными (преимуществом является простое создание «тестовой» среды).

FYA
источник