среда, 1 июня 2011 г.

Design Pattern - MVC

Patterns, они же "Шаблоны" или алгоритмы программирования (далее паттерны) предназначены для упрощения работы с кодом, а также организации функционала для лучшего его понимания и расширения. По сути, формирование паттерна это последняя стадия рефакторинга, на которой код принимает наиболее структурированный вид. Паттерн это всего лишь структура кода которая может быть применена к обширному кругу задач. Самым распространенным паттерном является, MVC - связка Model - View - Controller, в которой классы Model (Модель) включают в себя данные и упорядочивают их, View классы управляют отображаемыми на экране объектами, и Control классы управляют данными в модели, могут их изменять, а также обрабатывают сообщения от View, и основе этих изменений (изменений в модели) происходит обновление объектов на экране, т.е. классов View. Таким образом различный функционал, выполняющий одинаковые функции может быть объединен в блоки, которые затем будет легко расширить или изменить. Ниже можно посмотреть простейшую диаграмму того как устроен паттерн MVC.


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


Прежде чем начать хотелось бы пару слов сказать о том для чего используется MVC паттерн. Ответ на этот вопрос не очевиден, потому как структуру передачи и обработки данных каждый программист выбирает (или разрабатывает) сам, но для того чтобы эта структура была понятна другим программистам (и не только) лучше пользоваться общепринятыми "стандартами", тогда работа становиться более продуктивной, а накопленные знания по этим "стандартам" позволяют быстро включить их в разработку и дополнить в случае необходимости. MVC паттерн применяется при разработке крупных приложений направленных на взаимодействия с пользователем. Это могут быть финансовые или "структурные" приложения, крупные сайты, порталы строят свою структуру на этом паттерне (к примеру Gaia Framework, один из самых популярных "site-making" платформ основан на MVC, есть еще  flashflowfactory, но я с ним не работал). Также на паттернах сделаны многие хорошие игры, если не все.

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

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

Разработку этого примера я буду проводить в FlashDevelop, эх вот сделали бы VisualStudio такой же или FD для C#!

1. Создание проекта AS3
Создадим AS3.0 Project, потому что он компилируется без участия Flash IDE, только на Flex SDK которая уже идет в комплекте к FD. Поскольку я большой любитель МАС шрифтов, то хочу заранее предупредить, что все комментарии будут на английском ввиду того, что используемый мною Monaco шрифт не поддерживает латиницу, да и мне самому проще читать комменты на языке кода.


При этом создастся уже готовый файл Main.as - главный файл вашего приложения из которого можно запускать все остальные файлы "классы-объекты".


2. Класс View
Создадим первый класс View. Сделать это можно несколькими способами, я же забил себе hotkey: Alt+C, чтобы выпадало окно создания нового класса, настроить горячие клавиши и воспользоваться всеми удобностями FD вы можете скачав и установив плагины к нему.


Поскольку объекты внутри класса View, да и сам объект View, будут находиться на главное "сцене" и юзер сможет их, как минимум, видеть, то мы должны "расширить", наследовать их параметры от визуальных объектов Flash. Самым подходящим для наших целей будет класс Sprite который помимо визуального отображения позволяет обрабатывать взаимодействия пользователя с ним, это один кадр MovieClip, который немного больше Sprite и несет в себе избыточную информацию (слайд с Adobe Max 2010).


Класс View, как было сказано выше, будет содержать объекты отображаемые на экране: кнопку, текстовое поле, shape. Он принимает все взаимодействия пользователя, то есть это GUI (Graphic User Interface) Выглядит он следующим образом:


Как вы можете видеть код разбит на блоки, которые проименнованы в соответствии с выполняемыми ими действиями. Это есть одни из основных принципов Рефакторинга - методики упрощения понимания и увеличения производительности кода. Одной из отличительных особенностей языка ActionScript 3.0 является то, что у объектов есть заранее заготовленные параметры, к примеру имена, которые можно использовать для их упорядочивания, как это сделано в примере _screenContainer_onMouseDownHandler. Где происходит фильтрация нажатия мыши только на подложке. Также с помощью имен можно организовывать "пседво-слои" когда одна группа объектов имеет имя с индексом, к примеру Image_# (# - порядковый номер). Тогда после прослушивания только одного (!) события на контейнере с этими Image_# можно отделить их от всех остальных объектов в этом контейнере -  создав фильтр, только предварительно разложив имя "таргета" в массив: 

if(e.target.name.split("_")[0] == "Image"){trace("Do Something")}; 

Иногда такой доступ к объекту e.target.name."operation" не проходит, в этом случае можно создать темповый объект и с него уже брать имя, раскладывать его в массив, а затем уже его анализировать (или пользоваться синтаксисом parent["childName"], который немного медленней работает, но зато надежно). Таким образом можно создать различные структуры, к примеру открытые, доступные и закрытие объекты в магазине. Лично я так делаю постоянно. : ) Также не бойтесь создавать темповые объекты, они помогают лучше понять код, и долго не задерживаются в памяти, потому как язык AS3.0 является ссылочным и не копирует сущность объектов, а всего лишь передает их по цепочке делая какие либо преобразования над ними (что в некоторых случаях является большой проблемой, когда необходимо создать множественные копии одного объекта).

Теперь нужно создать instance ("образец", дословно - экземпляр) класса View, в Main.as и поместить его на сцену. И можно переходить к созданию модели (хранилища-распределителя данных).


3. Класс Model
Модель выступает в роли хранителя и обработчика данных юзера, это может быть связь с сервером, XML или сохранение информации в файл. Также она может обновлять заранее обработанные или полученные с сервера данные на GUI. В AS3.0 существуют несколько механизмов передачи "сообщения" от объекта к объекту, в следующих постах мы их обязательно рассмотрим, но тут будем пользоваться стандартными возможностями ActionScript 3.0 - Event System = Event Listener/Event Dispatcher, т.к. это не требует от нас добавления дополнительных "модулей". 

Модель можно построить в двух вариантах: 

1) Модель которая сообщает о своих изменениях GUI, при этом во View передается model и "прослушивается" на обновления; 

2) Или же можно наоборот производить обновление View непосредственно из модели и следовательно тогда нужно передать view в model

Но есть и другие варианты, можно и самому придумать какого-нибудь посредника через которого производить обновления; или использовать "альтернативные" событийные движки, к примеру, мне очень нравиться NResponder и в больших проектах мы его постоянно используем, полезная и очень удобная штука!
В случае использования стандартных событий флэша удобно сделать Model наследником класса EventDispatcher, что позволит отсылать события не создавая лишнего экземпляра.


Теперь нужно создать instance в главном классе и отправить во View, где из него можно будет вытаскивать необходимую нам информацию, прослушивать его на обновления и прочие вещи : ).

Main.as:
model = new Model();
view = new View(model);

View.as:
Заменяем текст ("Display Text - First Initilization") в txt_displayText на данные из модели:
txt_displayText.text = _model.dataForView;

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

4. Класс Controller
Проще всего понять как работает структура MVC это хорошо проанализировать процесс переключения каналов телевизора. : ) В этом случае самым очевидным является работа пульта, который всего лишь преобразует нажатие клавиш, физическое взаимодействие (Пьезоэффект), на понятный электронике язык сигналов, т.е. можно сказать обрабатывает взаимодействия пользователя, передавая их на дальнейшую обработку в модель или обрабатывая самостоятельно. Таким образом пульт является чистым аналогом контроллера, он работает с моделью запуская операции обработки данных или сохранения. Из выше сказанного следует, что контроллеру необходим instance модели и совсем не нужно знать, как выглядит GUI и что с ним происходит. С другой стороны View нуждается в контроллере как в средстве обработки взаимодействий пользователя.


Main.as:

model = new Model();
controller = new Controller(model);
view = new View(model, controller);

View.as:

public function View(model:Model, controller:Controller) 
{
this._controller = controller;
this._model = model;

...

Объединение свершилось! : ) Приступим к соединению объектов в приложение!

5. "Связка" объектов
Начать лучше всего с визуальных компонентов, в нашем случае это кнопка которая будет отправлять данные из текстового поля на хранение в модель. Обработчик клика уже создан - btn_acceptButton_clickHandler - осталось привязать к нему функционал который будет связан с контроллером. Также нужно поставить "прослушку" обновлений от модели, а соответственно и обработчик, поэтому добавляем функцию LookForChange в которой будем ставить "слушателя" на обновления нужных нам данных.
View.as



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


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

Вторая часть, обычный конструктор в котором присваиваются значения переменным - создается новый таймер с секундной задержкой (но пока не запускается), на него ставится слушатель интервала (TimerEvent.TIMER) и создается новое, стандартное событие типа "изменения" (Event.CHANGE).

Далее идет обработчик слушателя таймера, который обновляет View, добавляя к прошедшему времени одну секунду. Пока что он не работает так как таймер не запущен.
Функция UpdateView() присваивает "публичной" переменной значение. А затем отсылает событие о том, что произошло изменение.

Последняя функция saveData вызывается из контроллера и сохраняет (обрабатывает) переданные данные юзера, после чего обновляет View и запускает таймер.
Controller.as



Последнее, что осталось сделать - это присвоить обновленные данные во View.
View.as



Вот собственно и все - КОМПИЛИРУЕМ!

ANOTHER WORDS
Идея написать эту статью родилась при разработке небольшого приложения для расчета коэффициента поглощения в моей Магистрской Дисертации - Absorbtion Calculator, который также можно скачать (пишите письма) и посмотреть как пример работы с MVC.

Скачать rar проект FD можно отсюда.

ВСЕМ УСПЕХА И ПРИЯТНОЙ РАБОТЫ! 
Учитесь всегда и помните: "No Questions = No Answers" 



Если возникнут вопросы
email: vladimirminkin@gmail.com
skype: rimidalv.niknim

http://vkontakte.ru/dqvsra

Комментариев нет:

Отправить комментарий