Божьи Контролеры - Как их предотвратить?
В нескольких проектах MVC, над которыми я работал, стало очевидно, что есть несколько проблемных контроллеров, которые органично выросли в классы Бога - полубоги, каждый в своей собственной области, если хотите.
Этот вопрос может быть более важным, "что происходит", но я думаю, что это важный вопрос в отношении SRP (принцип одиночной ответственности), DRY ( "Не повторяйте себя" ) и сохраняя вещи краткими "," - и я недостаточно опытен (с этим шаблоном и в общем дизайне), чтобы быть осведомленным об этом.
В одном проекте у нас есть NutritionController. Со временем он стал включать эти действия (многие из них имеют соответствующие методы GET, POST и DELETE):
Index (home controller)
ViewFoodItem
AddFoodItem
EditFoodItem
DeleteFoodItem
ViewNutritionSummary
SearchFoodItem
AddToFavorites
RemoveFromFavorites
ViewFavorites
Затем у нас есть ExerciseController, который будет включать много подобных действий, таких как поиск и действия избранных. Должны ли они быть реорганизованы в собственный контроллер, чтобы это было так?
SearchController {
SearchExercise
SearchNutrition
//... etc
}
FavoritesController {
ViewNutritionFavorites
AddToNutritionFavorites
AddToExerciseFavorites
EditNutritionFavorites
EditExerciseFavorites
//... etc
}
Мне кажется, что если вы разложите их на отдельные контроллеры, вы получите невероятно большую зависимость на каком-то уровне, чтобы справиться с информацией, которая вам понадобится. ИЛИ у вас будет полностью общее приложение обработки, которое будет очень сложно обрабатывать, так как вам придется прыгать через так много обручей, чтобы получить желаемый эффект (либо на уровне M, V, либо на C).
Я думаю об этом неправильно? Например, должен ли я иметь общий объект "Избранное", а затем позволить контроллеру решить, к какому представлению его нужно переместить?
* Извините за то, что написано аббревиатуры - я делаю это, если кто-то еще сталкивается с этим вопросом и не знает, что это за вещи
EDIT:
Вся логика, которую я выполняю, в значительной степени обрабатывается в слоях службы. Например, контроллер отправит "новый" FoodItem в службу. Если он уже существует или произошла ошибка с ним, служба запустит его обратно в контроллер.
Ответы
Ответ 1
Я сломаю ваш первый список на основе ответственности:
HomeController
FoodItemController
- ViewFoodItem
- AddFoodItem
- EditFoodItem
- DeleteFoodItem
- SearchFoodItem
NutritionController
FavoritesController
- AddToFavorites
- RemoveFromFavorites
- ViewFavorites
- SearchFavorites
Django подход к MVC заключается в разделении обязанностей на "приложения", каждый со своими собственными моделями, контроллерами и даже шаблонами, если необходимо, Скорее всего, у вас есть приложение для продуктов, приложение для питания, приложение "Поиск" и приложение "Избранное".
Edit: OP упомянул, что поиск более специфичен для каждого контроллера, поэтому я сделал эти действия. Тем не менее, поиск также может быть просто общемировой, поэтому в таких случаях SearchController будет в порядке.
Ответ 2
Соблюдай, как говорит Совет. Вы хотите, чтобы контроллеры были простыми. Похоже, что вы закончили слишком много логики координации в ваших контроллерах. Помните, что они отвечают за подключение к представлению и модели. Эта логика координации, вероятно, должна быть разделена на службы.
Я получаю это чувство, потому что вы упоминаете о возможности того, что ваш контроллер будет наращивать огромные зависимости. Ну, если FavoritesController должен знать о питании и использовать избранное (для отображения в одном и том же представлении), не делайте ваш контроллер зависимым от 2 репозиториев, подобных классам. Вместо этого инкапсулируйте это поведение координации. Возможно, создайте FavoriteService, который знает, как вернуть как питание, так и избранное. Эта служба может делегировать функции NutritionFavoritesService и ExerciseFavoritesService. Таким образом, вы являетесь контроллером только с 1 зависимостью, вы держите вещи DRY, применяете SRP и концентрируете свою бизнес-логику в некотором месте, отличном от контроллера.
Ответ 3
Я не слишком хорошо знаком с этой базой, но могу предложить несколько общих советов. Контроллер должен, вероятно, знать только, как выполнить одно действие, или вызвать другие контроллеры одного действия для завершения последовательности связанных действий. Любая информация, которая должна быть передана от действия к действию, должна, вероятно, каким-то образом проходить через слой модели, поскольку эта информация наиболее вероятно относится к базовой модели.
Ответ 4
Я также испытывал такие виды головных болей в обслуживании и считаю, что метод Rails очень полезен для того, чтобы мои контроллеры были сфокусированы и не распущены.
Если я нахожу, что добавляю действия с необычными именами. Чтобы использовать пример блогов, AddPostToBlog, это будет флаг для создания нового Post-контроллера с действием Create.
Другими словами, если действие не является ни одним из действий Index, New, Create, Show, Edit, Update и Destroy, я добавляю новый контроллер, специфичный для требуемого действия.
В вашем примере.
SearchController {
SearchExercise
SearchNutrition
//... etc
}
Я бы реорганизовал это...
SearchExerciseController {
Index
}
SearchNutritionController {
Index
}
Это может означать наличие большего количества контроллеров, но, на мой взгляд, легче управлять, чем когда-либо расширяя "божественные" контроллеры. Это также означает, что контроллеры более самодокументируются.
Eg. Выполняет ли действие SearchExercise, возвращает View для поиска упражнения или действительно выполняет поиск? Вероятно, вы можете убедиться в этом, посмотрев параметры и тело, но это не так просто, как, например, пара действий New и Create, или Edit and Update.
SearchController {
SearchExercise
}