Я испытываю некоторые затруднения при понимании Шаблона 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
?
У меня будут один или еще два вопроса, но они, вероятно, будут иметь больше смысла, справился, я понимаю эту первую деталь. Или возможно после того, как я понимаю, что первый вопрос все другие разрывают.
Спасибо!
Самый простой способ разобраться с MVC - использовать его во фреймворке, который его реализует, и это при том, что...
Эта диаграмма очень полезна (она имеет гораздо больше смысла, чем диаграмма Википедии):
Источник, а также отличная статья о MVC!
Что касается критики в моем посте, я решил написать о том, как я создаю MVC-паттерн в PHP
В PHP я разделяю фреймворк на несколько секций, из которых некоторые являются обычными для MVC.
Primaries:
Secondariness - ModelLayer
Внутри контроллера я обычно разрешаю доступ всем вторичным слоям, а 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>
Надеюсь, мои примеры помогут вам понять немного больше.
Ответ на третий вопрос :
Когда модель изменяется, она уведомляет представление, затем оно получает данные из модели, используя свои геттеры.
"Насколько я понял, представление - это то, что видит пользователь. То есть это обычно окно/форма. Контроллер находится между представлением и моделью. Контроллер будет "обрабатывать" данные в обоих направлениях. Он также сохраняет состояние, когда это необходимо (если у меня есть мастер с 5 шагами, то контроллер отвечает за то, чтобы они были выполнены в правильном порядке и т.д.). Модель - это то место, где находится ядро логики моего приложения."
Это почти верно. Контроллер не сохраняет данные. Он вызывает службу, которая сохраняет данные. Причина в том, что сохранение данных - это не просто вызов сохранения. Вы можете захотеть выполнить проверку достоверности данных, чтобы убедиться, что они соответствуют вашим бизнес-потребностям. Вы можете захотеть провести аутентификацию, чтобы убедиться, что данные могут быть сохранены пользователем. Если вы сделаете это в сервисе, то у вас будет хороший набор функциональности, который вы сможете использовать снова и снова, скажем, для веб-приложения и веб-сервиса. Если вы сделаете это в контроллере, скажем, для веб-приложения, когда вы перейдете к написанию веб-сервиса, вам придется рефакторить и/или дублировать код.
В ответ на ваш комментарий "Я не уверен, что полностью понял вашу мысль. Проверяет ли контроллер ввод UI, или это делает модель?"
Ваш контроллер должен только контролировать, какие пути бизнес-функциональности выполняются. Именно так. Контроллеры должны быть самой простой частью кода для написания. Вы можете сделать некоторую валидацию на gui (т.е. в представлении, например, убедиться, что адреса электронной почты правильно отформатированы, текстовые вводы не превышают максимальных значений), но бизнес-слой также должен валидировать ввод - по причине, которую я упоминал ранее, чтобы, когда вы начнете создавать больше конечных точек, вам не пришлось рефакторить.
Должен ли Контролер нести ответственность для создания MyNumbers?
Я бы сказал: « определенно не .»
Если шаблон MVC предназначен для разделения элементов M, V и C, как это может работать, если C просто создает экземпляры буква M с new MyNumbers ()
?
В Java мы бы использовали здесь что-то вроде Spring Framework . Вам нужен способ выразить отношения зависимости - или, скорее, подробности того, как они выполняются - в файле конфигурации или другом подходящем месте ( то есть не в скомпилированном коде).
Но у этой проблемы есть еще один элемент: вам, вероятно, не следует определять переменную myNumbers
(внутри C) с конкретным типом среды выполнения, который вы собираетесь использовать. Используйте интерфейс или абстрактный класс и оставьте открытым вопрос о фактическом типе среды выполнения. Таким образом, в будущем вы можете повторно реализовать интерфейс IMyNumbers для удовлетворения возникающих требований (тех, которых вы не знаете сегодня), и ваш компонент C продолжит работать безупречно, никем не мудрее.
Почему в следующей диаграмме MVC представление имеет стрелку к модели? Разве контроллер не должен быть связующим звеном между представлением и моделью?
Это модель MVC 2. Обычно ее можно увидеть в корпоративных приложениях Java где CONTROL занимается бизнесом, а также обрабатывает данные от/к МОДЕЛИ и выбирает, какой VIEW отдать клиенту. При отображении клиенту, VIEW будет использовать данные из MODEL:
(источник: blogjava.net)
Вот пример, как получить доступ к данным из файла JSP (VIEW), который содержит боб (MODEL):
class Person {String name;} // MODEL
My name is ${bean.name} // VIEW
Это из 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, чтобы полностью понять его. Удачи!
Я попытаюсь ответить на этот вопрос с относительно менее технической точки зрения, если это того стоит. Я попробую пройтись по общему примеру.
Контроллер
управляет тем, какой вид
используется. Так, скажем, если вы пишете на страницу, контроллер
направит вас к input view
(скажем), а если вы читаете ту же страницу, он направит вас к success view
(скажем).
После записи на страницу контроллер
передаст эти параметры соответствующей модели
, где находится логика, относящаяся к тому, что с ними нужно сделать. Если возникнет ошибка, то контроллер
направит вас к представлению ошибки
.
Мои знания основаны на месячном опыте работы с Agavi. Надеюсь, это поможет.
Нет смысла переводить это в код. Вы все равно не сможете сделать это правильно.
View
пользовательский интерфейс / отвечает за ввод вывод / некоторую валидацию / должен иметь способ уведомления внешнего мира о событиях на уровне UI
знает только о модели
Model
. 126]знает только о себе (вспомните класс Person, у которого есть только имя и возраст)
Controller
отвечает за бизнес-логику / создает представления и приклеивает к ним соответствующие модели / должен уметь реагировать на события представления / получает доступ к другим уровням приложения (персистентность / внешние сервисы / бизнес-слои и т.д.). )
знает все (по крайней мере, представление и модель) и отвечает за склеивание всего вместе