MVC для начинающих

Monday, 08 February 2010 | Автор: programmer

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

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

В принципе у меня уже написан код программы для статьи  "Скрипт фотогалереи. Том 1. Глава 2", где я рассматриваю создание backend'а для фотогалереи и вот именно этот код и построен по технологии MVC.

 

Первоисточник: http://chtivo.webhost.ru/articles/mvc.php

 

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

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

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

Стандартная схема архитектуры «Модель-Вид-Контроллер» изображена на следующем рисунке: (схема заимствована из книги «Ajax in action» издательского дома «Вильямс»)

 



 

Разберём по пунктам данную схему.

В шаблоне MVC, как следует из названия, есть три основных компонента: Модель, Представление, и Контроллер.

Представление (вид) отвечает за отображение информации, поступающей из системы или в систему.

Модель является «сутью» системы и отвечает за непосредственные алгоритмы, расчёты и тому подобное внутреннее устройство системы.

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

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

Итак, условимся:

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

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

  • Передавать ли их в модель
  • Вывести сообщение об ошибке и запросить повторный ввод (заставить модуль представление обновить страницу с учётом изменившихся условий)

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

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

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

Но на самом деле ничего сверхсложного в этом нет.

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

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

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

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

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

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

 

 

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

Теперь попробуем составить диаграмму классов для большей наглядности.

 

 

Диаграмма классов содержит три класса, по одному для каждого компонента архитектуры MVC. Для удобства, они так и названы: Model, View, Controller.

В представлении есть три функции (хотя, вполне возможно обойтись только лишь одной), которые отвечают за отображение состояния приложения:
displayDefault() – вывод формы по умолчанию.
displayError(error = false) – вывод формы с сообщением об ошибке, либо отдельной формы с ошибкой
displayResults() – вывод результатов вычислений

Контроллер имеет не только методы, но и поля. С полями всё просто: это ошибка и результаты вычислений. По умолчанию им задаётся значение false, что свидетельствует о том, что пока нет ни ошибки, ни результатов.

Три метода, присутствующие в контроллере, служат для управления и проверки. Метод для проверки (validate()) является необязательным, и вполне может отсутствовать, если никаких проверок не требуется.

Метод processData() служит для вывода формы по умолчанию, однако он включает также метод userRequest(), функциональность которого выполняется лишь в том случае, когда есть введённые пользователем данные. Именно метод userRequest() содержит в себе функцию validate() (если данные не введены, следовательно, незачем делать их проверку) и, кроме того, должен содержать вызов конструктора класса модели.

В модели может содержаться любое количество полей и методов. Однако два метода должны быть обязательными (или даже один. Как удобнее будет).
calculate() – функция, производящая основной расчёт
getData() – функция, возвращающая данные результата.

Разделение функций модели скорее смысловое. Вполне достаточно создать один метод, который будет и считать, и возвращать результат.

Возвращаемся в метод userRequest() контроллера. После того, как в нём был посчитан результат и возвращён в том или ином виде, его можно смело отдавать на вход функции displayResults() класса View. Однако заметим, что в принципе, можно отдавать на вход представления и экземпляр класса модели, если вывод оного хранится в его полях, а их много (если лень, так сказать, создавать структуру, массив либо ещё какой-либо объёмный тип данных).

Если функция validate() контроллера выявила ошибку, и установила значение поля error в значение, отличное от false, контроллер сам вызовет метод displayError() класса View.

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

 

 

 

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

Например, в представлении мы можем написать:

 

public function displayDefault() {
    echo "<p>Введите имя:  ";
    echo "<input type='text' name='name' value=''>  ";
}

 

А потом через месяц ужаснуться, и перейти к использованию шаблонизатора. Скажем, smarty.

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

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

 

<?
/**
*    Пример реализации MVC на PHP
*
**/
class Controller {
    private $error;
    private $result;

    function __construct() {
        $this->error = false;
        $this->result = false;
    }

    function processData() {
        $this->userRequest();

        if ($this->error)
            View::displayError($this->error);
        else
        if ($this->result)
            View::displayResults($this->result);
        else
            View::displayDefault();
    }
    
    function userRequest() {
        // данные отправлены
        if (isset($_POST['send'])) {
            $this->validate();
            if (!$this->error) {
                // основные вычисления
                $model = new Model();
                $model->calculate($_POST['name']);
                $result = $model->getData();
                
                // проверка на ошибки в самой модели
                if (!is_array($result))
                    $this->error = $result;
                else
                    $this->result = $result;
            }
        }
    }
    
    function validate() {
        if (empty($_POST['name']))
            $this->error = 'Не введено имя!';
        else    
        if (strlen(strval($_POST['name'])) < 3)
            $this->error = 'Имя слишком короткое!';
    }

} // class Controller

class View {
    static function displayDefault() {
        echo "<form method='POST' action=''>";
        echo "<p>Введите имя:  ";
        echo "<input type='text' name='name' value=''>  ";
        echo "<input type='submit' name='send' value='Отправить'>";
        echo "</form>";
    }
    
    static function displayError($error) {
        echo "<p><b>Ошибка:</b> {$error}";
        View::displayDefault();
    }
    
    static function displayResults($results) {
        echo "<p><b>Результаты:</b>";
        echo "<p>Ваше имя <b>".$results[0].
            "</b> означает <i>".$results[1]."</i>";
        echo "<p><a href='".$_SERVER['REQUEST_URI'].
                    "'>Узнать ещё об одном имени</a>";
    }    

} // class View


class Model {
    private $data;

    function __construct() {
        $this->data = false;
    }

    function calculate($name) {
        $this->data[] = $name;
        $len = strlen($name);
    
        if ($len == 3)
            $this->data[] = 'краткость - сестра таланта';
        else
        if (($len > 3) && ($len < 6))
            $this->data[] = '...нет особого значения';
        else
            $this->data[] = 'невероятно богатая фантазия родителей';
    }

    function getData() {
        if ($this->data)
            return $this->data;
        else
            return 'Вычисления не произведены!';
    }
} // class Model

$controller = new Controller();
$controller->processData();
?>

 

Итак, мы получили простейшую MVC-систему. Выделим положительные и отрицательные стороны:

К минусам можно отнести

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


Последнее требование к нашему примеру не относится, но для реальных систем оно весьма актуально.

К плюсам отнесём следующее:

  • Несомненно более гибкий код
  • Возможность повторного использования каждой из трёх составных частей MVC
  • Безболезненная замена модели (другие алгоритмы расчета, способа хранения данных и т.д.)
  • Достаточно просто перейти от одного представления, к другому (от HTML к XML или JSON)




 
Tweet


Категория(и): Изучаем PHP, Скрипты

Комментарии


Monday, 08 February 2010 | 14:25:35 | Автор: dan
Очень сложно!
Monday, 08 February 2010 | 14:26:57 | Автор: my
Ваша реализация гостевой книги - это уже MVC. =)

К минусам стоит добавить снижение производительность.
Monday, 08 February 2010 | 15:05:34 | Автор: programmer
my,
у меня гостевая книга без "представления", там только класс реализирующий прослойку между БД и пользователем, т.е. "Контролер".

Снижение производительности не заметил если честно. Вот если какой-нить smarty использовать, то да, будет снижение.
Monday, 08 February 2010 | 15:10:15 | Автор: my
Как так?

"""
if (isset($_POST['save'])) { // Если была нажата кнопка "Сохранить"
$guestbook->addRecord($_POST);
}

// делаем запрос к базе данных, чтобы взять сообщения
$rowset = $guestbook->viewBoard();
"""
^^^ - контроллер

class guesstbuk - модель =)
Monday, 08 February 2010 | 15:29:10 | Автор: programmer
Вьюхи то нет :) а как без представления представить mvc? :)
Monday, 08 February 2010 | 15:31:40 | Автор: my
А нагромождение HTML в index.php не "представление"?
Monday, 08 February 2010 | 15:39:24 | Автор: programmer
нагромождение HTML в коде это слабый отклик представления, ведь согласитесь что если я туда захочу вставить разное представление дизайна, например, смену цветовой гаммы, расположения блоков и т.д., то в одном файле придется громоздить кучу ифов в зависимости от условий задачи, а вот в "правильном" представлении это гораздо легче будет сделать.

Спорить больше не буду :)
Monday, 08 February 2010 | 15:48:50 | Автор: my
Существование различных вариантов хранения компоновки MVC никак не пересекается с самой концепцией.
Добавить комментарий
Чтобы оставить комментарий, Вам необходимо зарегистрироваться или авторизироваться