среда, января 06, 2010

Знакомство с Mate

Вступление.

Этот пост начал свое существование 7 месяцев назад. Именно тогда я решил взять на вооружение Mate Flex Framework. Но злостный непрекращающийся дидлайн никак не давал мне закончить статью. Год выдался трудным и на работе и дома (в конце ноября у нас родился 3й ребятёнок). Сейчас, на праздниках (Всех с Новым Годом и Рождеством!) появилась свободная минутка, чтобы оглянуться назад.

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

Забегая вперед, хочу с удовольствием отметить, что освоение прошло успешно и пол-десятка проектов были запущены на каркасе Mate.

Теоретическое знакомство с Mate

Наконец-то у меня появилось время копнуть Mate.
И начну я с того, чтобы удостовериться в правильности выбора.

  • Мне, как Flex-разработчику, Mate в первую очередь интересен тем, что это фреймворк, базирующийся на Flex.
  • Очень хочется меньше кодить, но иметь при этом наглядный и прозрачный код.
  • Понятная документация, обучение и примеры. Теперь посмотрим, какие фреймворки сравнивают и что о них думают:
    - Здесь - presentation сравниваются Cairngorm, Mate и PureMVC. Забавно, что количество доводов "против" для Mate меньше чем для остальных.
    - Довольно объемная статья: FrameworkQuest 2008, а точнее, заключительная часть исследования фреймворков. Почти во всех качествах, Mate выигрывает.
  • Последним аргументом будет личный опыт. Приступим к тестовому проекту.

Для начала посмотрим видео: Mate Helloworld. Впечатления позитивные. Всё довольно очевидно и изящно. Здесь рассказывают про Mate на примере работы с простейшим сервисом при использовании AMFPHP. Мне это дело так понравилось, что я решил немедленно себе поставить эту штуку - но об этом позже.

А пока - продолжаем изучать Mate. Читаем про Mate на Go!Verla Flex Blog:

Теперь, когда достаточно изучен хороший пример, почитаем документацию, и особенно, раздел Tags.

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


Неудачно перехожу от теории к практике

Пока изучал Mate, решил испробовать на реальном проекте (проект небольшой многослойной карты с маркерами и с подгрузкой данных). Подготовил все компоненты к виду Model/View и... всё получилось само собой, но только без Mate. На байндинге и событиях. Делаем вывод - Mate не для простых проектов. Точнее, не имеет смысла его использовать в простых проектах. Хотя, в примерах числятся очень простые приложения типа "Weather Widget".

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

Кстати, хочу отметить, что мое решение ничуть не проиграло бы Mate-решению. Чисто-Flex-разработка обладает богатым набором возможностей, и вовсе не обязательно всем сломя голову пересаживаться на Mate. Но попробовать, точно, стоит.


Создаю проект Mate

Но меня просто переполняет желание освоить этот фреймворк. Попробую начать с нуля.
Для начала, создаю проект (обычный). В папку libs скачиваю свежий SWC.

Теперь хорошо бы создать структуру пакетов (проще говоря, папок) проекта. После обзора примеров, выявляется устоявшаяся структура:

Основной пакет проекта помещается в папку:
src/[org, com, ru, ....]/[name, company-name, nick-name, ...]/[project name]

В проекте содержатся следующие пакеты:

  • business. Классы, которые определяют модель поведения приложения. Это классы, которые как правило наследуются от EventDispatcher или не наследуются совсем. Они выполняют различные действия по инициализации, обработке данных и пр.
  • events. Классы событий. Назначение их понятно - передача команд и данных.
  • extensions. Классы, представляющие собой расширения классов Mate. Собственно, расширяют функционал Mate для конкретных целей приложения. Таковых, в средне-обычном проекте вообще не бывает.
  • maps. Здесь размещаются карты событий EventMap. Содержат обработчики событий EventHandlers, Injectors.
  • views. Классы и компоненты, представляющие UI и всё что с ним связано - в общем, занимаются вводом и отображением данных.
  • vos. Классы объектов данных (Value Object). Это классы, наследники EventDispatcher, с набором bindable-свойств, представлюящих данные объекта. VO не должны ничего делать кроме как получать и выдавать свои данные.

Само собой разумеется, что каждый волен пользоваться собственной структурой. Но по мне, так лучше присоединиться к общественности и следовать какому-то общему правилу. Хотя бы самому обобщенно общему. Для лучшего понимания собственного и чужого кода.

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

Первое Mate-приложение

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

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

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

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

1. Создаем все Views

Их у меня получилось два:

  • первый - вывод карты с маркерами (LayerView.mxml),
  • второй - списки информации по объектам карты (InformationView.mxml).

Экраны оформляю так, чтобы все детали ушли в компоненты, а полученные компоненты "плавали на поверхности".

Таким образом, InformationView содержит компоненты:

  • InformationPanel - панель списка объектов карты
  • RouteSelectionPanel - панель выбора варианта маршрута

Каждый из них имеет dataProvider, в который передается список данных объектов карты informationDataProvider и список маршрутов routesDataProvider.

А LayerView содержит компоненты:

  • LayerContent - графическое содержимое карты (имеет selectedRoute - текущий вариант маршрута - ID клипа, который выводится на карте в данный момент)
  • MarkerSet - слой с множеством маркеров (имеет dataProvider)

Всё это лежит в папке views.

2. Создаем VO

Теперь создадим объекты данных, которые будут выводить наши views. Это обычные потомки EventDispatcher с Bindable-свойствами:

  • InstitutionVO - объект, описывающий организацию (дом) на карте (id, название, координаты маркера)
  • RouteVO - объект, описывающий маршрут на карте (id клипа, название маршрута)

Размещаем их в папке vos.

3. Создаем файлы данных

Имея представление о данных, которые мы выводим, можно создать XML-файл с данными. Данные будем получать по HTTP-запросу, и нам не важно, будет ли их кто-то генерировать или это будет простой XML файл. Сейчас главное, чтобы наше приложение их получало, обрабатывало и выводило. Создаю в src папку data, в которой размещаю XML-файл с данными. Структуру выбираю произвольно - так как мне удобно - тех-требований на формат обмена данными нет, поэтому их диктую я сам. URL файла данных приложение получает через переменные FlashVars (без подробностей).

4. Создаем поставщиков данных

InformationDataProvider - так я его назвал. Он получает загруженные данные, обрабатывает их и размещает у себя. Собственно, этим занимается его метод aquire(data:XML), который помещает данные в полях:

  • information:ArrayCollection - список объектов InstitutionVO,
  • routes:ArrayCollection - список объектов RouteVO.
В случае ошибки при разборе данных, или сервер возвратил информацию об ошибке, поставщик генерирует событие, информирующее об ошибке - SettingsEvent.SETTINGS_ERROR. В случае успеха - событие успешного получения данных SettingsEvent.SETTINGS_SUCCESS.

События из классов, не являющихся компонентами приложения, нужно генерировать через специальный, предусмотренный в Mate, класс Dispatcher. Тогда они будут отрабатываться в картах событий EventMap.
Размещаем его в папке business.


5. Создаем события и карты событий

В папке maps создаем InjectionMap.mxml, в котором размещаем теги Injectors, связывающие information (из InformationDataProvider) и informationDataProvider (из InformationView и LayerView), и аналогично для routes - routesDataProvider. Как только InformationDataProvider получит данные и установит их в information и routes - они тут же попадут по средством связывания (binding) в соответствующие поля InformationView и LayerView и будут отображены их компонентами в окне приложения.

Другая карта событий - MainEventMap.mxml - содержит обработчики событий. Такие инструменты как HTTPServiceInvoker и т.п. прекрасно заменяют рутинные классы отправки/получения данных. Обрабатываются все основные FlexEvent-события, которые позволяют обрабатывать инициализацию приложения и его готовность к работе. Чтобы событие улавливалось картой событий, оно должно иметь параметр по умолчанию bubbles:Boolean=true.
Итак, наша карта событий улавливает в контейнере EventHandlers событие FlexEvent.APPLICATION_COMPLETE и отправляет HTTP-запрос (HTTPServiceInvoker) на получение конфигурации. Не забываем указать resultFormat="e4x", чтобы полученные данные были представлены в виде XML. В разделе обработчиков resultHandlers помещаем MethodInvoker с вызовом нашего метода aquire, у которого resultObject указан в аргументах. В раздел faultHandlers помещаем обработчики ошибки запроса.

Теги с картами событий InjectionMap и MainEventMap обязательно помещаем в корне приложения.

Заметки

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

Столкнулся со следующими проблемами:

1. id в Request. Если я хочу передать в HTTPServiceInvoker, в Request переменную id - у меня проблема. В MXML id - это имя инстанцируемого объекта. В обсуждении предлагается использовать для этих целей параметр _id, но это не помогает - с запросом отправляется чепуха. Выход - не именовать так переменные (даже сильно поругался из-за этого с сервер-разработчиком).
2. source в EventHandlers. Переменные с именем source ведут как-то странно в обработчиках событий. Несколько раз ловил в них не действительное значение, а что-то вроде строки "Event". Непонятна причина такой особенности, поэтому, опять-таки, лучше избегать таких имен.
3. События. Не сильно проблемная особенность, но все же - нельзя модифицировать параметры конструктора события - добавлять и менять порядок следования параметров.

* * *

Работая с flex-модулями (Module), убедился, что Mate следует использовать очень осторожно. Лично я так и не понял, как сделать так, чтобы модуль выгружался и убивался GC.
Взаимодействие модулей с главным приложением производится успешно, однако, выгруженные модули продолжают ловить события.
Следуя инструкциям и собственным экспериментам, я постарался сделать модули как можно более автономными и создал следующую структуру:
1. Каждый модуль имеет свои карты событий. По возможности, я старался сделать их локальными (LocalEventMap). Однако, карты, которые работают с событиями главного приложения не могут являться таковыми, да и вообще - не очень то с ними всё сложилось.
2. События, которые используются и основным приложением и модулями выделены в отдельный пакет. Обработка и отправка событий в/из модуля вынесены в отдельные карты событий.
3. Перекрыл класс Module - каждый модуль при удалении из Stage подчищает в своих картах событий все возможные связи.

После таких действий, выгруженные модули теряют интерес к событиям приложения, однако, сомневаюсь, что они удаляются из памяти - есть угроза утечки памяти. Виновен ли в этом я, или Mate или вообще Flex - сказать трудно. Остается полагаться на то, что смена модуля не столь частая операция (я использую flex-модули в качестве "страниц" flash-сайта), чтобы съесть всю память во время просмотра сайта.

* * *

Общее впечатление

Mate упрощает разработку, но усложняет логику.
Многие вещи лучше визуализируются, работа с событиями выносится в отдельные блоки, повышается наглядность, а связность кода уменьшается. Превосходно решается задача отделения представления от логики - задача любого фреймворка, особенно для flash.
Возможность легкого надстраивания приложения - огромный плюс Mate. Если работа с событиями в приложении изначально организована правильно, добавлять новые программные возможности легче простого. Я без труда добавил возможности дип-линкинга SWFAddress, или встроил приложение в Zinc.

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

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

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

6 комментариев:

injun #576871 комментирует...

Отличный материал, сделал ссылку у себя.

Olexandr комментирует...

еще бы ссылку на архив проекта и посту цены бы не было!!!
в любом случае, спасибо!

Racer комментирует...

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

mr_jazz комментирует...

Отличная статья, спасибо. Особенно понравилось то что рассказал о личных впечатления и в сравнении с flex-фреймворком.

Olexandr комментирует...

понятно! спасибо! :)

ManatalaKahi комментирует...

Found this beete http://adf.ly/1058616/cinterviewqs