Как стать автором
Обновить

Комментарии 53

Стена текста. Хоть изображениями/схемами разбавили бы.
А лучше комиксов добавить!
И сисёк не меньше 3 размера.
Статье про модели без фотографий моделей чего-то не хватает… ;)
Стена хорошего текста, имхо.
Просто мне лично сложно сконцентрироваться при чтении сплошного текста технического, внимание теряется по ходу чтения.

p.s. я не говорил, что информация в тексте плохая.
Ну я просто уточнил. Однако, это разве технический текст, скорее программистско-философский. Впрочем, не суть важно, лишь вопрос терминологии.
Обычно я пропускаю статьи в которых упоминается PHP (да, предрассудки), но эта статья хороша. Она относится не только PHP, но и к другим веб-платформам, где «M» попирается всеми возможными способами.
И не только к веб-платформам.
Я бы сказал — не только к IT.
НЛО прилетело и опубликовало эту надпись здесь
Проще. Но вы когда нибудь поддерживали проект написанный так? Где хелперы вида — суть модель? А написанные не вами такие проекты? Мне доводилось. Это ад. И этот ад MVC ничем не лучше ада MVC. Так что вот.
Так виджеты (в статье названы помощниками действий) всего лишь позволяют избежать дублирования кода. Того кода, с помощью которого отображение «вытягивает» данные с модели.
В Zend_Framework идеология несколько другая. Модель в принципе там изначально отвечает именно за представление одного объекта. За «сохранение состояния» отвечают Zend_Db_table реализации. По логике вещёй именно они должны «швыряться моделями» из базы, каждая из которых желательно должна иметь возможность рулить своим состоянием в БД.
Главная проблема Zend_Framework ИМХО в том, что валидация вынесена в формы. Я хотел в своём проекте втянуть это дело в саму модель, но тут возникает вопрос производительности — по-сути избыточная проверка. А при том, что ZF весьма небыстр и имеет весьма нехилый при запуске overhead — подобное решение может стать узким местом.
Скажите, в Zend и Symfony у каждого контроллера есть своя модель? Или сущности контроллер и модель не связаны друг с другом?
Вам ничто не мешает сделать ни то, ни другое :) Физически они ничем не связаны, и фактически вы можете использовать любую модель (из любого модуля) в любом контроллере другого модуля.
С другой стороны вы можете создать свой класс с реализацией, при которой создаются именно связанные модель и контроллер. Это будет иногда неплохим применением convention over configuration pattern
Скорее односторонняя связь многие-ко-многим. Любой контроллер может создать любую модель, а может не создавать вообще. А модели о контроллерах вообще ничего знать не должны по идее.
Отличная статья. Прекрасно описана проблематика. Полностью согласен с посылом. Одного прошу: Научите

Я не представляю как в ZF посадить на диету контроллеры, когда весь мануал напичкан примерами его кормления. Да и компоненты (особенно Form/Filter/Validate), как было отмечено выше, ну никак не хотят становиться частью «модели».

К слову, я за годы так и не научился делать «слабо связанные классы» моделей. В ZF

И да, я все ещё предпочитаю толсто собрать SQL-запрос (Zend_Db_Select) в контроллере на основе параметров запроса, нежели написать модель с тремя десятками методов в духе getLastArticlesInTopicByAuthor.
НЛО прилетело и опубликовало эту надпись здесь
А есть ли в природе ASP.Net MVC -приложение, разработанное по этой RICH-концепции? Самые известные примеры MVC Music Store и NerdDinner (можно сказать классика) — модели практически пустые, вся логика в контроллерах.

Music Store:
Модель
Контроллер

NerdDinner:
Модель
Контроллер

Чаще всего разработчики начинают изучение ASP.Net MVC с этих примеров и сами пушут в подобном стиле. Модель — простой класс с валидацией данных, контроллер — вся логика.

Может есть причина для ASP.Net использовать именно такой подход?
Есть: «Developers, developers, developers!» ©
Книгу на русском увидеть хотелось бы.
Мне иногда кажется, что MVC — треугольник.
Model — View — Controller: выберите два.
Не согласен.
В частных случаях — может быть, в общем — все становится монструозным.
Я не говорю, что это хорошо. Мне кажется, что так часто получается.

Ниже приводят варианты того, как разнести логику по разным сущностям. Но при этом то, что получается, уже не будет MVC. Если оставаться в пределах MVC, то что-нибудь начинает перевешивать в ущерб остальному.
Да, так получается, если следовать примерам к использованию framework'ов.
А осмелиться сделать шаг назад и взглянуть на всю картину целиком, отбросив привычные способы работы и взяв на вооружение чистые концепции ООП — это может далеко не каждый. А кто осмеливается, пишет вот такие статьи. Хорошо, что после получения опыта разработки в разных направлениях (desktop, web, gameplay, IDE plugin, etc.) начинаешь чувствовать правильное направление. Но для этого нужна хорошая почва на основе базовых концепций. Если ее нет — это первое, чем необходимо заниматься в плане саморазвития. Отсутствие своего мнения с твердой аргументацией мешает видеть проблемы и пути их решения.
Вообще-то в этой статье автор предлагает отказаться от контроллеров по максимому:
Самый лучший контроллер для меня — это отсутствие контроллера.

Никакого открытия при этом не совершается. То, что можно придумать многоуровневую архитектуру — в этом никто не сомневается. Я же говорю про MVC.

А ООП так вообще всему этому ортогонально, пусть MVC и придумали Smalltalk-еры.
Или добавьте четвертое — которое относится только к конкретным технологиям.
— «Ты по политическим взглядам кто?»
— «Мне нравится, когда у модели большие сиськи!»
Хороший перевод, спасибо! На статью сам недавно ссылался. Сейчас добавлю ссылку еще и на Ваш перевод :)
В хорошем ООП не должно быть жырных чего-либо. Именно в этом проблема всяких PHP фреймворков. Возьмём маленький пример, есть форма, при отсылке которой создаётся запись в базе. Например, форма добавления новости на сайт. По-хорошему, контроллер не должен быть жырным и делать всё на свете (чтение данных, создание модели, передача данных, валидация и т.д.) и жырная модель, которая всё это делать нам тоже вообще не нужна. Нам нужен маленький контроллер, который создаст обьект обрабатывающий данные из формы, этот обьект считает нужные данные и отдаст моделе, модель сделает валидацию и выплюнет результат. Обработчик формы оповестит об этом контроллер, который в свою очередь расскажет вьюшке что делать. В итоге у нас всё тонкое и каждый обьект занят своим делом.

Городить огороды — всегда плохо.
У меня от вашего комментария когнитивный диссонанс. Хорошая орфография, терпимая пунктуация, даже ё — и ЖЫ.
И все-таки несколько запятых пропущены, да :-)
В пятый раз перечитывая коммент я, кажется, понял о чем вы: лично я слово «жырные» воспринял как авторский стиль (или цитату, которую, опять же лично автор, не счел нужным «закавычивать»).
Тоже сначала так подумал. Но «и отдаст моделе» разубедило.
Как-то я привык в последнее время разбивать логику на 4 части:

— модель: не зависит ни от чего, обычные POPO, работают в вакууме, не знают даже что они веб запрос обрабатывают и в базе данных (как правило) хранятся. В них сконцентрирована логика решения бизнес-задачи, например, правила учета или игровая механика. С остальными частями приложений они имеют связь только в виде инжектируемых фабрик для создания объектов типа агрегирующих документов или ошметков трупов на карте. и то, лишь затем, чтобы остальное приложение узнавало о них по факту, а не рекурсивным обходом занималось когда нужно сохранить. Хотя бы интересный вариант, когда модель завязывалась через инжект на сервис генерации случайных чисел — ничего более разумного не придумал. В дополнении к основной модели могут быть модели к бизнес-логике слабо относящиеся, например модель ACL, которую использует соответствующий сервис

— сервисы — обеспечивают прежде всего персистентность моделей, но не только. Как в примере из статьи — обеспечивают получение ленты из внешнего источника, причем не в виде XML или DOM, а как объект модели, представляющий фид, с агрегированной коллекцией итемов и т. п. Или хранит десериализованные объекты в сессии или куках. Сервисы знают всё о контрактах моделей. Знают, что им можно на вход подавать и что можно получать. Сервисы-фабрики создают новые объекты модели. В них же забита и валидация входных значений, и обработка исключений, выбрасываемых при противоречивых состояниях модели вследствие ошибок моделей (пользовательские ошибки отвественность исключительно сервисов). Физически представляют собой тоже POPO как правило, но уже знают о, например, базе данных (когда навороченная ORM внедренная через DI-контейнер, когда mysqli или PDO напрямую «захардкожены»). Активно используют более-менее независмые модули фреймворка, которые собственно на стэк не завязаны

— представления — в принципе понятно, но могут обращаться к некоторым сервисам, для получения (read only) моделей вспомогательных для страницы объектов (сайдбаров). В принципе возможны сервисы, возвращающие непосредственно html из SQL, но как-то необходимости не возникало, оверхид «результат запроса»-«преобразование в объект или массив объектов модели»-«вывод модели» незначительный по сравнению с инициализацией стэка и SQL или других внешних хранилищ

— контроллер связывает всё это воедино. Плотно завязан на фреймворк или обрабатывает запрос самостоятельно без FrontController. Обычно извлекает параметры запроса, передает их сервису, чтобы получить основной объект страницы, проверяет права, вызывает, если нужно и можно, метод модели, сохраняет если нужно её состояние и отображает представление, передавая ему, если нужно, основной объект. Редко когда больше десятка строк.

Толстые у меня обычно получаются модели и сервисы, но для них, как правило, применимы обычные методы тестирования и рефакторинга, невзирая на особенности фреймворка или его отсутствие. При тестировании моделей сервисы-фабрики легко заменяются на примитивные стабы, возвращающие захардкоженные (в коде теста или внешнем файле). То же с высокоуровневыми сервисами — низкоуровневые замещаются примитивными стабами, возврающими массив или XML-документ. Без юнит-тестов остаются только низкоуровневые типа запросов к БД или внешнему серверу.

В общем сервисы у меня выполняют роль помойки, куда «выкидываю» весь код, который мне не нужен в модели, контроллере или представлении. Прежде всего это кучи кода из контроллеров, относящиеся к хранению и валидации данных, а также получения моделей к основноой функции контроллера не относящейся.
Набор процедурных сервисов, как правило, можно заменить набором связанных моделей.
Причем модель инкапсулирует и управляет своим состоянием.
Просто в наше время почему-то народ увлекся процедурным подходом. Видимо потому, что ООП сложнее, требует продумывания use-case'ов, разделения на модели и обдумывание связей между ними, а также поддержание целостности концепции. Процедурный же подход намного проще: взял параметры, обработал, передал результат следующему обработчику, и так пока не дойдет до кода представляния. И то, что все это делается с использованием классов и объектов, не делает этот подход объектно-ориентированным. Это процедурное программирование с использованием объектов, не более того.
Кстати, модель потому и получается переносимой, что в ней инкапсулирована реализована концепция абстрактного типа данных.
Например, можно делать так.
Есть у нас информация об автомобиле. Это — абстрактный тип данных. Что можно делать с информацией? Читать и писать. Но сама она себя читать и писать не может — это должен делать кто-то еще. Получается, что нам нужен Обработчик информации об автомобиле, умеющий ее читать и писать. Возможно, он является частным случаем обработчика (произвольной) информации. И как ему читать и писать? Для этого нужны инструменты (глаза = wrapper для ResultSet, шариковая ручка = wrapper для PreparedStatement, для примера), которые он использует. Он умеет обрабатывать информацию из модели и подготавливать ее для записи, а также записывать в модель после чтения. И, заметьте, ему пофиг, из базы читать или из файла — это же тоже абстрактный тип данных, специфику оставим классам реализации. А для абстрактного типа данных самое важное — интерфейс. Интерфейс — это сама суть абстракции. Нужно мыслить интерфейсами — тогда ООП получается сам собой. Забудьте про реализацию, когда строите модель. Реализацию нужно вспомнить лишь после построения модели — и, возможно, поправить абстракцию для соответствия реальным возможностям. Но наоборот нельзя — концепция, как правило, рушится при обратном подходе: от реализации к интерфейсу.
Кто сказал, что сервисы процедурные? Они имеют свое состояние и его инкапсуляцию (например, на этом уровне кэширование работает и/или запоминание времени начала события, к которому потом все привязываются как к атомарной операции). Просто как раз юз-кейсы как правило не описываются в терминах БД или html, там даже термины хранения частенько отсутствуют: раз создали документ — он существует пока его не уничтожили с составлением акта, а пока документ не подписан — он не существует. Нет бизнес-термина «сохранить документ».

А концепция у меня как раз целостная (по-моему): в любой момент времени, когда модель получает управление, в ней уже содержатся непротиворечивые данные, равно как и когда она его отдает (без выброса исключений). А вот накладывать на объекты или классы моделей ещё ответственность за хранение состояния лично мне кажется нарушением единой концепции. В принципе, вы можете считать подавляющее большинство моих сервисов частью модели, просто я счел нужным отделить персистентность состояния от использования состояния на уровень не предусмотренный MVC — мой обычный контроллер обеспечивает обмен данными между моделью и представлением только в качестве реакции на действия пользователя. И то, лишь для того, чтобы явно показать в контроллере, что конкретное представление зависит от его действий.
Cервисы это как раз из Anemic Domain Model, Rich Domain Model подразумевает интеграцию сервисов в соответсвующий класс модели с которым этот сервис работает. По этому ADM часто грешит процедурной парадигмой.
Эх, каким идеалистом я был пару лет назад, думая, что все проекты можно переписать с нуля, или ошибки при плавном рефакторинге могут повлечь лишь недополучение прибыли :)
Я плохо знаком с MVC, но статья зацепила. Правильно ли я понял что
Контроллеры генерирует Представление (UI)
Пользователь взаимодействует с Представление передавая ему данные
Представление транслирует данные Контролеру
Контроллер передает эти данные Модели
Модель производит бизнес логику
Модель отдает данные Контролеру
Контроллер создает Представление
Пользователь видит новые данные?

А вся проблема в том, что создание представления в контролере производится автоматически на основе каких то правил?
И если так, то это вполне очевидно. Максимум отделять Бизнес логику от Логики отображения. Декомпозиция.
Проблем (причин для холиваров) две обычно:

— сложно разделить (в смысле понять) где уже бизнес-логика, а где ещё контроллер подготавливает данные для передачи. Скажем, получение данных из БД — должен ли контроллер передать модели готовые данные (грубо говоря, параметры конструкторов/сеттеров) или должен лишь указать модели что (id или условие поиска) она должна из БД тянуть (а может не из БД, а из ФС или внешнего REST-сервиса). Кроме проблем хранения сюда ещё как минимум входят проблемы валидации и санирования, аутентификации и авторизации, кэширования и прочих «умных слов» в предметной области не присутствующих.

— представление обычно требует не один-два объекта или коллекции модели, а несколько, десятки, а то и сотни, к основной семантике контроллера не относящиеся — на хабре практически весь сайдбар такой, да меню «личного кабинета». Плюс, как правило, эти дополнительные данные одинаковы для разных, а то и всех контроллеров и возникает желание сделать «хак» в виде обращения к данным модели, минуя контроллер.
а не устарела ли статья?
или еще кто то из адекватных разработчиков пишет ТТУКи?
или кто-то не передает модели в представления?

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

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


мда, статья действительно старая, датируется 2008 годом.
зачем она здесь? ностальгия?

На самом деле концепция «Тощего контроллера, толстой модели» характерна только для бизнес приложений. Большинство же сайтов (СМИ, развлекаловка, да даже форумы) по своей природе просто не нуждаются в переносе логики в модель. У них почти нету «модели» и контролеры не раздуты. 90% запросов это чтение и оно должно работать быстро, если модель увесистая то сразу возникают проблемы с производительностью доступа к БД. В современном вебе (публичном, а не корпоротивном) надо делать что то простое но ОЧЕНЬ быстро, по этому чем меньше абстракций тем лучше. На самом деле MVC это слишком поверхностный взгляд на то, что происходит. Мне кажется куда важнее описывать конкретные абстракции(элементы системы), к примеру: СУБД<->ORM<->Controller->Template. И тут уже проще понять куда и что. К примеру часть валидации можно положить констрейнами в СУБД (Postgres к примеру позволяет просто кучу всего валедировать), часть проверок можно установить в объектах ORM, а что то уже запихнуть в контроллер и даже в template можно через javascript/html5 проводить часть валидации. На этом примере мы видим, что у некоторых задачь нету чёткого места, каждая задача проходит декомпозицию и раскидывается по элементам системы.
Я бы поспорил на счёт раскидывания некоторых задач по элементам системы, в т.ч. ту же валидацию. Её нельзя проводить ограничениями в СУБД. И нельзя — на объектах ORM. Какой смысл пропихивать кривой ввод с представления, через контроллер и модель — в СУБД? Терять контекст в случае фейла и затем протаскивать ошибку обратно через всё это счастье? И заодно потерять гибкость на 1% задач, где временно необходимо проигнорировать консистентность данных на время выполнения запроса. Нет, валидацию нужно производить максимально близко к входу, а именно в браузере и контроллере. Каждой задаче — своё конкретное место: View, Controller или Model. Просто не нужно забывать, что всё это счастье крутится внутри некоторой исполняемой среды и нужна прослойка между миром приложения и суровым внешним миром. Обычно это называют инфраструктурой, которая неявно пронизывает каждый из модулей\слоёв\уровней. Именно там и живут задачи, которые кажется что не имеют чёткого места. И все абстракции встают на свои места.
Если форма отправляется через AJAX то перехватить ошибку от СУБД/ORM и вывести её пользователю вполне можно (не теряя контекст). Но на самом деле всё это я привёл только как пример. Вы к примеру сделали вывод, который противоречит данной статье и привели вполне себе веские технические доводы.
MVC слишком абстрактный «патерн», мне кажется он больше путает.
Можно — всё. Я уже написал про это ниже.

MVC абстрактный паттерн, но не слишком. Он конкретным образом бьёт наше приложение на вполне себе явные модули. Которые внутри уже могут быть реализованы хоть с использованием СУБД, хоть ORM (на примере модели). Просто одной статьи из Википедии недостаточно, чтобы понять данный паттерн.

А по поводу статьи — я с ней не спорил. По-моему в ней просто расписаны способы, как можно извратить изначальную концепцию. Но только практически все примеры из неё — это уже не MVC. Только в воображении авторов кода. ИМХО.
Конечная валидация может быть только на стороне СУБД. Дублирование первичных ключей или уникальных полей, нарушение ограничений внешних ключей и т. п. до записи в БД не проверишь (ну, если не ставить глобальных блокировок в самом начале обработки запроса). С другой стороны, многие валидации если можно провести на стороне СУБД, то по разным причинам нецелесообразно. А предварительную валидацию хорошо бы провести на стороне клиента вообще. То есть валидация уж точно будет размазана по приложению в целом — от браузера до СУБД.
Если вы так сделаете — то действительно, логика вашего приложения будет размана от клиента и аппликейшне сервера до самой СУБД. И валидация в том числе. Можно пойти ещё дальше и в коде приложения валидировать считанные из СУБД данные. И на клиенте, после загрузки страницы, заодно проходить валидатором. А потом! Можно в коде каждого метода добавить проверки на входные данные и бросать исключения, если что-то не так. И заодно проверять инварианты\выходные данные от методов! Ващеее, идеальный проект.
Это обычная практика минимум две валидации: почти всё в приложении и кое-что в базе. Обычно и третья на клиенте хотя бы средствами html5. Можно всё в базе, но не практично, как правило.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории