Удобный подход к веб-разработке: Модель MVC. Паттерны для новичков: MVC vs MVP vs MVVM

Прочие модели 04.08.2019
Прочие модели

В этой статье мы напишем «каркас» нашего проекта. Под словом «каркас» я подразумеваю рабочий код, который будет иметь в своей основе MVC подход, то есть будет иметь четкое разделение логики на контролеры, экшены, шаблоны (представления) и модели.

И так начнем, как я уже писал в предыдущей статье, паттерн MVC подразумевает одну точку входа – index.php, через это скрипт будут проходить все запросы, через него будет работать вся логика проекта. Для того чтобы реализовать такой подход необходимо настроить сервер, подразумевается, что сайт работает на сервере apache, поэтому нам достаточно создать файл.htaccess, в котором мы укажем правила маршрутизации URL. Помимо определения точки входа, маршрутизация позволяет создавать ЧПУ(человеко-понятные урлы). То есть после правильной настройки, адреса страниц буду выглядеть вот так site.ru/article/new.
Для начала, давайте составим.htaccess, который перенаправит обработку всех страниц на скрипт index.php. Код выглядит вот так:

RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?route=$1

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

Echo "test";

Теперь можно проверять работу перенаправления, введите любой адрес и посмотрите, что получиться: test-mvc.web/sdf/sdf/ или test-mvc.web/sdf/sdf/2342/не важно, на экране в любом случае, должно появиться «Test». Если Вы увидели эту надпись, значит, у нас все получилось.
Продолжим, давайте для удобства создадим в корне сайта файл config.php, в котором будем задавать различные константы, облегчающие своим существование настройку сайта. Это могут быть различные пути к скриптам, подступы к базе данных и так далее. Сейчас в конфиге давайте зададим следующее:

// Задаем константы: define ("DS", DIRECTORY_SEPARATOR); // разделитель для путей к файлам $sitePath = realpath(dirname(__FILE__) . DS); define ("SITE_PATH", $sitePath); // путь к корневой папке сайта // для подключения к бд define("DB_USER", "root"); define("DB_PASS", ""); define("DB_HOST", "localhost"); define("DB_NAME", "blog_mvc");

Для того, чтобы константы и другие данные конфига мы могли использовать во всем проекте, в файле index.php необходимо подключить скрипт config.php.
Помимо подключения файла с настройками, в index.php нужно создать подключение к базе данных, подключить скрипт с ядром сайта и запустить роутер, в котором будет происходить маршрутизация.
Теперь по порядку, создание соединения с базой данных будет находиться в index.php для того, чтобы соединение открывалось только один раз. Единожды открыв соединение, мы сможем использовать его во всех контроллерах и моделях, но об этом чуть позже. Сейчас просто создадим соединение с базой. Для работы с бд я решил использовать PDO. Подробнее почитать про PDO можно .
Ядро сайта расположим в папке core и назовем скрипт core.php, тут мы напишем функцию, которая будет сама подключать, необходимы для работы классы. Такая функция очень облегчит и упростит нам работу с контролерами, моделями и тд. Поскольку, забегая вперед скажу, что каждый контролер и каждая модель будут представлять собой отдельный класс.
Помимо авто подключения классов, добавим в ядро создания хранилища (реестра), в котором будем хранить все необходимые объекты и переменные, которые могут пригодиться в любом месте проекта.
Роутер тоже подключим в индексном файле, он будет анализировать URL и подключать необходимый контроллер и экшен. Что такое контролер я писал в предыдущей статье, а информацию про экшен я пропустил умышленно, не став нагружать лишней информацией. Так что же такое экшен?
Контролер это класс, в котором заключены различные методы, при MVC подходе каждый метод будет являться экшеном. То есть экшен(action) – это метод класса, который будет обрабатывать данные и передавать их в представление (в шаблон). Может быть, пока не совсем понятно, но после примера все станет на свои места.
На данном этапе теории достаточно, давайте перейдем к практике. Приведу код файлов, работу которых, я описывал выше.
Код скрипта index.php:

// включим отображение всех ошибок error_reporting (E_ALL); // подключаем конфиг include ("/config.php"); // Соединяемся с БД $dbObject = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASS); // подключаем ядро сайта include (SITE_PATH . DS . "core" . DS . "core.php"); // Загружаем router $router = new Router($registry); // записываем данные в реестр $registry->set ("router", $router); // задаем путь до папки контроллеров. $router->setPath (SITE_PATH . "controllers"); // запускаем маршрутизатор $router->start();

Скрипт core.php:

// Загрузка классов "на лету" function __autoload($className) { $filename = strtolower($className) . ".php"; // определяем класс и находим для него путь $expArr = explode("_", $className); if(empty($expArr) OR $expArr == "Base"){ $folder = "classes"; }else{ switch(strtolower($expArr)){ case "controller": $folder = "controllers"; break; case "model": $folder = "models"; break; default: $folder = "classes"; break; } } // путь до класса $file = SITE_PATH . $folder . DS . $filename; // проверяем наличие файла if (file_exists($file) == false) { return false; } // подключаем файл с классом include ($file); } // запускаем реестр (хранилище) $registry = new Registry;

Класс хранилища Registry.php, будет находиться в папке /classes/

// Класс хранилища Class Registry { private $vars = array(); // запись данных function set($key, $var) { if (isset($this->vars[$key]) == true) { throw new Exception("Unable to set var `" . $key . "`. Already set."); } $this->vars[$key] = $var; return true; } // получение данных function get($key) { if (isset($this->vars[$key]) == false) { return null; } return $this->vars[$key]; } // удаление данных function remove($var) { unset($this->vars[$key]); } }

Код файла router.php, который находиться в папке /classes/

// класс роутера Class Router { private $registry; private $path; private $args = array(); // получаем хранилище function __construct($registry) { $this->registry = $registry; } // задаем путь до папки с контроллерами function setPath($path) { $path = trim($path, "/\\"); $path .= DS; // если путь не существует, сигнализируем об этом if (is_dir($path) == false) { throw new Exception ("Invalid controller path: `" . $path . "`"); } $this->path = $path; } // определение контроллера и экшена из урла private function getController(&$file, &$controller, &$action, &$args) { $route = (empty($_GET["route"])) ? "" : $_GET["route"]; unset($_GET["route"]); if (empty($route)) { $route = "index"; } // Получаем части урла $route = trim($route, "/\\"); $parts = explode("/", $route); // Находим контроллер $cmd_path = $this->path; foreach ($parts as $part) { $fullpath = $cmd_path . $part; // Проверка существования папки if (is_dir($fullpath)) { $cmd_path .= $part . DS; array_shift($parts); continue; } // Находим файл if (is_file($fullpath . ".php")) { $controller = $part; array_shift($parts); break; } } // если урле не указан контролер, то испольлзуем поумолчанию index if (empty($controller)) { $controller = "index"; } // Получаем экшен $action = array_shift($parts); if (empty($action)) { $action = "index"; } $file = $cmd_path . $controller . ".php"; $args = $parts; } function start() { // Анализируем путь $this->getController($file, $controller, $action, $args); // Проверка существования файла, иначе 404 if (is_readable($file) == false) { die ("404 Not Found"); } // Подключаем файл include ($file); // Создаём экземпляр контроллера $class = "Controller_" . $controller; $controller = new $class($this->registry); // Если экшен не существует - 404 if (is_callable(array($controller, $action)) == false) { die ("404 Not Found"); } // Выполняем экшен $controller->$action(); } }

Теперь необходимо создать папки для хранения контроллеров, шаблонов и моделей – в корне создадим три папки controllers, views и models. И создадим несколько тестовых файлов /controllers/index.php, /views/index/index.php и /models/model_users.php, а теперь заполним файлы:
Для контроллера:

// контролер Class Controller_Index Extends Controller_Base { // шаблон public $layouts = "first_layouts"; // экшен function index() { $model = new Model_Users(); $userInfo = $model->getUser(); $this->template->vars("userInfo", $userInfo); $this->template->view("index"); } }

Для отображения(/views/index/index.php)

Test view
id:
name:

И модель:

// модель Class Model_Users{ public function getUser(){ return array("id"=>1, "name"=>"test_name"); } }

Как вы могли заметить, класс контролера наследуется от родительского класса Controller_Base. Это сделано, для того, чтобы упростить класс контролера. Поскольку нам еще необходимо подключать класс для работы с шаблонами, его подключение вынесено в Controller_Base.
Приведу его код, он расположен в папке /classes/ и называется controller_base.php:

// абстрактый класс контроллера Abstract Class Controller_Base { protected $registry; protected $template; protected $layouts; // шаблон public $vars = array(); // в конструкторе подключаем шаблоны function __construct($registry) { $this->registry = $registry; // шаблоны $this->template = new Template($this->layouts, get_class($this)); } abstract function index(); }

Теперь осталось только разобраться с шаблонами. В абстрактном классе Controller_Base мы вызываем класс Template и передаем ему имя шаблона и имя контроллера.
Код класса Template, который лежит тут /classes/ и называется template.php

// класс для подключения шаблонов и передачи данных в отображение Class Template { private $template; private $controller; private $layouts; private $vars = array(); function __construct($layouts, $controllerName) { $this->layouts = $layouts; $arr = explode("_", $controllerName); $this->controller = strtolower($arr); } // установка переменных, для отображения function vars($varname, $value) { if (isset($this->vars[$varname]) == true) { trigger_error ("Unable to set var `" . $varname . "`. Already set, and overwrite not allowed.", E_USER_NOTICE); return false; } $this->vars[$varname] = $value; return true; } // отображение function view($name) { $pathLayout = SITE_PATH . "views" . DS . "layouts" . DS . $this->layouts . ".php"; $contentPage = SITE_PATH . "views" . DS . $this->controller . DS . $name . ".php"; if (file_exists($pathLayout) == false) { trigger_error ("Layout `" . $this->layouts . "` does not exist.", E_USER_NOTICE); return false; } if (file_exists($contentPage) == false) { trigger_error ("Template `" . $name . "` does not exist.", E_USER_NOTICE); return false; } foreach ($this->vars as $key => $value) { $$key = $value; } include ($pathLayout); } }

Если вы внимательно прочитали код, то наверняка поняли, что для отображения на страницах у нас используется шаблон first_layouts и вьюха(отображение) index.php – ее код я приводил чуть выше. Все что нам осталось, это создать файл шаблона first_layouts. Расположим его в папке /views/layouts/first_layouts.php
Шаблон будет содержать вот такой код:

header

footer

Вот и все, на этом создание «каркаса» закончено. Сейчас у нас получилась самая простая структура, использующая в своей основе паттерн MVC. В этой статье я не стал затрагивать работу с базой данных, только вскользь упомянул ее, поскольку статья и так получилась большая. Непосредственно работу с базой данных я опишу в следующей статье.
На этом статья закончена, скачать исходники можно архивом .

SRC:
O’Rеillу. ActionScript 3.0 Design Patterns.
Глава 12. Model-View-Controller Pattern.


Что такое шаблон Модель-Представление-Контроллер?

«Модель-Представление-Контроллер» (Model-View-Controller, MVC) - это составной шаблон, или несколько шаблонов, работающих совместно для реализации сложных приложений. Наиболее часто этот шаблон используется для создания интерфейсов приложений, и как следует из названия, состоит из трех элементов:

Модель (Model)
Содержит данные и логику приложения для управления состоянием этого приложения

Представление (View)
Реализует пользовательский интерфейс и состояние приложения, наблюдаемые на экране

Контроллер (Controller)
Обрабатывает действия пользователя, влияющие на состояние приложения.

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

Модель

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

Представление

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

Контроллер

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

Допустим, наш цифровой плеер имеет кнопки Громче и Тише в Представлении. Громкость звука является переменной состояния. Модель будет отслеживать эту переменную, чтобы менять значение этой переменной в соответствии с логикой приложения. Если значение громкости звука проградуировать от 0 до 10, Контроллер определит, насколько нужно прибавить или убавить звук при одиночном нажатии на одну из этих кнопок. Поведение может сообщить Модели, что нужно увеличить громкость на 0,5 или на 0,1, или любое другое значение, как задано программно. В таком ключе, Контроллер - это специфичные реализации, которые определяют, каким образом приложение ответит на ввод пользователя.

Хотя каждый элемент в триаде MVC имеет отдельную и уникальную зону ответственности, они не функционируют в изоляции. На самом деле, с тем чтобы составить шаблон MVC, каждому элементу следует коммуницировать с остальными. Что это значит, мы рассмотрим ниже.

Взаимодействие элементов MVC

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

  1. Пользователь взаимодействует с элементом интерфейса (например, нажимает на кнопку в Представлении).
  2. Представление отсылает событие нажатия Контроллеру, чтобы решить, как это нажатие обработать.
  3. Контроллер меняет Модель на основе того, что он решил относительно нажатия кнопки.
  4. Модель информирует Представление о том, что состояние Модели изменилось.
  5. Представление читает информацию о состоянии в Модели и самостоятельно видоизменяется.

Это очень простая схема того, как взаимодействуют элементы MVC. В некоторых случаях Контроллер может просто указать Представлению чтобы оно изменилось. Это единственный случай, когда изменения в Представлении становятся необходимыми из-за действий пользователя и не требуют изменений в Модели, а просто приводят к одним только визуальным изменениям. Например, вспомните о том, как пользователь выбирает песню на цифровом плеере. Он выбирает песню из списка кнопками прокрутки. Представление должно сообщить Контроллеру, что нажаты кнопки Листать вверх или Листать вниз, но Контроллеру не нужно информировать об этом Модель. Он напрямую говорит Представлению прокрутить список песен в нужном направлении. Такое действие пользователя не требует изменений в Модели. Однако, когда пользователь выберет песню из списка и запустит её на воспроизведение, Контроллер изменит Модель, чтобы отразить это изменение в значении воспроизводимой в данный момент песни.

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

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

Взгляните на Рисунок 12-1 и обратите внимание на направление стрелок. Они показывают, кто инициирует взаимодействие между элементами. Для того, чтобы один MVC-элемент смог сообщаться с другим, ему нужно знать о нем и владеть ссылкой на этот элемент.

Думайте о Модели, Представлении и Контроллере как о трех разных классах. Давайте посмотрим, каким классам нужно иметь ссылки на остальные классы:

Представление
Ему нужно иметь ссылку и на Модель, и на Контроллер

Контроллер
Ему необходимо владеть ссылкой на Модель

Мы начали с того что заявили, что MVC – это составной шаблон, который объединяет несколько шаблонов. Вам, должно быть, интересно, какие шаблоны вошли в данный составной шаблон. Или, точнее, чем они могут быть представлены? Главным преимуществом использования шаблона MVC является возможность разъединять его на составляющие три элемента. Это позволяет нам увязать несколько Представлений с одной Моделью, заменять Модели и Контроллеры, не затрагивая другие элементы. Но некоторые элементы в триаде MVC должны поддерживать ссылки на остальные элементы, а также поддерживать активный обмен данными между ними. Как мы можем назвать такое разделение? Это имеет отношение к паттернам Observer (Обозреватель) , Strategy (Стратегия) и Composite (Компоновщик) .

Внедрение шаблонов в MVC

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

Каждая Модель может иметь несколько Представлений, с ней связанных. Так же, Представления могут быть сложными, с несколькими окнами или панелями, внутри которых находятся другие элементы пользовательского интерфейса. Например, такие элементы интерфейса как кнопки, текстовые поля, списки, ползунки и т.п. могут быть сгруппированы в панель с закладками, а панель в свою очередь может быть частью окна наравне с другими панелями. Каждая кнопка или группа кнопок может быть Представлением. То же самое с коллекцией текстовых полей. Представляется полезным обращаться с панелью или окошком, которые содержат коллекции простых Представлений, таким же образом, как мы обращаемся с любыми другими Представлениями. Именно здесь использование шаблона Компоновщик сэкономит нам много сил (см. Главу 6). Почему реализация шаблона Компоновщик столь полезна в данном контексте? Если Представления могут быть вложенными, а они таковы, если созданы с применением шаблона Компоновщик, процесс обновления упрощается. Событие обновления автоматически обойдет все потомственные Представления. Создание сложных Представлений становится проще, когда нет необходимости рассылать индивидуальные сообщения об обновлении каждому вложенному Представлению.

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

Минималистичный пример шаблона MVC

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

Модель как Конкретный Субьект в шаблоне Обозреватель

Взаимоотношения между Моделью и Представлением - это связь между Субъектом и Обозревателем (См. Главу 8). Модель должна реализовать интерфейс Субъекта, который является частью шаблона Обозреватель. К счастью, ActionScript 3.0 имеет встроенные классы, уже реализующие этот принцип, используя модель событий ActionScript чтобы информировать Обозревателей об изменениях.

Класс EventDispatcher в ActionScript 3.0

Класс EventDispatcher снабжен интерфейсом IEventDispatcher. Наравне с другими методами, интерфейс IEventDispatcher определяет следующие методы, необходимые для субъекта в шаблоне Обозреватель. (См. документацию по AS3 для подробной информации о всех параметрах методов).

addEventListener(type :String , listener:Function , useCapture:Boolean = false , priority:int = 0 , useWeakReference:Boolean = false ) :void removeEventListener(type :String , listener:Function , useCapture:Boolean = false ) :void dispatchEvent(event:Event) :Boolean

Чтобы Модель могла выступить в качестве Конкретного Субъекта в шаблоне Обозреватель, необходимо реализовать интерфейс IEventDispatcher. Однако, самый простой способ для определенного пользовательского класса заполучить способность распространять события – это наследовать от класса EventDispatcher.

Обозреватель регистрирует методы слушателя, чтобы получать уведомлении от объектов EventDispatcher, методом addEventListener().

Модель

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

Пример 12-1. IModel.as

package { import flash.events .* ; public interface IModel extends IEventDispatcher { function setKey(key :uint) :void ; function getKey() :uint; } }

Интерфейс IModel, показанный в Примере 12-1, расширяет интерфейс IEventDispatcher и определяет пару методов для прочтения и установки кода символа последней нажатой клавиши. Поскольку интерфейс IModel расширяет IEventDispatcher, любой класс, реализующий его, должен реализовать все методы, определенные в обоих интерфейсах. Класс Model, показанный в Примере 12-2, реализует интерфейс IModel.

Пример 12-2. Model.as

package { import flash.events .* ; public class Model extends EventDispatcher implements IModel { private var lastKeyPressed:uint=0 ; public function setKey(key :uint) :void { this .lastKeyPressed =key ; dispatchEvent(new Event(Event.CHANGE ) ) ; // распространяется событие } public function getKey() :uint { return lastKeyPressed; } } }

Класс Model расширяет класс EventDispatcher, который уже реализовал интерфейс IEventDispatcher. Обратите внимание, что функция dispatchEvent() вызывается внутри метода setKey(). Она отсылает событие CHANGE всем зарегистрированным обозревателям, как только значение lastKeyPressed изменится внутри метода setKey().

Контроллер как Конкретная Стратегия в шаблоне Стратегия.

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

Контроллер

В нашем минималистичном примере поведение, требуемое от Контроллера, это всего лишь принятие события о нажатии клавиши. IKeyboardInputHandler - это интерфейс Стратегии (Пример 12-3), где определен единственный метод keyPressHandler().

Пример 12-3. IKeyboardInputHandler.as

package { import flash.events .* ; public interface IKeyboardInputHandler { function keyPressHandler(event:KeyboardEvent) :void ; } }

Конкретным Контроллером будет класс Controller (Пример 12-4), который реализует интерфейс IKeyboardInputHandler.

Пример 12-4. Controller.as

package { import flash.events .* ; public class Controller implements IKeyboardInputHandler { private var model:IModel; public function Controller(aModel:IModel) { this .model =aModel; } public function keyPressHandler(event:KeyboardEvent) :void { model.setKey (event.charCode ) ; // изменяем модель } } }

Обратите внимание, что Контроллер имеет конструктор, который в качестве параметра принимает экземпляр Модели. Это необходимо для того, чтобы Контроллер смог установить связь с Моделью, как это показано на Рисунке 12-1. Поэтому необходимо хранить ссылку на Модель.

Метод keyPressHandler() принимает событие пользовательского интерфейса (в данном случае KeyboardEvent) как параметр, и потом решает как его обработать. В нашем примере он просто устанавливает код нажатой клавиши в Модели.

Представление как Конкретный Обозреватель в шаблоне Обозреватель и Контекст в шаблоне Стратегия

Представление, возможно, наиболее сложный элемент в шаблоне MVC. Он играет роль интегрирующей части в реализации шаблонов как Обозревателя, так и Стратегии, что формирует основу его взаимоотношения с Моделью и Контроллером. Класс View, показанный в Примере 12-5, реализует Представление в минималистичном примере.

Пример 12-5. View.as

package { import flash.events .* ; import flash.display .* ; public class View { private var model:IModel; private var controller:IKeyboardInputHandler; public function View(aModel:IModel,oController:IKeyboardInputHandler,target :Stage ) { this .model =aModel; this .controller =oController; // подписывается на получение уведомлений от Модели model.addEventListener (Event.CHANGE ,this .update ) ; // подписывается на получение нажатий клавиш от сцены target .addEventListener (KeyboardEvent.KEY_DOWN ,this .onKeyPress ) ; } private function update(event:Event) :void { // получение данных от Модели и обновление Представления trace (model.getKey () ) ; } private function onKeyPress(event:KeyboardEvent) :void { // обработка передается в Контроллер (Стратегия) на обработку controller.keyPressHandler (event) ; } } }

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

Кроме того что класс View рисует пользовательский интерфейс, он выполняет еще пару важных задач. Он регистрируется у Модели для получения событий об обновлении, и делегирует Контроллеру обработку ввода пользователя. В нашем примере Представление не имеет внешнего видимого присутствия на сцене, но отображает состояние Модели в окне вывода Output. Ему нуждается в получении события нажатия клавиши, и регистрирует метод onKeyPress() для получения события KEY_DOWN от сцены. Вторая задача – это зарегистрировать метод слушателя update() для получения события CHANGE от модели. При получении уведомления об изменении, метод update() прочитывает код последней нажатой клавиши из Модели и печатает его в окне вывода, используя функцию trace().

Построение триады MVC

Мы рассмотрели реализацию каждой из трех составляющих шаблон MVC частей по отдельности. Однако, должен существовать клиент, который инициализирует каждый элемент и построит модель MVC. На самом деле, никакого сложного построения не будет - все что нужно уже сделано при написании классов Модели, Представления и Контроллера. В Примере 12-6 приводится класс Flash-документа, который иллюстрирует элементы MVC.

Пример 12-6. Main.as (основной класс минималистичного примера)

package { import flash.display .* ; import flash.events .* ; /** * Main Class * @ purpose: Document class for movie */ public class Main extends Sprite { public function Main() { var model:IModel=new Model ; var controller:IKeyboardInputHandler=new Controller(model) ; var view:View=new View(model,controller,this .stage ) ; } } }

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

Вам нужно запретить шорткаты для тестирования нажатий клавиш. В противном случае пользовательский интерфейс Flash перехватит события нажатия клавиш, которые соответствуют шорткатам. Чтобы запретить шорткаты, выберите Disable Keyboard Shortcuts из меню Control во время выполнения ролика.

Обратите внимание, что экземпляр Модели передается Контроллеру. Подобным образом экземпляры Модели и Контроллера передаются Представлению. Мы можем просто заместить существующие Модель и Контроллер другими, при условии, что они реализуют интерфейсы IModel и IKeyboardInputHandler. Дополнительные Представления также могут быть безболезненно добавлены прямо в отношения Субъект-Обозреватель между Моделью и Представлением. Модель ничего не знает о Представлениях, так как это забота Представления - зарегистрировать себя в качестве слушателя уведомлений об изменении Модели. Это большой плюс шаблона MVC; Модель, Представление и Контроллер разделены, слабо связаны, что придает гибкости в их использовании.

Вложенные Представления и узлы шаблона Компоновщик

Как вы помните, Представление, возможно, самый сложный элемент в триаде MVC, поскольку в контексте MVC он задействован как в реализации шаблона Обозреватель, так и в Стратегии. Наши элементы Представления способны быть более сложными, поскольку могут реализовать третий шаблон - Компоновщик (см. примеры шаблона Компоновщик в Главе 6). Реализация Представлений как элементов шаблона Компоновщик позволяет разобраться со сложными пользовательскими интерфейсами, которые содержат множественные Представления. Вложение Представлений превносит некоторые преимущества в процесс обновления пользовательского интерфейса, так как обновления могут распространяться по ветвям структурного дерева составного Представления. Также составные Представления могут добавлять и удалять вложенные Представления, основываясь на режиме работы приложения и пользовательских настройках. Хорошим примером сложного интерфейса является панель Properties Inspector в среде разработки Flash. Содержимое Properties Inspector зависит от контекста, и элементы интерфейса появляются и исчезают в зависимости от того, какой объект выделен на сцене.

Компонент и составное Представление

Первым шагом будет создание компонента и составных классов для Представления. Эти классы должны быть описаны как абстрактные, должны быть подклассами и не должны порождать экземпляры, как показано в Примере 12-7.

Пример 12-7. ComponentView.as

package { import flash.errors .IllegalOperationError ; import flash.events .Event ; import flash.display .Sprite ; public class ComponentView extends Sprite { { protected var model:Object ; protected var controller:Object ; public function ComponentView(aModel:Object ,aController:Object =null ) { this .model =aModel; this .controller =aController; } public function add (c:ComponentView) :void { throw new IllegalOperationError("add operation not supported" ) ; } public function remove(c:ComponentView) :void { throw new IllegalOperationError("remove operation not supported" ) ; } public function getChild(n:int ) :ComponentView { throw new IllegalOperationError("getChild operation not supported" ) ; return null ; } // АБСТРАКТНЫЙ метод(должен быть замещен в классе-потомке) public function update(event:Event=null ) :void { } } } }

Класс ComponentView из Примера 12-7 определяет абстрактный интерфейс для Представления компонента. Это похоже на класс классического компонента из Главы 6, но с несколькими ключевыми отличиями. Класс ComponentView хранит ссылку на Модель и Представление, и содержит конструктор. Не все Представления обрабатывают ввод пользователя, и компонентное Представление может быть сконструировано с простой передачей экземпляра Модели. Поэтому параметр aController принимает в конструкторе значение null по умолчанию. Также обратите внимание, что класс ComponentView унаследован от класса Sprite. Это логично, поскольку большинство Представлений рисуют пользовательский интерфейс на сцене. Мы можем использовать свойства и методы, реализованные в классе Sprite, для рисования и добавления объектов в список отображения.

Метод update() должен вести себя как абстрактный метод. Дочерние Представления, являющиеся потомками ComponentView, должны заместить и реализовать метод update(), чтобы уметь обновлять свою часть пользовательского интерфейса. По этой причине методу передается параметр типа Event. Этот параметр также по умолчанию установлен в null, что позволяет вызывать update() без передачи события как параметра. Такой подход полезен, когда изначально отрисованный пользовательский интерфейс находится в своем состоянии по умолчанию, и наш следующий пример иллюстрирует это.

Класс CompositeView расширяет ComponentView и замещает методы, которые отвечают за дочерние Представления.

Пример 12-8. CompositeView.as

package { import flash.events .Event ; // АБСТРАКТНЫЙ класс (от него нужно наследовать, не создавая экземпляра данного класса) public class CompositeView extends ComponentView { private var aChildren:Array ; public function CompositeView(aModel:Object ,aController:Object =null ) { super (aModel,aController) ; this .aChildren =new Array ; } override public function add (c:ComponentView) :void { aChildren.push (c) ; } override public function update(event:Event=null ) :void { for each (var c:ComponentView in aChildren) { c.update (event) ; } } } }

Обратите внимание на замещаемую (override) функцию update() класса CompositeView в Примере 12-8. Она вызывает метод update у всех дочерних классов. Поэтому, вызов функции update() в корне структуры составного Представления приведет к распространению обновления по структуре и обойдет дерево компонента, обновляя все Представления. Давайте расширим классы CompositeView и ComponentView, и создадим структуру Представлений, чтобы посмотреть как это работает

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

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

Архитектура программы или компьютерной системы - это структура или структуры системы, которые включают элементы программы, видимые извне свойства этих элементов и связи между ними [Басс (Bass)].

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

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

Так что же такое архитектура программы?

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

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

Таким образом, назревает первый вопрос: как разбить программу на файлы. Файловая архитектура программы - это один из аспектов ее структуры.

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

Отношения между компонентами системы также определяются ее архитектурой.

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

Уровни абстракции

При проектировании модулей (выбора групп функций и распределения их по файлам в случае процедурного подхода) важно выделять абстракции и пытаться их разложить по нескольким уровням.

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

Рассмотрим в качестве примера галерею фотографий. Информация об изображениях и пользователях хранится в БД, пользовательский интерфейс разделен на клиентскую часть и панель администратора.

Структура программы могла бы быть такой, как на рисунке ниже:


В этом примере прослеживаются три уровня абстракции .

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

Архитектура MVC

Сейчас популярен шаблон проектирования MVC . Он служит для отделения логики приложения от пользовательского интерфейса. Но сначала проясним, что такое шаблон проектирования.

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

Что же предлагает нам MVC для отделения логики приложения от пользовательского интерфейса?

Шаблон MVC позволяет разделить данные, представление и обработку действий пользователя на три отдельных компонента:

  1. Модель (Model) . Модель предоставляет данные (обычно для Представления), а также реагирует на запросы (обычно от Контроллера), изменяя свое состояние;
  2. Представление (View) . Отвечает за отображение информации (пользовательский интерфейс);
  3. Контроллер (Controller) . Интерпретирует данные, введенные пользователем, и информирует модель и представление о необходимости соответствующей реакции.

На рисунке ниже показаны отношения между компонентами каркаса. Проиллюстрируем рисунок небольшим примером.


Представьте форму, где можно ввести текст, нажать кнопку Edit и получить его транслитерацию:


Повторим шаги, изображенные на схеме:

  1. Пользователь нажимает кнопку Edit, при этом Представление (View) посылает сообщение Контроллеру (Controller): "Команда: edit"
  2. Контроллер принимает сообщение и обращается к Модели (Model), вызывая метод Edit() .
  3. В результате модель меняет свое состояние (запомненный в ней транслитерированный текст) и оповещает об этом представление: "Событие: changed".
  4. Представление принимает сигнал и обращается к модели за новым значением результата, вызывая ее метод Get() .

Реализация MVC

Реализация MVC предполагает объектно-ориентированный подход (ООП). Однако шаблон проектирования - это всего лишь набор решений. Адаптируем их для PHP без применения ООП. Упрощение делается для того, чтобы сконцентрироваться на сути разделения логики, а также для того, чтобы материал смог применить читатель, не знакомый с ООП.

Рассмотрим снова пример с галереей фотографий .
У нее есть два режима просмотра:

  1. Режим просмотра уменьшенных изображений (всех сразу);
  2. Режим просмотра фотографии полного размера (одной).

Также есть возможность загружать фотографии на сервер. Дополнительно реализуем поддержку типов визуализации, чтобы оценить гибкость каркаса.

На сайте будут две точки входа :

  1. index.php (просмотр галереи);
  2. photo.php (просмотр полноразмерной фотографии).
Эти два файла будем считать Контроллерами .

В качестве Модели будет выступать модуль, обеспечивающий работу с хранилищем изображений. Назовем его gallery.php и поместим в папку model .

В роли Представления будут выступать HTML-шаблоны, они будут находиться в папке templates . Что такое шаблоны и для чего они нужны - будет видно дальше.

Страницы просмотра галереи и просмотра фотографии будут иметь общую шапку и подвал страницы, отличаться будет только центральная часть.

Просмотр галереи будет иметь два типа визуализации:

  1. В виде таблицы (по умолчанию);
  2. В виде списка.
Нам потребуются четыре шаблона:
  1. main.php (каркас страницы);
  2. content_index_table.php (табличный вид содержимого галереи);
  3. content_index_list.php (списочный вид содержимого галереи);
  4. content_photo.php (содержимое страницы просмотра фотографии).

Получается следующая структура сайта:


Файловая структура разделена двумя горизонтальными чертами, образующими три секции. Файлы верхней секции относятся к Модели, файлы средней секции - к Представлению, файлы нижней секции - к Контроллеру.

Модель

Начнем с реализации Модели. В коде ниже приведен не полностью для минимизации и лучшей наглядности примера.

Мы определили лишь интерфейс Модели, оставив реализацию пропущенной. Однако для примера реализации каркаса MVC она вовсе и не нужна.

Представление

Теперь рассмотрим шаблоны. Начнем с общего каркаса страницы:

<?=$title?>

Вас не должно смущать, что в шаблоне используются непонятно откуда взявшиеся переменные $title и $content . Их подставит Контроллер. Но об этом позже.

- это сокращенный вариант записи .

Его удобно использовать в шаблонах. Также в шаблонах удобнее использовать альтернативные варианты записи конструкций if-else , foreach , for , while . Выглядят они так:

If (<условие>): <тело> endif; foreach (<инициализация цикла>): <тело> endforeach;

Остальные шаблоны будут подставляться в main.php таким образом:

В примерах ниже, приведен их код:

Код templates/content_index_table.php

Таблица | Список

"> " />



Код templates/content_index_list.php

Таблица | Список

"> " />



templates/content_photo.php: Назад

" />

Контроллер

И, наконец, соберем все вместе, описав наши два Контроллера. Их задача заключается в обработке запроса, выборе шаблона и подстановке нужных шаблону данных. Данные берутся, как правило, из модели.

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

Код index.php

Контроллер просмотра фотографии еще проще:

Код photo.php

В заключение

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

Для реализации модели MVC лучше выбрать объектно-ориентированный подход .

Существует множество готовых решений каркаса, например в Zend Framework. Однако информации, изложенной в текущем уроке, достаточно для того, чтобы понять архитектурные решения MVC и начать их использовать уже сейчас.

Фреймворк Bootstrap: быстрая адаптивная вёрстка

Пошаговый видеокурс по основам адаптивной верстки в фреймворке Bootstrap.

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

Верстайте на заказ и получайте деньги.

Бесплатный курс "Сайт на WordPress"

Хотите освоить CMS WordPress?

Получите уроки по дизайну и верстке сайта на WordPress.

Научитесь работать с темами и нарезать макет.

Бесплатный видеокурс по рисованию дизайна сайта, его верстке и установке на CMS WordPress!

*Наведите курсор мыши для приостановки прокрутки.

Назад Вперед

Удобный подход к веб-разработке: Модель MVC

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

Расшифровывается MVC как "Модель-Вид-Контроллер" (Model-View-Controller). Давайте поговорим об этом подробнее.

На сегодняшний день можно выделить два наиболее типичных способа создания веб-приложений (сайтов).

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

Скажем, в начале файла производится запрос к базе данных для получения из нее какой-либо информации. Здесь мы имеем дело с языком SQL - специальным языком запросов, предназначенным для взаимодействия с базой данных.

После этого, как правило, начинается html-разметка страницы (куда же без нее?). Причем внутри html-разметки в нужных местах производятся вставки PHP-кода, которые производят управление сайтом, являются его логикой. Итого мы имеем в одном файле: SQL, (X)HTML и PHP. Это уже - сборная солянка. Не забудьте добавить сюда еще CSS и немного Javascript для полноты картины, и, в итоге мы получим такую кашу, что в этом файле сам черт ногу сломит.

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

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

Второй способ связан как раз-таки с применением схемы "Модель-Вид-Контроллер" .

В чем суть данного подхода, и как его использование может помочь вам в работе?

Основная идея данного подхода заключается в необходимости разделения однородных элементов по разным файлам . Если говорить очень упрощенно: один файл - один язык. Но это очень грубый пример. Такое встречается крайне редко.

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

Здесь мы начинаем с вами подходить к более подробному рассмотрению модели MVC.

Данная модель подразумевает разделение всех файлов, задействованных при разработке сайта на три группы:

1. Файлы группы "модель"
2. Файлы группы "контроллер"
3. Файлы группы "вид"

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

Итак, давайте посмотрим на сравнительную схему модели MVC и "классического" способа разработки .


В левой части вы видите как раз то, о чем мы говорили выше. Вверху страницы - SQL-запросы к базе. Затем разметка плюс вставки PHP.

Справа же приведена простейшая схема модели MVC. В рамках данной схемы в модели происходят операции, связанные с взаимодействием с базой данных : извлечение данных, их модификация и удаление, подсчет количества записей в определенных таблицах и т.п.

В контроллере находится логика приложения , т.е. то, что определяет его функционал.

Вид же предназначен для показа конечному пользователю .

Двунаправленные стрелки на схеме показывают то, что в парах "Модель - Контроллер" и "Контроллер - Вид" существует взаимосвязь. Рассмотрим эту взаимосвязь подробнее на примере следующей схемы.


На этой схеме у нас добавилось два новых элемента: браузер пользователя и база данных. Рассмотрим в общих чертах весь цикл: от обращения браузера к определенному url-адресу до момента отображения страницы для пользователя:

1. Пользователь вводит адрес, и браузер обращается к контроллеру.

2. Контроллер обращается к модели.

3. Модель обращается к базе данных (к примеру, для получения необходимой для вывода информации)

4. Информация из базы попадает обратно в модель.

5. Из модели информация передается в контроллер.

6. Контроллер передает эту информацию в вид.

7. Вид выводится в браузер с помощью контроллера.

Такова общая схема работы данной модели. Как вы можете видеть, некоторым особняком на данной схеме стоят браузер и база данных. Действительно, браузер может обращаться только к контроллеру, так как контроллер является частью url-адреса . Посетитель не может обратиться к чему бы то ни было, кроме контроллера. Это важно понимать. Человек не может через адресную строку обращаться к видам или моделям. Он взаимодействует только с контроллером.

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

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

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

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

"Крайних" мы рассмотрели, а в центре схемы так и осталась наша троица, где происходят взаимодействия "Модель - Контроллер" и "Контроллер - Вид".

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

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

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

Говоря другим языком, фреймоворк - это гибкие рамки, которые ограничивают вас в плане структуры, но не ограничивают в плане функционала.

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

Среди других преимуществ модели MVC можно отметить разделение кода по функциональному признаку . Вам больше не нужно будет копаться в "каше" из SQL-запросов, разметки и PHP-кода. Если вам нужно что-то подправить или изменить, вы точно будете знать, какой именно файл вам надо править.

Ниже вы можете видеть часть файла, относящегося к группе "виды":


А вот кусок кода из модели:


Так может выглядеть контроллер:


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

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

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

Очевидны преимущества применения модели MVC в рамках фреймворка , например, того же CodeIgniter.

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

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

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

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

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

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

Дмитрий Науменко

P.S. Думаете, какой бы PHP-фреймворк освоить? Обратите внимание на CakePHP - он реализует рассмотренный выше паттерн MVC, и прямо сейчас вы можете получить небольшой вводный видеокурс, чтобы получить общее представление о возможностях этого фреймворка:

Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!


Что такое MVC?

Итак, MVC - это про пользовательский интерфейс (UI). Не обязательно графический, голосовое управление тоже годится. Не забудем, что программа может не иметь пользовательского интерфейса, может иметь программный интерфейс (API) или вообще никакого не иметь и всё ещё быть полезной.

Но если у нас есть пользователь, значит должен быть пользовательский интерфейс. Что же такое интерфейс? Это смежная граница между двумя системами. В нашем случае: с одной стороны - программа, с другой - пользователь. Вот они.

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

Юзкейсы

В качестве примера представьте терминал для торговли на бирже. Пользователь терминала выставляет заявку, в которой указывает, что он хочет купить акции компании «Светлый путь» в количестве 20 штук по цене 1500 рублей за акцию. Также указывает, что заявка действительна в течение четырёх часов, и с какого из его счетов списать деньги, в случае успешной сделки.

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

Но на бирже нельзя изменить заявку, в предметной области нет такого понятия. Заявку можно только выставить и отменить. Чтобы дать пользователю возможность в один клик менять заявку, надо запоминать старые значения, снимать заявку, давать редактировать то, что запомнили, и выставлять новую заявку. Такая комбинация. Но для пользователя она выглядит как одно простое действие: изменение заявки. Это называется - use case.

Дополним нашу диаграмму местом под юзкейсы.

Ещё пользователю надо дать возможность дёргать эти юзкейсы и получать результат. Это могут быть кнопки и другие графические элементы ввода-вывода, жесты, распознавание и синтез речи. Любой вариант обмена данными и командами. Вуаля:

Пользователь дёргает какой-то из юзкейсов, который, в свою очередь, производит манипуляции над программой. Программа публикует результат или изменения в её состоянии.

Так где же тут все-таки MVC?

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

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

Теперь немного частностей.

Это был классический вариант MVC - Active Model. Бывает и так, что модель не оповещает об изменениях. Тогда эту обязанность берёт на себя контроллер. Он знает, какие манипуляции производит над моделью, и, очевидно, знает, какие изменения в состоянии модели могут последовать. Это Passive Model.

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

За материал благодарим нашего подписчика Станислава Ильичева



Рекомендуем почитать

Наверх