Как определить, что должен получить свой соответствующий контроллер?

10

Я использую шаблон MVC в своем веб-приложении, построенном на PHP.

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

Есть ли хорошие правила, которым нужно следовать при создании контроллеров?

Например, я могу иметь:

AuthenticationController с действиями:

  • index() отобразить форму входа.
  • submit() обрабатывать отправку формы.
  • logout(), само за себя.

ИЛИ

LoginController с действиями:

  • index() отобразить форму входа.
  • submit() обрабатывать отправку формы.

LogoutController с действием:

  • index() обрабатывать выход.

ИЛИ

AccountController с действиями:

  • loginGet() отобразить форму входа.
  • loginPost() обрабатывать отправку формы входа.
  • logoutGet() обрабатывать выход.
  • registerGet() отобразить регистрационную форму.
  • registerPost() обрабатывать отправку формы.

    И любые другие действия, связанные с учетной записью.

Малыш Даймонд
источник
Может быть, взглянуть на RESTful дизайн. Это не решает все проблемы такого рода, но дает вам очень хорошее представление о том, как об этом думать.
Торстен Мюллер

Ответы:

3

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

(Даже если вы на самом деле не проводите никакого тестирования, размышления о том, как вы будете проводить тестирование ваших контроллеров, дадут вам очень хорошее представление о том, как их структурировать.)

An AuthenticationControllerне является тестируемым сам по себе, потому что он содержит только функциональные возможности для входа и выхода, но ваш тестовый код должен будет каким-то образом создать поддельные учетные записи для целей тестирования, прежде чем он сможет протестировать успешный вход в систему. Вы можете обойти тестируемую подсистему и перейти непосредственно к своей модели для создания учетных записей тестирования, но тогда у вас будет хрупкий тест в ваших руках: если модель изменится, вам придется изменять не только код, который тестирует модель, но также и код, который тестирует контроллер, хотя интерфейс и поведение контроллера остались неизменными. Это неразумно.

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

An AccountControllerпредоставит вам все необходимое для проведения тестирования: вы можете создать тестовую учетную запись и затем попытаться войти в нее, вы можете удалить учетную запись, а затем убедиться, что вы больше не можете войти в систему, вы можете изменить пароль и убедиться, что Для входа в систему необходимо использовать правильный пароль и т. д.

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

И вообще, подход «думать о тестировании» будет работать не только в этом конкретном сценарии, но и в любом подобном сценарии, с которым вы столкнетесь в будущем.

Майк Накис
источник
1

Ответ не так очевиден

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

Что такое контроллер?

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

Какова сфера действия контроллера?

И это более или менее важно, когда у нас будет какой-либо ответ. Что вы думаете? Это контроллер вещей (например, Учетная запись) или контроллер действий? Конечно, это контроллер какой-то модели или более абстрактная вещь, которая обеспечивает действия над ней.

Ответ...

AuthenticationController с действиями:

  • index () для отображения формы входа.
  • submit () для обработки отправки формы.
  • выход из системы (), не требует пояснений.

Нет, аутентификация - это процесс. Не иди этим путем.

LoginController с действиями:

  • index () для отображения формы входа.
  • submit () для обработки отправки формы.

Тоже самое. Логин - действие. Лучше не создавать контроллер действий (у вас нет соответствующей модели).

AccountController с действиями:

  • loginGet () для отображения формы входа.
  • loginPost () для обработки отправки формы входа.
  • logoutGet () для обработки выхода из системы.
  • registerGet () для отображения формы регистрации.
  • registerPost () для обработки отправки формы.

Неплохо, но я не уверен, стоит ли строить этот контроллер низкого уровня (контроллер - это сама абстракция). В любом случае, создание методов с помощью * Get или * Post неясно.

Любое предложение?

Да, учтите это:

AccountController:

  • Логин (AccountModel)
  • выход из системы (AccountModel)
  • Регистр (AccountModel)
  • индекс()

И связанная с ним модель, оф класс аккаунта. Это даст вам возможность переместить пару модель-контроллер в другое место (если это будет необходимо) и создать четкий код (очевидно, что login()означает метод). Привязка к модели действительно известна, особенно с CRUD-приложениями, и, возможно, это способ для вас.

Давид Пура
источник
1

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

class SecurityController
{
    // can handle both the login page display and
    // the login page submission
    login(); 

    logout();

    register();

    // optional: confirm account after registration
    confirm();

    // displays the forgot password page
    forgotPassword();

    // displays the reset password page
    // and handle the form submission
    resetPassword();
}

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

С контроллерами, созданными для ресурсов (скажем Task), у вас будут обычные действия CRUD :

class TasksController
{
    // usually displays a paginated list of tasks
    index();

    // displays a certain task, based on an identifier
    show(id);

    // displays page with form and
    // handles form submission for creating
    // new tasks
    create();

    // same as create(), but for changing records
    update(id);     

    // displays confirmation message
    // and handles submissions in case of confirmation
    delete()
}

Конечно, у вас есть возможность добавить связанные ресурсы на тот же контроллер. Например, у вас есть сущность Business, а у каждой есть несколько BusinessServiceсущностей. Контроллер для этого может выглядеть так:

class BusinessController
{
    index();

    show(id);

    create();

    update(id);

    delete();

    // display the business services for a certain business
    listBusinessServices(businessId);

    // displays a certain business service
    showBusinessService(id);

    // create a new business service for a certain business
    createBusinessService(businessId);

    // updates a certain business service
    updateBusinessService(id);

    // deletes a certain business service
    deleteBusinessService(id);
}

Этот подход имеет смысл, когда связанные дочерние объекты не могут существовать без родительского объекта.

Вот мои рекомендации:

  • создавать контроллеры на основе группы связанных операций (выполнение определенных обязанностей, таких как безопасность или операции CRUD над ресурсами и т. д.);
  • для контроллеров на основе ресурсов не добавляйте ненужные действия (если вы не должны обновлять ресурс, не добавляйте действие обновления);
  • Вы можете добавить «пользовательские» действия, чтобы упростить вещи (например, у вас есть Subscriptionобъект, который имеет доступность на основе ограниченного числа записей, вы можете добавить новое действие к названному контроллеру use(), единственная цель которого - вычитать одну запись из Subscription)
  • не усложняйте ваш контроллер огромным количеством действий и сложной логикой, попробуйте упростить ситуацию, уменьшив количество действий или сделав два контроллера;
  • если вы используете MVC-ориентированную среду, следуйте их рекомендациям (если они есть).

Некоторые ресурсы для дальнейшего чтения здесь .

qretzu
источник
0

Я вижу два антагонистических дизайна "силы" (которые не являются исключительными для контроллеров):

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

С точки зрения сплоченности все три действия (вход в систему, выход из системы, регистрация) связаны между собой, но вход и выход из системы намного больше, чем регистрация. Они связаны семантически (одно является инверсией другого) и, вполне возможно, также будут использовать одни и те же сервисные объекты (их реализации также взаимосвязаны).

Моим первым инстинктом было бы сгруппировать вход и выход в один контроллер. Но если реализации контроллера входа в систему и выхода из системы не так просты (например, в логине есть капча, больше методов аутентификации и т. Д.), У меня не будет проблем с разделением их на LoginController и LogoutController для обеспечения простоты. Где лежит этот порог сложности (когда вы должны начать разделять контроллер), немного личное.

Также помните, что независимо от того, что вы изначально разрабатываете свой код, вы можете (и должны) реорганизовать его по мере его изменения. В этом случае довольно типично начать с простого проектирования (иметь один AuthenticationController), и со временем вы получите больше требований, которые усложнят код. Как только он пересекает порог сложности, вы должны реорганизовать его на два контроллера.

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

QBD
источник
0

Вот несколько практических правил:

  • Упорядочить по теме или теме, с именем контроллера, являющимся названием темы.

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

В ситуации, которую вы упоминаете (аутентификация), команда MVC уже написала для вас контроллер. Откройте Visual Studio 2013, а затем нажмите

File / New / Project... 
Search installed templates for "ASP.NET MVC4 Web Application"
Choose "Internet Application" / OK.

AccountController.cs содержит все методы для управления учетными записями пользователей:

Login()
Logoff()
Register()
Disassociate()
Manage()
ExternalLogin()

Таким образом, они организованы по теме «Учетные записи пользователей и аутентификация», с видимым названием темы «Учетная запись».


источник
0

терминология

Я считаю, что ошибочно называть класс, содержащий некоторые связанные с HTTP методы, «контроллером».

Контроллер - это метод, который обрабатывает запрос, но не класс, содержащий такие методы . Так index(), submit(), logout()являются контроллерами.

Класс, содержащий методы такого типа, называется «контроллер» только потому, что он представляет собой группу контроллеров и играет роль пространства имен «нижнего уровня». На языке FP (например, Haskell) это был бы просто модуль. Хорошей практикой является сохранение классов «контроллеров» в состоянии без возможности сохранения состояния на языках ООП, за исключением ссылок на сервисы и другие вещи всей программы.

Ответ

С разборкой терминологии возникает вопрос: «Как разделить контроллеры на пространства имен / модули?». Я думаю, что ответ таков: контроллеры внутри одного пространства имен / модуля должны иметь дело с однотипными данными . Например, UserControllerимеет дело прежде всего с экземплярами Userкласса, но иногда затрагивает другие связанные вещи, если требуется.

Так login, logoutи другие подобные действия, в основном дело с сессией, он , вероятно , лучше всего поместить их внутрь SessionController, и indexконтроллер, который просто выводит форму, должен быть помещен в LoginPageController, так как он , очевидно , имеет дело с страницей входа. Имеет смысл объединить рендеринг HTML и управление сеансами в один класс, что нарушит SRP и, возможно, множество других хороших практик.

Основной принцип

Если вам трудно решить, куда поместить фрагмент кода, начните с данных (и типов), с которыми вы имеете дело.

scriptin
источник
2
Извините, это действия, а не контроллеры :)
JK01
@ JK01 Вот как ты их называешь. Вы знаете, это терминология. И есть фреймворки, которые называют эти функции «контроллерами» (или «обработчиками»), так как есть много фреймворков, которые не организуют их в классы, так как пространства имен / модулей уже достаточно. Вы можете использовать любые термины, которые вам нравятся, это просто слова, но я думаю, что лучше иметь меньше терминов.
скрипт