Понимание шаблона MVC - PullRequest
43 голосов
/ 22 июля 2010

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

Из того, что я понял, View - это то, что видит пользователь.Так что это вообще окно / форма.Controller находится между View и Model.Контроллер сделает поток данных в обоих направлениях.Он также сохранит состояние при необходимости (если у меня есть мастер с 5 шагами, это ответственность Controller, чтобы убедиться, что они сделаны в правильном порядке и т. Д.).Model, - это то, где живет ядро ​​моей логики приложения.

Правильно ли это представление?

Чтобы попытаться превратить это во что-то более значимое, я попытаюсь набросать простоеПример с WinForms (не ASP.NET или WPF, пожалуйста! - для толпы Java, насколько я понимаю, Swing работает аналогично WinForms!), чтобы проверить, правильно ли я это понимаю, и я поднимувопросы, к которым я всегда обращаюсь при этом.


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

class MyNumbers {
    private IList<int> listOfNumbers = new List<int> { 1, 3, 5, 7, 9 };

    public IList<int> GetNumbers() {
        return new ReadOnlyCollection<int>(listOfNumbers);
    }
}

Теперь пришло время сделать мой Controller:

class Controller
{
    private MyNumbers myNumbers = new MyNumbers();

    public IList<int> GetNumbers() {
        return myNumbers.GetNumbers();
    }
}

У View должно быть просто ListBox, в котором в качестве элементов указаны все найденные числав MyNumbers.

Теперь возникает первый вопрос:

Должен ли Controller отвечать за создание MyNumbers?В этом простом случае я считаю его приемлемым (так как MyNumbers будет делать то же самое, несмотря ни на что, и не имеет ассоциированного состояния).Но давайте предположим, что я хотел бы использовать для всех различных контроллеров, мое приложение имеет один и тот же экземпляр MyNumbers.Я должен был бы перейти к этому Controller (и всем остальным, кто нуждается в этом) тот экземпляр MyNumbers, который я хочу использовать.Кто будет нести ответственность за это?В этих примерах WinForms это будет View?Или это будет класс, который создает View?

Обращая вопрос: каков порядок создания этих трех частей?Какой код вызывал «владелец» MVC для его создания?Должен ли Controller создать View и Model?Должен ли View создать экземпляр Controller и Controller * Model?

Второй вопрос:

Как должен выглядеть метод main, предполагая, что я толькохотите, чтобы мое приложение имело Use Case этот Controller портрет?

Третий:

Почему на следующей диаграмме MVC у View есть стрелка к Model?Разве Controller не должен всегда быть мостом между View и Model?

alt text


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

Спасибо!

Ответы [ 10 ]

26 голосов
/ 22 июля 2010

Самый простой способ получить дескриптор на MVC - это использовать его в среде, которая обеспечивает его, как говорится.

  • Модель взаимодействует с источником данных (БД или любым другим) и предоставляет вам доступ к вашим данным.
  • Представление взаимодействует с внешним миром, откуда-то получает входные данные и передает данные контроллеру, а также слушает контроллер, чтобы убедиться, что оно отображает правильные данные.
  • Контроллер - это место, где происходит вся магия; Контроллер обрабатывает данные, отправляет события и обрабатывает изменения в обоих направлениях (в / из вида и в / из модели).

Эта диаграмма очень полезна (она имеет гораздо больше смысла, чем в Википедии): Диаграмма MVC http://java.sun.com/developer/technicalArticles/javase/mvc/images/Figure4.gif

Источник , и отличная статья о MVC!

3 голосов
/ 22 июля 2010

Что касается критики в моем посте, я подумал, что дам пост о том, как я склонен создавать MVC паттерн в PHP

В PHP я разбил фреймворк на несколько разделов, некоторые из которых являются нормальными, когда дело доходит до MVC.

Праймериз:

  • Контроллер
  • Модель
  • View

вторичность - ModelLayer

  • ViewLoader
  • Библиотека
  • ErrorLayer

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

Вот как я бы это структурировал

|---------|       |------------|       |------------|
| Browser | ----> | Controller | ----> |   Model    |
|---------|       |------------|       |------------|
     |                  |   |                |
     |                  |   |----------------|
     |                  |
     |            |------------|
     -------------|    View    |
                  |------------|

Из моей схемы я обычно обхожу соединение View <-> Model и делаю Controller <-> Model, а затем ссылка из Controller <-> View назначает данные.

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

class Registry
{
   static $storage = array();

   public static function get($key)
   {
       return isset(self::storage[$key]) ? self::storage[$key] : null;
   }

   public static function set($key,$object)
   {
       self::"storage[$key] = $object;
   }
}

Несколько более продвинутый в этом плане, поэтому, когда я впервые инициализирую объекты, я сохраняю их как Registry::set("View",new View());, так что они всегда доступны.

Итак, в моем контроллере находится базовый контроллер, я создаю несколько магических методов __get() __set(), так что любой класс, расширяющий контроллер, я могу легко вернуть запрос, например:

abstract class Controller
{
   public function __get($key)
   {
       //check to make sure key is ok for item such as View,Library etc

       return Registry::get($key); //Object / Null
   }
}

И пользовательский контроллер

class Controller_index extends Controller
{
    public function index()
    {
       $this->View->assign("key","value"); // Exucutes a method in the View class
    }
}

Модель также будет помещена в реестр, но ее можно будет вызывать только из ModelLayer

class Model_index extends ModelLayer_MySql
{
}

или

class Model_index extends ModelLayer_MySqli
{
}

или файловая система

class Model_file extends ModelLayer_FileSystem
{
}

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

Это не традиционный тип шаблона MVC, но его можно назвать Adoptive MVC.

Другие объекты, такие как View Loader, не следует помещать в реестр, поскольку они не предназначены специально для интересов пользователей, а используются другими объектами, такими как View

abstract class ViewLoader
{
   function __construct($file,$data) //send the file and data
   {
       //Include the file and set the data to a local variable
   }

   public function MakeUri()
   {
       return Registry::get('URITools')->CreateURIByArgs(func_get_args());
   }
}

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

Пример файла шаблона.

<html>
   <body>
      <?php $this->_include("another_tpl_file.php"); ?>
      <?php if(isset($this->session->admin)):?>

          <a href="<?php echo $this->MakeUri("user","admin","panel","id",$this->session->admin_uid) ?>"><?php echo $this->lang->admin->admin_link ?></a>

      <?php endif; ?>
   </body>
</html>

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

2 голосов
/ 22 июля 2010

Ответ на третий вопрос :

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

1 голос
/ 22 июля 2010

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

Controller контролирует, какой view используется. Так, скажем, если вы пишете на страницу, controller направит вас к input view (скажем), а если вы читаете ту же страницу, она направит вас на success view (скажем).

После записи на страницу controller передаст эти параметры соответствующему model, где находится логика, относящаяся к тому, что должно быть сделано с ними. Если есть ошибка, controller направит вас к error view.

Мои знания основаны на моем месячном опыте работы с Агави. Надеюсь, это поможет.

1 голос
/ 22 июля 2010

Почему на следующей диаграмме MVC вид имеет стрелку к модели?Разве Контроллер не должен всегда быть мостом между представлением и моделью?

Это модель MVC 2. Обычно это можно увидеть в корпоративном Java-приложении, где CONTROL занимается бизнесом, а также обрабатывает данные.От / до МОДЕЛИ и выберите, какой ВИД для рендеринга клиенту.При рендеринге клиенту VIEW будет использовать данные из MODEL:

альтернативный текст http://www.blogjava.net/images/blogjava_net/marco/7342/o_2.JPG

Вот пример, как получить доступ к данным из файла JSP (VIEW), который является компонентом (MODEL):

class Person {String name;} // MODEL
My name is ${bean.name}     // VIEW
1 голос
/ 22 июля 2010

Должен ли Контроллер отвечать за создание MyNumbers?

Я бы сказал: ' определенно не .'

Если шаблон MVC разработанчтобы отделить элементы M, V и C, как это может работать, если C просто создает M с помощью new MyNumbers()?

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

Но есть еще один элемент этой проблемы: вам, вероятно, не следует определять переменную myNumbers (внутри C) с конкретным типом среды выполнения, который вы намереваетесь использовать.Используйте интерфейс или абстрактный класс и оставьте его открытым для определения фактического типа времени выполнения.Таким образом, в будущем вы сможете заново реализовать интерфейс IMyNumbers для удовлетворения возникающих требований (тех, которые вы не знаете сегодня), и ваш компонент C продолжит работать отлично, ни один мудрый.

1 голос
/ 22 июля 2010

Это с Java, но, надеюсь, это поможет.

Для основного:

public static void main(String[] args) 
{
       MyNumbers myNums = new MyNumbers();  // Create your Model
       // Create controller, send in Model reference.      
       Controller controller = new Controller(myNums); 
}

Вашему контроллеру нужна ссылка на вашу модель. В этом случае контроллер фактически создает все компоненты Swing. Для C # вы можете оставить здесь инициализацию формы, но View / Form требуется ссылка на Model (myNums) и Controller (controller). Надеемся, что некоторые люди из C # могут помочь в этом. Представление также необходимо зарегистрировать в качестве наблюдателя модели (см. Шаблон наблюдателя).

Вот мой конструктор (с учетом вашего случая):

public NumberView(Controller controller, MyNumbers myNums)
{
      this.controller = controller; // You'll need a local variable for this
      this.myNums = myNums; //You'll need a local variable for this
      myNums.registerObserver(this); // This is where it registers itself
}

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

Итак, модель будет иметь:

public void notifyObservers()
{
    for (Observer o: observers)
    {
        o.update();  // this will call the View update below since it is an Observer
    }
}

Так что в представлении у вас будет что-то вроде этого:

public void update()
{
    setListBox(myNums.getNumbers());  // Or whatever form update you want
}

Надеюсь, это поможет. Я знаю, что это Java, но концепция все еще применяется. Вам нужно будет немного почитать шаблон Observer, чтобы полностью его получить. Удачи!

1 голос
/ 22 июля 2010

"Из того, что я понял, представление - это то, что видит пользователь. Так что, как правило, это окно / форма. Контроллер находится между представлением и моделью. Контроллер будет" обрабатывать "данные в обоих направлениях.также сохраняйте состояние, когда это необходимо (если у меня есть мастер с 5 шагами, это ответственность контроллера за то, чтобы они были выполнены в правильном порядке и т. д.) Модель - это то, где находится ядро ​​логики моего приложения. "

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

В ответ на ваш комментарий «Я не уверен, что яполностью понял вашу точку зрения. Контроллер проверяет ввод пользовательского интерфейса или модель, которая это делает? "

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

0 голосов
/ 22 июля 2010

Просмотр

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

  • знает только о модели

Модель

  • структура данных / представляет данные, которые представлены / не должны содержать бизнес-логику (может быть, самое большее, только некоторые данные / проверка структуры)
  • знает только о себе (представьте себе класс Person, который имеет только Имя и Возраст)

Контроллер

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

  • знает все (по крайней мере, вид и модель) и отвечает за склейку всего

0 голосов
/ 22 июля 2010
  • Вид рисует модель и представляет ее пользователю
  • Контроллер обрабатывает вводимые пользователем данные и переводит их в модификации модели
  • Модель содержит данные и логику модификации

Нет смысла переводить это в код.В любом случае, вы не получите правильного ответа.

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