Pull to refresh

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

Reading time 6 min
Views 3.4K
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) между интерфейсами.
Tags:
Hubs:
+25
Comments 14
Comments Comments 14

Articles