В MVC считается ли хорошей практикой иметь частные, не действующие функции в классе контроллера?

10

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

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

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

Дэвид "лысый имбирь"
источник

Ответы:

16

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

Вы можете применить несколько ту же логику к вашему MVC-приложению. Огромные и неприятные функции, которые вы описываете, являются наиболее вероятным поведением модели, и они должны принадлежать модели (обратите внимание, что модель - это не только объект, который отображается в представлении). Если поведение модели меняется, то должна меняться модель, а не контроллер, который ее обрабатывает.

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

devnull
источник
+1 за творческую аналогию. :) Вы делаете интересное замечание. Особенно на формирование вредной привычки. Спасибо.
Дэвид «лысый имбирь»
8

Лучший ответ, который я могу дать, это процитировать замечательную книгу Роберта Мартина «Чистый код», которую я настоятельно рекомендую всем, кто интересуется этой темой:

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

Лучше сказать не могу. Применяется еще одна замечательная цитата из той же книги:

Функции должны делать одно. Они должны делать это хорошо. Они должны это делать только.

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

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

Дмитрий Зайцев
источник
1
Я не думаю, что представление бизнес-логики в представлениях - это «неклассическая MVC», это просто «плохая MVC». Очевидно, вам нужны базовые структуры управления в представлениях, но они должны быть согласованы с интересами пользователя / пользовательского интерфейса, а не с интересами домена / бизнеса. Фактическая функция в представлении довольно ужасна.
Aaronaught
1
@ Aaronaught Я был немного неопределен с «некоторой логикой», я имел в виду, например, библиотеку Backbone.js, где вы размещаете пользовательские события и функции для их обработки в своем представлении. В классическом MVC это работа контроллера. Однако это может быть непрактичным, так как вам нужно будет настраивать и View, и Controller каждый раз, когда изменяется ваш пользовательский интерфейс. Поместив функции обработчика пользовательского интерфейса в представление, вам нужно только настроить представление. Это только мой субъективный взгляд - я что-то упустил?
Дмитрий Зайцев
1
То, что что-то доставляется на стороне клиента, не означает, что это логически часть представления. Конечно, привязки данных в представлениях, но Backbone сама по себе является инфраструктурой MV * (вроде MVC, MVP, не совсем), и ваши клиентские сценарии должны быть организованы соответствующим образом; в противном случае вы просто взламываете.
Аарона
0

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

Необходимая логика и вспомогательные функции помещаются в отдельные автономные вспомогательные классы. Это также значительно облегчает мое тестирование (вы тестируете .. правильно ??: D) Тестирование контроллеров, как известно, сложно, каждый раз, когда вы пытаетесь создать экземпляр контроллера для тестирования, вы должны думать о HTTP-контексте и подделке. http это и то, и это боль, но это боль нарочно. Вам нужно все это, потому что контроллер так тесно связан с HTTP и сетью. Это точка входа в ваше веб-приложение.

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

Мы разработали наш сайт MVC со всеми нашими объектами базы данных (не нашими моделями mvc, нашими фактическими объектами db), нашим хранилищем, нашими вспомогательными классами и нашей логикой в ​​отдельных автономных библиотеках DLL. У нас только у каждого был один веб-сайт, но мы все равно сделали это так.

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

астронавт
источник
2
Модель в MVC - единственный слой, который не должен быть тупым. Если смартов нет в модели, а их нет в контроллере, то где они ... в представлении? Контроллеры также не должны быть сложными для тестирования; Возможность использовать DI и фальшивые / фиктивные средства для облегчения модульного тестирования является одним из преимуществ MVC над другими средами. Большинство моих тестов контроллеров под 5 строк.
Aaronaught
я бы использовал «вспомогательный» класс с логикой, а не пронизывал модель логикой. какую логику вы бы поместили в модель? он знает, как загрузить себя и сохранить себя? Я согласен, что подделка / заглушка - это легко, но это не повод начинать откармливать ваших контроллеров.
космонавт
У меня такое ощущение, что этот ответ значит хорошо, но он сформулирован неправильно ... или, возможно, отличается терминологией.
Саймон Уайтхед
3
«Вспомогательные» классы не являются архитектурным элементом. Они либо являются частью M, V или C. Если вы не уверены, какой из них, то этим помощникам не хватает сплоченности . Слово «помощник» также стоит здесь с «ручкой», «делом», «исполнением» и страшным менеджером .
Aaronaught
@SimonWhitehead: большинство ответов значат хорошо, но многие не верны. Этот, к сожалению, либо продвигает неправильное понимание значения «Модель», либо рекомендует не использовать критическую бизнес-логику. Я имел сомнительное удовольствие поддерживать сайты MVC с миллионом «помощников» - они ужасны.
Aaronaught
-2

Кроме хороших ответов Дмитрия Зайцева и космонавта, я не знаю, действительно ли следующее также верно для PHP: вам следует избегать частных методов из-за отсутствия возможностей автоматического тестирования.

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

Всегда помните принцип KISS: будь проще, глупее.

cHaOs667
источник
5
Это не очень хорошая причина избегать закрытых методов, а также не имеет ничего общего с архитектурой MVC. Вы не пытаетесь тестировать закрытые методы, они должны быть покрыты тестами открытых методов. Если вы не можете их охватить, это признак того, что ваш класс слишком сложен и нуждается в рефакторинге; это не значит, что у вас не должно быть приватных методов или (я искренне надеюсь, что это не то, что вы на самом деле имели в виду), что они должны быть публичными.
Aaronaught