войти зарегистрироваться

Adobe Flex whois

индекс
62,91

Пример использования Mate Flex Framework

Mate Flex FrameworkСразу же после появления на свет вышеуказанного фреймворка я понял — это именно то, чего так долго не хватало. Влезать в кабалу создания огромного количества классов используя фреймворки Cairngorm или PureMVC очень не хотелось, а с приходом Mate, появилась возможность уменьшить общую связанность компонентов приложения и использовать безболезненный обмен сообщениями(событиями) декларируя их в «родном» mxml.

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


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



У нас есть список сущностей с возможностью редактирования каждой из них в отдельной форме, а также добавлять новые — проще не бывает. Следующее решение, как всегда покажется необоснованно сложным для подобного примера, но незабываем, что перед нами стоит задача ознакомления с Mate, а не написания примера в 25 строк экшен-скрипта.

Я использую, так называемую схему «двустороннего коммуницирования посредством модели и инжектинга», на сайте фреймворка есть отличная диаграма как этого так и других приёмов:



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

Model


Я определил три класса модели. Самый элементарный, зависимый от представления, класс элемента списка OpportunityVO. Второй класс OpportunitiesDataProvider, является списком экземпляров OpportunityVO, он инкапсулирует в себе вызовы серверных методов и для удобства наследуется от ArrayCollection. Это даёт возможность использовать его провайдером данных для большинства списочных компонентов из Flex фреймворка.

Третий — OpportunityDataProvider, предназначен для работы с конкретным редактируемым OpportunityVO из списка. У него есть публичное свойство opportunity, а также методы для манипуляций с ним. Этот класс диспатчит события opportunityCreated и opportunitySaved типа DataProviderEvent, которые информируют о завершении обработки opportunity(так как эти действия зачастую связаны с вызовом серверных методов, мы неможем ожидать их синхронного выполнения в потоке вызова методов класса). Реализация класса очень простая, но в реальности, в него нужно помещать все трансформации, инициализации и прочие процедуры над элементом, которые не связаны с логикой работы представления, и вполне успешно могут быть отделены от него.



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

View


Для представления также определены три класса, первый OpportunitiesListView для показа списка элементов. У него есть одно публичное свойство dataProvider, а также, он диспатчит два события createOpportunity и editOpportunity типа OpportunityEvent, которые говорят сами за себя.

Второй класс OpportunityEditView определяется свойством opportunity, в которое нужно присваивать непосредственно объект редактирования OpportunityVO, а также он диспатчит другие два события saveOpportunity и closeOpportunity также типа OpportunityEvent.

Третий — Index, является композицией предыдущих. В нём доступно свойство selectedIndex, которое определяет текущую видимую форму.



Это наше представление. Как видим, его классы наследуются от стандартных контейнеров Flex фрейворка, они не имеют зависимостей от провайдеров данных модели, являются пассивными и выглядят как довольно самодостаточные компоненты для повторного использования — то, что нам и нужно.

Mate Event Maps


Перейдём непосредственно к роли Mate в связывании модели и представления. Все декларации этого фреймворка описываются в так называемых «картах событий». Это MXML файлы, которые поддерживают биндинг и все остальные удобства Flex Builder-а. По мере увеличения проекта, эти карты довольно сильно разростаются, так что для разных групп коммуникаций я завожу отдельные файлы. В данном примере их четыре.

Одна из карт — DataInjectionMap, описывает связи между представлением и моделью, выполняется только один раз при запуске приложения. Это частичная реализация паттерна IoC(Inversion of Control), которая работает необычным образом: при необходимости, она создаёт только экземпляры классов модели и слушая события родительского Application, в момент создания представлений, выполняет описанные связывания. Типичная нотация с использованием тегов Injectors и PropertyInjector:

<Injectors
	target="{OpportunitiesListView}">
 	<PropertyInjector
		targetKey="dataProvider"
		source="{OpportunitiesDataProvider}" />
</Injectors>


Здесь мы задаем связь на основе биндинга между OpportunitiesListView и OpportunitiesDataProvider, посредством свойства dataProvider, данные которого в результате попадут в DataGrid.

Остальные: OpportunitiesEventMap, DataProviderEventMap, NavigationEventMap, непосредственно описывают действия, которые должны происходить при попадении в поле видимости Mate(Event Bus) каких-либо событий как из представления так и из классов модели. Например, нотация с использованием тегов EventHandlers, MethodInvoker и EventAnnouncer:

<EventHandlers
	type="{OpportunityEvent.EDIT_OPPORTUNITY}">
 	<MethodInvoker
		generator="{OpportunityDataProvider}"
		method="editOpportunity"
		arguments="{[event.data]}"/>
 	<EventAnnouncer
		type="{NavigationEvent.NAVIGATE_EDIT_VIEW}"
		generator="{NavigationEvent}"/>
</EventHandlers>


Это выражение читается так: в тот момент когда представление продиспатчит событие editOpportunity(по-сути нажатие кнопки Edit), у экземпляра класса модели OpportunityDataProvider должна запуститься операция редактирования экземпляра класса OpportunityVO, которые доступен из события.

Так же, посылается специальное событие NavigationEvent, в результате которого представление должно переключиться в режим редактирования. Внутри класса OpportunityDataProvider, должен произойти вызов серверного метода для получение экземпляра OpportunityVO с последним состоянием элемента(но в моём примере, я упростил схему, и сохраняю в свойстве opportunity модели, тот же объект). Далее, так как мы указали связывание(injection) в DataInjectionMap между OpportunityEditView и OpportunityDataProvider по свойству opportunity, редактируемый элемент будет доступен в представлении.

В свойствах generator или source вышеописанных тегов, мы указываем класс модели, не экземпляр этого класса. Mate автоматически создаст экземпляр в нужный момент, причём, по-умолчанию, только один раз. Фреймворк имеет внутренний кеш для всевозможных экземпляров модели или событий, и ищет там необходимые объекты для повторного использования. Эта схема похожа на паттерн Singleton, но она гораздо практичнее, так как нет лишнего связывания между сущностями системы, которые неизбежны при использовании «одиночек»(кстати, довольно сильный недостаток прочих фреймворков типа Cairngorm или PureMVC).

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

<EventHandlers
	type="{NavigationEvent.NAVIGATE_EDIT_VIEW}">
 	<MethodInvoker
		generator="{OpportunityViewState}"
		method="setSelectedIndex"
		arguments="{[1]}" />
</EventHandlers>


При попадении в Mate события navigateEditView, вызывается метод у вспомогательного класса модели OpportunityViewState, который является проксирующим. Я не хотел бы вводить для этих целей этот класс, а бы присваивал значения в selectedIndex прямо в классе представления Index, но в свойство generator тега MethodInvoker(как и всех других тегов), нельзя указывать классы визуальных компонентов(то-есть можно, но это будет неправильно, так как фреймворк создаст экземпляр, недоступный никому). Суть в том, что, как я уже писал выше, Mate не создаёт экземпляры визуальных компонентов(и не пытается, это делает MXML), он слушает события всего приложения, и когда компонент попадёт в display list флеш плеера, фреймворк начинает с ним «сотрудничать»(зачастую для определения(injection) зависимостей). Получается, что есть внутренний кеш всех созданных классов самим фреймворком и отдельный список компонентов, которыми заведует сам флеш плеер и они не пересекаются между собой.

Вот и всё. Информацию о всех доступных тегах фреймворка можна найти в документации на сайте Mate. Надеюсь описание не получилось чересчур запутанным. С удовольствием отвечу на все адекватные вопросы по теме.

P.S. Кстати, на днях вышло обновление Mate до версии 0.8.7, в котором появилась возможность задавать связи(injection) между интерфейсами.

комментарии (12)

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

      Развитие примера не предусмотрено.
  • Его действительно не хватало.
  • Модель MVC можно реализовать и самому без всех этих фреймворков, используя самые важные вещи для себя. Например, в проекте где я работаю, был использован фреймворк написаный по подобию CairnGorm, только заметно проще.

    Но часто появляется проблема создания сложных UI компонентов, которые требуют заказчики и рисуют дизайнеры. И вот как раз на эту тему в интернете очень мало материала, да и никаких библиотек нет, а реализовывать в чистом виде использую за основу классы Flex выходит накладно
    • Конечно можно, и я написал некоторое количество проектов, без использования фреймворков. Но, суть использования Mate, как раз и заключается в крайней ненапряжности. Тоесть, вы пишите как всегда, но теперь 5% нюансов покрываете с помощью Mate.

      Объясните подробнее, о проблеме с UI компонентами, может я попробую написать статью на эту тему. Есть некоторые приёмы построение шаблонных компонентов, которые могут покрывать гибкие требования заказчика.
      • Суть всех Flex-фреймворков, что я видел (в том числе и этот Mate) сводится к тому, чтобы автоматизировать процесс создания однотипных RIA: списки сущностей, формы для редактирования, отображение данных и так далее, естественно используя для этого богатые возможности Flex SDK и UI компонентов которые можно реализовать с помощью Flash.

        Но заказчики (а скорее даже дизайнеры и специалисты по Usability) видя такой инструмент как Flex не ограничивают себя в фантазии и предлагают для реализации сложнейшие компоненты. Например у меня в приложении существуют Комбобоксы и Деревья совмещенные с радиобаттонами и чекбоксами: В дереве ветвь енаблится/дизаблится радиобаттоном, а листья отмечаются чекбоксами и все это, естественно, некоторым образом должно быть связано с моделью и изменять ее. Или скажем DataGrid совмещенный с деревом, в котором некоторые строки (rows) распахиваются как ветвь в дереве.

        Конечно сами по себе эти компоненты реализовать возможно. Но когда дело доходит до того, чтобы попытаться связать такой компонент с самим шаблоном MVC, не нарушая парадигмы (скажем view не может изменять model, а только рассылать события и слушать (binding) модель) тут и начинаются проблемы
        • Вы всё правильно описали. Но, следует разделять такие понятия как контрол и компонент.

          У нас тоже используются такие нестандартные контролы, как, например, комбобокс совмещённый с деревом и чекбоксами. Это контрол — атомарный элемент UI, в нём не должно быть никаких попыток связи с моделью, только набор событий которые он диспатчит и публичные гетеры/сетеры, которые определяют данные, которые он обрабатывает.

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

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

          По идее, всё что мы создаём с помощью MXML это компоненты, так нам подсказывает и Flex Builder. А атомарные, полностью независимые ни от чего контролы, зачастую делают в чистом Action script наследуя UIComponent.
        • использую PureMVC в проекте, где много _сложных_ составных компонентов. Полет нормальный. Mate что-то не впечатлил, имхо это для тех, кому сложно поделить в голове свой код на отдельные независимые mxml (и as3) компоненты и работать с ними/связывать их исключительно через mvc фреймворк.
          • Э, как же так, зачем тогда вообще фреймфорк, если не разделять код на независимые сущности?

            Что-то вы излишне компроментируете Mate =)
            • я, наверное, не совсем понятно выразился. PureMVC хорош именно тем, что не использует никаких flex/mxml-специфичных фич. Это позволят четко провести грань между компонентами и их связями в _голове_ разработчика, что очень помогает в больших проектах. «Влезать в кабалу создания огромного количества классов» — во-первых, не огромного, а ровно один медиатор на один компонент, во-вторых, это не кабала, а очень приятный процесс — представьте себе, вы пишете логику программы вообще не обращая внимания на то, как в конце-концов будут реализованы компоненты, т.е. разделение полное, не даром он *pure* mvc. Это сложно объяснить словами, понимание приходит только в процессе разработки…
              • Что-то в ваших словах есть конечно. Я, чесно говоря, не пробовал использовать PureMVC, всегда отталкивала указанная схема: на один компонент — один медиатор.

                Пожалуй, действительно, слова тут не к месту, нужно ощущать и делать персональные выводы об удобстве.
  • Ну как вариант конечно. На самом деле и Cairngorm был-бы не такой монструозный, будь в наличии визарды для автоматизации написания цепочек. Да и Mate это-бы не повредило. В целом для небольших проектов Mate несомненно экономит кучу времени.
Только авторизованные пользователи могут оставлять комментарии. Авторизуйтесь, пожалуйста.