Понимание шаблона MVC

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

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

Это представление корректно?

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


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

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 должен просто иметь a ListBox это имеет как объекты все числа, полученные в MyNumbers.

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

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

Изменение к лучшему вопроса: каков порядок инстанцирования этих 3 частей? Каков код что "владелец" MVC названный для создания его? Если Controller создайте обоих View и Model? Если View инстанцируйте Controller и Controller Model?

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

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

В-третьих:

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

alt text


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

Спасибо!

43
задан Community 8 February 2017 в 14:24
поделиться

10 ответов

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

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

Эта диаграмма очень полезна (она имеет гораздо больше смысла, чем диаграмма Википедии): MVC Diagram

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

26
ответ дан 26 November 2019 в 23:09
поделиться

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

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

Primaries:

  • Controller
  • Model
  • View

Secondariness - ModelLayer

  • ViewLoader
  • Library
  • ErrorLayer

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

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

|---------|       |------------|       |------------|
| 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, но его можно назвать адаптивным 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 loader, а не в класс 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>

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

3
ответ дан 26 November 2019 в 23:09
поделиться

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

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

2
ответ дан 26 November 2019 в 23:09
поделиться

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

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

В ответ на ваш комментарий "Я не уверен, что полностью понял вашу мысль. Проверяет ли контроллер ввод UI, или это делает модель?"

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

1
ответ дан 26 November 2019 в 23:09
поделиться

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

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

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

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

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

1
ответ дан 26 November 2019 в 23:09
поделиться

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

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

alt text
(источник: blogjava.net)

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

class Person {String name;} // MODEL
My name is ${bean.name}     // VIEW
1
ответ дан 26 November 2019 в 23:09
поделиться

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

Для main:

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# вы можете оставить инициализацию формы здесь, но представлению/форме нужна ссылка на модель (myNums) и контроллер (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
}

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

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

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
ответ дан 26 November 2019 в 23:09
поделиться

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

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

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

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

1
ответ дан 26 November 2019 в 23:09
поделиться
  • View рисует модель и представляет ее пользователю
  • Controller обрабатывает пользовательский ввод и переводит их в модификации в модели
  • Model хранит данные и логику модификации

Нет смысла переводить это в код. Вы все равно не сможете сделать это правильно.

0
ответ дан 26 November 2019 в 23:09
поделиться

View

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

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

Model

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

. 126]знает только о себе (вспомните класс Person, у которого есть только имя и возраст)

Controller

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

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

0
ответ дан 26 November 2019 в 23:09
поделиться