Подтвердить что ты не робот

Разделение проблем; MVC; Зачем?

Я сейчас читаю OO, прежде чем приступать к следующему крупному проекту. Чтобы дать вам быстрый фон, я разработчик PHP, работая над веб-приложениями.

Одной из областей, которая меня особенно интересует, является пользовательский интерфейс; в частности, как это сделать и подключить к моей модели OO.

Я занимаюсь чтением в этой области. Один из моих фаворитов: Построение пользовательских интерфейсов для объектно-ориентированных систем

"Все объекты должны предоставлять свой собственный интерфейс"

Размышляя о моей проблеме, я вижу, что это хорошо работает. Например, я создаю свой "пользовательский" объект для представления того, кто зарегистрировался на моем сайте. Один из моих методов - это "display_yourself" или аналогичный. Я могу использовать это во всем моем коде. Возможно, для начала это будет только их имя. Позже, если мне нужно настроить, чтобы показать свое имя + маленький аватар, я могу просто обновить этот метод, и hey-presto, мое приложение обновлено. Или, если мне нужно сделать свое имя ссылкой на их профиль, эй-престо, я могу легко обновить его с одного места.

В терминах системы OO; Я думаю, что этот подход работает хорошо. Если посмотреть на другие потоки StackOverflow, я нашел это в разделе "Разделение проблем": Soc

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

На мой взгляд, я достиг этого. Мой объект пользователя скрывает всю информацию. У меня нет никаких мест в моем коде, где я говорю $user- > get_user_name(), прежде чем я его покажу.

Однако, похоже, это противоречит тому, что, по-видимому, думают другие люди как "лучшая практика".

Чтобы указать "выбранный" (зеленый) ответ на тот же вопрос:

"Разделение интересов код для каждой из этих проблем отдельный. Изменение интерфейса не должны требовать изменения код бизнес-логики и наоборот. Модель-View-Controller (MVC) образцом является прекрасным примером отделяя эти проблемы от совместимость программного обеспечения".

Почему это делает для улучшения обслуживания? Разумеется, с MVC мой взгляд должен знать о модели очень много? Прочтите статью JavaWorld для подробного обсуждения этого вопроса: Построение пользовательских интерфейсов для объектно-ориентированных систем

В любом случае... наконец-то дойдем до сути!

1. Можете ли вы порекомендовать какие-либо книги, которые подробно обсудят это? Мне не нужна книга MVC; Я не продаюсь на MVC. Я хочу книгу, в которой обсуждаются OO/UI, потенциальные проблемы, возможные решения и т.д. (Возможно, включая MVC) Arthur Riel Объектно-ориентированная эвристика дизайна

затрагивает его (и это отличная книга!), но я хочу что-то более подробное.

2. Может ли кто-нибудь изложить аргумент, который также объясняется, как статья Allen Holub JavaWorld, в которой объясняется, почему MVC - хорошая идея?

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

4b9b3361

Ответ 1

Это ошибка в том, как часто учат ООП, используя такие примеры, как rectangle.draw() и dinosaur.show(), которые не имеют никакого смысла.

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

"Позже, если мне нужно настроить, чтобы показать свое имя + маленький аватар, я могу просто обновить этот метод, и hey-presto, мое приложение обновлено."

Подумайте о этой маленькой части для момента. Теперь взгляните на Qaru и обратите внимание на все места, где отображается ваше имя пользователя. В каждом случае это выглядит одинаково? Нет, наверху вы только что получили конверт рядом с вашим именем пользователя, за которым следуют ваша репутация и значки. В теме вопроса у вас есть ваш аватар, за которым следует ваше имя пользователя с вашей репутацией и значками ниже. Как вы думаете, есть ли пользовательский объект с такими методами, как getUserNameWithAvatarInFrontOfItAndReputationAndBadgesUnderneath()? Нах.

Объект связан с данными, которые он представляет, и методами, которые воздействуют на эти данные. У вашего пользовательского объекта, вероятно, будут элементы firstName и lastName, а также необходимые геттеры для извлечения этих фрагментов. Он также может иметь удобный метод, например toString() (в терминах Java), который возвращает имя пользователя в общем формате, например, имя, за которым следует пробел, а затем фамилия. Кроме того, пользовательский объект не должен делать ничего другого. Клиент должен решить, что он хочет сделать с объектом.

Возьмем пример, который вы предоставили нам с пользовательским объектом, а затем подумайте о том, как вы будете делать следующее, если вы построили в нем "UI":

  • Создайте экспорт CSV, показывающий всех пользователей, упорядоченных по имени. Например. Фамилия, Имя.
  • Обеспечьте как тяжелый графический интерфейс, так и веб-интерфейс для работы с пользовательским объектом.
  • Показывать аватар рядом с именем пользователя в одном месте, но показывать только имя пользователя в другом.
  • Предоставьте список пользователей RSS.
  • Показывает имя пользователя жирным шрифтом в одном месте, выделено курсивом в другом и как гиперссылка в другом месте.
  • Показывать средний начальный адрес пользователя.

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

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

Ответ 2

Здесь подход, который я беру при создании веб-сайтов на PHP с помощью шаблона MVC/разделения проблем:

Я использую три основных элемента:

  • Модели - классы PHP. Я добавляю им методы для извлечения и сохранения данных. каждый модель представляет собой отдельный тип сущности в системе: пользователи, страницы, сообщения в блоге
  • Представления - шаблоны Smarty. Здесь находится html.
  • Контроллеры - классы PHP. Это мозги приложения. типично URL-адреса в сайте вызывают методы класса. example.com/user/show/1 вызовите метод $user_controller- > show (1). Контроллер извлекает данные из модели и дает ей представление.

Каждая из этих частей имеет определенную работу или "заботу". Задача model - обеспечить чистый интерфейс для данных. Как правило, данные сайта хранятся в базе данных SQL. Я добавляю методы модели для извлечения данных и сохранения данных.

Задача view - отображать данные. Вся разметка HTML отображается в представлении. Логика для обработки зебра-полосания для таблицы данных идет в представлении. Код для обработки формата, в котором должна отображаться дата, отображается в представлении. Мне нравится использовать шаблоны Smarty для просмотров, потому что он предлагает некоторые приятные функции для обработки таких вещей.

Задача контроллера - действовать как посредник между пользователем, моделью и представлением.

Посмотрите пример того, как они объединяются и где преимущества лежат:

Представьте себе простой блог-сайт. Основная часть данных - это сообщение. Также представьте, что сайт отслеживает количество просмотров сообщения. Для этого мы создадим таблицу SQL:

posts
id date_created title body hits

Теперь предположим, что вы хотели бы показать 5 самых популярных сообщений. Вот что вы можете увидеть в приложении, отличном от MVC:

$sql = "SELECT * FROM posts ORDER BY hits DESC LIMIT 5";
$result = mysql_query($sql);

while ($row = mysql_fetch_assoc($result)) {
    echo "<a href="post.php?id=$row['id']">$row['title']</a><br />";
}

Этот фрагмент довольно прост и работает хорошо, если:

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

Представьте, что вы хотите показать 10 наиболее популярных сообщений на домашней странице и 5 самых популярных в боковой панели на подстраницах. Теперь вам нужно либо скопировать код выше, либо поместить его в файл include с логикой, чтобы проверить, где он отображается.

Что делать, если вы хотите обновить разметку для домашней страницы, чтобы добавить класс "new-post" к сообщениям, которые были созданы сегодня?

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

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

Использование подхода MVC и обеспечение разделения проблем в вашем приложении может смягчить эти проблемы. В идеале мы хотим разделить его на три области:

  • логика данных
  • прикладная логика
  • и логика отображения

Посмотрите, как это сделать:

Начнем с модели . У нас будет класс $post_model и дадим ему метод под названием get_popular(). Этот метод вернет массив сообщений. Кроме того, мы укажем ему параметр, чтобы указать количество возвращаемых сообщений:

post_model.php

class post_model {
    public function get_popular($number) {
        $sql = "SELECT * FROM posts ORDER BY hits DESC LIMIT $number";
        $result = mysql_query($sql);
        while($row = mysql_fetch_assoc($result)) {
            $array[] = $row;
        }
        return $array;
    } 
}

Теперь для главной страницы у нас есть контроллер, мы будем называть его "home". Представьте себе, что у нас есть схема маршрутизации URL-адресов, которая вызывает наш контроллер при запросе домашней страницы. Задача состоит в том, чтобы получить популярные сообщения и дать их правильному виду:

home_controller.php

class home_controller {
    $post_model = new post_model();
    $popular_posts = $post_model->get_popular(10);

    // This is the smarty syntax for assigning data and displaying
    // a template. The important concept is that we are handing over the 
    // array of popular posts to a template file which will use them 
    // to generate an html page
    $smarty->assign('posts', $popular_posts);
    $smarty->view('homepage.tpl');
}

Теперь посмотрим, как выглядит представление:

homepage.tpl   

{include file="header.tpl"}

 // This loops through the posts we assigned in the controller
 {foreach from='posts' item='post'} 
    <a href="post.php?id={$post.id}">{$post.title}</a>
 {/foreach}

{include file="footer.tpl"}

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

Модель связана с получением данных. Он знает о базе данных, знает о запросах SQL и операторах LIMIT. Он знает, что он должен вернуть хороший массив.

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

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

Также важно упомянуть, что каждая часть не знает.

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

Контроллер и не знают, что сообщения хранятся в базе данных SQL.

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

Итак, в этом состоянии мы установили четкое разделение проблем между логикой данных (моделью), логикой приложения (контроллером) и логикой отображения (представлением). Ну что теперь? Мы взяли короткий простой PHP-фрагмент и разбили его на три путаных файла. Что это дает нам?

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

  • Показать популярные сообщения в боковой панели на подстраницах
  • Выделить новые сообщения с дополнительным классом css
  • Измените базовое определение "популярного сообщения"

Чтобы показать популярные сообщения на боковой панели, мы добавим два файла на нашу страницу:

Контроллер подстраницы...

subpage_controller.php

class subpage_controller {
    $post_model = new post_model();
    $popular_posts = $post_model->get_popular(5);

    $smarty->assign('posts', $popular_posts);
    $smarty->view('subpage.tpl');
}

... и шаблон подстраницы:

subpage.tpl

{include file="header.tpl"}

<div id="sidebar">

 {foreach from='posts' item='post'}
    <a href="post.php?id={$post.id}">{$post.title}</a>
 {/foreach}

</div>

{include file="footer.tpl"}

Новая подстраница контроллер знает, что на подстранице должны отображаться только 5 популярных сообщений. Подстраница view знает, что подстраницы должны помещать список сообщений в боковую панель div.

Теперь на главной странице мы хотим выделить новые сообщения. Мы можем добиться этого, изменив страницу homepage.tpl.

{include file="header.tpl"}

 {foreach from='posts' item='post'}
    {if $post.date_created == $smarty.now}
        <a class="new-post" href="post.php?id={$post.id}">{$post.title}</a>
    {else}
        <a href="post.php?id={$post.id}">{$post.title}</a>
    {/if}
 {/foreach}

{include file="footer.tpl"}

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

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

post_model.php

class post_model {
    public function get_popular($number) {
        $sql = "SELECT * , COUNT(comments.id) as comment_count
                FROM posts 
                INNER JOIN comments ON comments.post_id = posts.id
                ORDER BY comment_count DESC 
                LIMIT $number";
        $result = mysql_query($sql);
        while($row = mysql_fetch_assoc($result)) {
            $array[] = $row;
        }
        return $array;
    } 
}

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

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

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

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

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

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

Ответ 3

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

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

В представлении у вас есть указатели на модель, которая придерживается этого контракта, но я могу использовать простой объект:

 public class User
 {
    public string FirstName;
    public string LastName;
 }

Да, я понимаю, что общественные поля плохие.:-) Я также могу использовать DataTable в качестве модели, пока он выдает FirstName и LastName. Это может быть не лучший пример, но дело в том, что модель не привязана к виду. Представление привязано к контракту, и конкретная модель придерживается этого контракта.

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

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

Что касается книг по пониманию ООП, я, как правило, люблю книги по шаблонам, поскольку они представляют собой более практичные способы понимания концепций. Вы можете найти материал для праймера в Интернете. Если вы хотите, чтобы книга по языковым языкам не была описана, и немного подумайте, книга "Банда четырех" - это хорошее место для начала. Для более творческих типов, я бы сказал, Heads Up Design Patterns.

Ответ 4

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

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

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

Ответ 5

Может ли кто-нибудь указать аргумент [...], который объясняет, почему MVC - хорошая идея?

Это помогает вам осознать, что ваш код делает, потому что они изолированы друг от друга.

Ответ 6

  • Рассмотрим количество кода, который войдет в этот единственный класс, если вы хотите разоблачить ту же самую информацию, а не только как Html в пользовательском интерфейсе, но как часть из RSS, JSON, службы отдыха с XML, [вставить что-то еще].
  • Это нечеткая абстракция, означающая, что она пытается дать вам понять, что это будет единственная вещь, которая когда-либо узнает эти данные, но это не может быть полностью правдой. Допустим, вы хотите предоставить услугу, которая будет интегрирована с несколькими внешними третьими лицами. Вам будет очень сложно заставить их использовать ваш конкретный язык для интеграции с вашим сервисом (так как это класс, единственный элемент, который может когда-либо использовать данные), или, если с другой стороны, вы раскрываете некоторые свои данные вы не скрываете данные от сторонних систем.

Обновление 1:. Я подробно рассмотрел всю статью и, будучи старой статьей (99), на самом деле не о MVC, как мы знаем сегодня, против объектно-ориентированного, есть аргументы, которые противоречат СРП.

Вы могли бы прекрасно соответствовать тому, что он сказал, и обрабатывать вышеупомянутый сценарий, который я упомянул, с конкретными классами, ответственными за перевод публичного контракта объекта в разные форматы: основная проблема заключалась в том, что у нас не было четкого места для обрабатывать изменения, а также что мы не хотим, чтобы информация повторялась на всем протяжении. Таким образом, в случае с html вы могли бы отлично иметь элемент управления, который отображает информацию или класс, которые преобразуют его в html или [вставляют повторное использование mecanism здесь].

Кстати, у меня была вспышка с бит RMI. Во всяком случае, в этом примере вы можете видеть, что он привязан к коммуникационному меканизму. Тем не менее, каждый вызов метода удаленно обрабатывается. Я думаю, что он также очень беспокоился о том, что разработчики, имеющие код, вместо того, чтобы получать один объект и работать с возвращенной информацией, имели множество небольших вызовов Get, чтобы получить тонны различных фрагментов информации.

Ps. Я предлагаю вам прочитать информацию о DDD и Solid, что, как я уже сказал, для SRP, я бы не сказал, что это тот тип вещей, о котором автор был спокоен о

Ответ 7

Я не знаю хороших книг по теме MVC, но по собственному опыту. Например, в веб-разработке вы работаете с дизайнерами, а иногда и с dbas. Разделение логики из презентации позволяет вам лучше работать с людьми с разными навыками, потому что дизайнеру не нужно много говорить о кодировании и наоборот. Кроме того, для концепции DRY вы можете сделать свой код менее повторяющимся и более простым в обслуживании. Ваш код будет более многоразовым и сделает вашу работу намного проще. Это также сделает вас лучшим разработчиком, потому что вы станете более организованным и подумаете о программировании по-другому. Поэтому, даже если вам нужно работать над чем-то, что не является MVC, у вас может быть другой подход к архитектуре проекта, потому что вы понимаете концепции MVC.

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

Ответ 8

My 2c.. еще одна вещь, которую вы могли бы сделать, помимо того, что было сказано, - это использовать Decorators ваших объектов User. Таким образом, вы можете украсить пользователя по-разному в зависимости от контекста. Таким образом, вы получите WebUser.class, CVSUser.class, RSSUser.class и т.д.

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