SlideShare a Scribd company logo
FlexPure MVC архитектура для приложений
             enterprise уровня




Описание:
1. Сначала мы вспомним основные составляющие микроахитектурыPureMVC, используя презентацию
   SamuelAsherRivello.
2. Далее рассмотрим структуру и задачи, возникающие при использовании PureMVC в больших проектах.
      a. Разновидности и жизненный цикл элементов системы
      b. Необходимость синхронизации работы медиаторов с асинхронно создаваемыми видимыми
          элементами.
3. «Страничная» архитектура для PureMVC проекта:
      a. Управление View компонентами в «страничной» архитектуре:
              i. отложенное (deferred) создание,
             ii. расположение в иерархии DisplayObjects- слоты, медиаторы-слотхолдеры и их
                 взаимодействие с медиаторами-клиентами.
      b. Жизненный цикл «страницы», дескриптор «страницы»
      c. Прелоадер страницы

4. Взаимодействие View Components и Mediators
      a. Шаблон «страницы»
      b. Интерфейс «страничного» View Component
      c. Обмен данными между медиатором и View компонентом.
      d. Состояние «редактирования» (Editing state)
      e. Обработка нажатий кнопок (Buttons)
      f. Обработка других команд (пример – Chevrons)

5. Использование модулей в «страничной» архитектуре.

Требования к слушателям:
знание AS3, жизненного цикла компонентов Flex, PureMVChttp://puremvc.org

Докладчик:
Сергей Шичинов - SergiyShychynov (EPAM)Sergiy_Shychynov@epam.com

Примечание: пункт 4 был исключён из доклада для того чтобы длительность доклада
соответствовала регламенту.

2012-03-24
Основные составляющие PureMVC
(используя презентацию SamuelAsherRivello)

http://www.adobe.com/newsletters/inspire/december2008/articles/article6/index.html

MVC in PureMVC




Схема взаимодействия компонентов PureMVC для выполнения элементарной операции:

-   Внешний вид компонента HelloGoogle – кнопка и текстовое поле для сообщения
-   Последовательность элементарных действий выполняемых при нажатии кнопки
-   Положение этих действий на диаграмме PureMVC
Enterprise flex pure mvc.v4
Основные задачи, возникающие при использовании PureMVC в больших
проектах.
Когда речь идет о приложение enterprise уровня, то это означает что они БОЛЬШИЕ. То есть имеется
БОЛЬШОЕ количество РАЗНЫХ форм и компонентов, которые необходимо будет использовать в разных
ситуациях и в разное время. И речи о том, что все элементы будут созданы одновременно или заранее быть
не может. Более того – было бы неплохо, чтобы и грузить их все сразу не требовалось.

Рассмотрим несколько разных «видов» гипотетического большого приложения.
Enterprise flex pure mvc.v4
Среди всего UIбольшого приложения можно выделить элементы (подсистемы):

1. Которые постоянно находятся на экране и доступны для взаимодействия.
2. Которые могут вызываться пользователем на экран по желанию и ведут себя относительно независимо
   от других элементов UI.
3. Которые имеют отношение к текущей операции, выполняемой пользователем, показываются на экране
   когда он начинает выполнять эту операцию и будут убраны с экрана когда он эту операцию
   завершит.Причем таких циклов появления и исчезновения с экрана может быть много. Этот третий тип
   подсистем мы будем в дальнейшем называть страницами – Pages.
И если операции по обслуживанию жизненного цикла (описанные далее) для компонетов 1 и 2 типа
можно выполнить один раз (например в StartupCommand), то выполнение всех нижеперечисленных
действий для компонентов подсистем 3-его типа является рутинной операцией и нуждается в
автоматизации.

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

1. Создать (инстанциировать) и разместить на экране (в иерархии DisplayObjects) необходимые компоненты
   - ViewComponents. (Здесь и дальше компонентом будет называться видимый элемент, занимающий
   место в иерархии DisplayObjects.)
2. создать и зарегистрировать необходимые медиаторы – Mediators.
3. связать медиаторы с соответствующими компонентами
        a. медиаторы должны иметь ссылки на свои компоненты, для того чтобы передавать в них данные и
           совершать действия
        b. медиаторы прослушивают события – Events от компонентов для того чтобы узнать что нужно что-
           то делать
4. зарегистрировать необходимые для работы подсистемы комманды – Commands (связать их с
   Notifications),
5. создать и зарегистрировать необходимые для работы прокси–Proxy,
6. отобразить в наших компонентах валидные данные и обеспечить взаимодействие
        a. Как правило, эти данные загружаются извне (с сервера). Это может занять определенное время.
           Эти данные возможно не получится загрузить в результате ошибки. И возможно имеются некие
           политики безопасности, которые в зависимости от того какие именно данные хочет увидеть
           пользователь, могут не разрешить ему увидеть эти данные – и мы не узнаем о том разрешено это
           или нет, пока не получим ответ с сервера.
           Во всех этих случаях неразумно показывать на экране новые компоненты, еще не заполненные
           данными, до того как эти данные будут получены с сервера. Это процесс мы будем называть
           предзагрузка – preloader.
        b. Во время работы пользователя с подсистемой, коммуникация с сервером обеспечивается по
           стандартной схеме изложенной выше.
c. Отдельный вопрос – это обработка ошибок (серверных) – в зависимости от типа ошибок мы может
          либо дальше продолжать оставаться на странице, либо потребуется обновить ее, либо придется
          покинуть ее.
7. После того как пользователь завершил операцию нам необходимо корректно освободить ресурсы
   системы от текущей «страницы» (очевидно перед тем как будет открыта следующая).
       a. Удаление «актеров» PureMVC (медиаторов)
                i. «отписать» медиаторы от системы (unregister) чтобы они не прослушивали больше
                   нотификейшены.
               ii. Можно также удалять медиаторы, но можно сохранять их в «кэше» до следующего
                   использования.
              iii. Команды тоже можно «отписать» от системы, но это не обязательно.
              iv. Некоторые прокси можно было бы тоже отписывать от системы и удалять, но обычно этого
                   не нужно. И даже вредно.
       b. Отписывание медиаторов от events, которые они прослушивали от компонентов.
       c. Удаление видимых компонентов.
          Вообще говоря, операция создания компонента и добавления его в DisplayList гораздо более
          ресурсоемкая, чем сделать невидимый компонент – видимым. Поэтому для увеличения
          перформанса и для того чтобы видимые элементы появлялись более плавно вместо удаления
          компонентов лучше делать их невидимыми.
       d. Если в процессе выполнения операций были загружены какие-то большие данные и ссылки на них
          сохранились в компонентах или «актерах» PureMVC – то нужно их очистить, чтобы позволить
          GarbageCollector –у освободить память.


Необходимость синхронизации работы медиаторов с асинхронно создаваемыми видимыми
элементами.
Как известно видимые элементы – компоненты флекса не создаются мгновенно по запросу. Как правил от
момента создание экземпляра компонента, установки его свойств и добавления его в иерархию
DisplayObjectsпроходит некоторое время и несколько этапов, прежде чем событие «creationComplete»дает
нам знать, что компонент и все его подкомпоненты полностью созданы и готовы к работе.
До этого момента медиатор, даже если и будет иметь ссылку на компонент, не сможет полноценно с ним
работать.


DefferedMediator

Решает проблему асинхронного создания компонентов. Откладывает вызовы initialize и activate до
момента, когда компонент будет создан и инициализирован. А потом еще откладывает update до
момента, когда все медиаторы из списка будут активированы.



Constructor() – чистый конструктор (не выполняющий никаких действий)

* Override it to complete setup mediator properties and associate mediator
* with the View Component. Use setupMediator function to do most of the tasks.
function create():void

* The method is called after component <code>creationComplete</code> event.
* Create it to add listeners and make additional view component initialization.
function initialize():void;

* This method is called every time when you show and activate the view component.
* Override it to add additional mediator's and view component's activation.
functionactivate():void;

waitForMediatorsActivation(mediatorNames);

* Override it to update view component in accordance with current viewParams.
* You can process here both stateParams and viewParams.
* Is called after initialize() (after creationComplete event) and
* activate() methods.
function update():void

* Deactivate mediator (remove from pureMVC workflow).
* You can override it to make additional deactivation.
functiondeactivate():void;

* Create it to remove listeners and make additional view component finalization.
functionfinalize():void;



Кто, когда и как создает видимые элементы.
Слоты, медиаторы-слотхолдеры и их взаимодействие с компонентами-клиентами.
Задача по созданию и связыванию вью компонентов с медиаторами возложена на медиатор.

Медиатору известен класс его компонента. При создании медиатора – в методе create() – медиатор создает
экемпляр вью компонента и помещает его в DisplayList. Делается это с помощью концепции медиаторов-
слотхолдеров, которые имеют в составе своего компонента slot – контейнер (обычно ViewStack) в который
можно поместить, найти и удалить комопнент.

SlotViewComponentDescription
viewComponentName:String,
viewComponentSlot:String,
viewComponent:UIComponent


                                                        SlotAMediator
      M1Mediator


                                                медиатор-слотхолдер
      медиатор-клиент                           SlotAMediator получает
      M1Mediator создает                        нотификейшен, адресованный ему
      компонет и отправляет его                 и добавляет в свой слот (ViewStack
      слоту "SlotAMediator" -                   или другой контейнер) новый
      просит его хранить под                    компонет под именем "M1"
      именем "M1"
Enterprise flex pure mvc.v4
«Страничная» архитектура для PureMVC проекта
Мы ввели понятие страниц – Pages.
«Страница»– это некоторое множество видимых элементов, функционирующих совместно в одном
промежутке времени. Эти компоненты и обслуживающие их актеры PureMVC совместно создаются, работают
и удаляются.
Выполнением всех этих операций в PureMVC традиционно занимаются команды.
Чтобы не писать отдельные команды для открытия каждой страницы, была создана единая команда
открытия новой страницы ViewPageCommand. Эта команда выполняет все необходимые при открытии
страницы действия в соответствии с переданным ей в качестве входного параметра дескриптором
страницы.
Она работает совместно с PagesProxy, которыйи является хранителем информации обо всех доступных
в системе «страницах» и «состояния» текущей открытой страницы.

Основные задачиPagesProxy:
        - хранение справочника всех страницах в системе
functiongetPageDescription(pageName:String):PageDescription

       - хранение информации о текущей открытой странице (ее имя, дескриптор, стейт, состояние
       редактирование, helpContext и прочее)

Соответственно все страницы имеют уникальное имя
publicclass Pages
{
publicstaticconstABOUT:String = "about";
publicstaticconstADMIN_EMAIL_TEMPLATE_EDIT:String = "adminEmailTemplateEdit";
publicstaticconstDOCUMENT_TRANSLATION:String = "documentTranslation";
…
}
идескрипторы

PageDescription
pageName:String,

preloader:Class, - наследникBasePagePreloader

mediators:Array, - списокклассовнаследониковBaseMediator
proxies:Array = null,
commands:Array = null, - списокCommandDescription(name:String, commandClass:Class)

stateParams:Object = null, - редко встречающиеся дополнительные параметры

pageTitle:String = null,    }   - часто встречающиеся дополнительные параметры
breadcrumbs:Array = null. … }

Примердескриптора страницы:
newPageDescription(Pages.QUESTIONNAIRE,
QuestionnaireAnswerPreloader,
    [QuestionnaireSlotHolderMediator, QuestionnaireAnswerMediator],
    [QuestionnairesProxy, ProcessEditProxy, ProcessTypesListProxy, TimeScaleProxy],
    [
newCommandDescription(SPNotification.FIND_NEW_QUEST, LoadQuestiannairCommand),
newCommandDescription(SPNotification.UPDATE_ASSET_QUEST, UpdateAssetForQuestCommand),
    ],
{questionnaireComponentState: QuestionnaireComponentState.ANSWER},
"Questionnaire Registry"
)


PageNotificationParams
varpageName:String;
varpageState:String;
varid:Number;
varbackPageName:String;
varpreloader:BasePreloaderMediator; - создается и добавляется в процессе открытия страницы


ViewPageCommand
    1. Проверяет наличие дескриптора страницы и при необходимости подгружает модуль содержащий
       страницу. Входными параметрами для открытия страницы являются:
          a. pageDescriptor – находится по имени страницы
          b. stateParams : {} – является дополнительным набором параметров страницы (которые не
             вошли в сам дескрипотор – так как редко используются)
          c. viewParams: PageNotificationParams – дополнительные параметры страницы, заполняемые
             при ее открытии
    2. Создает и регистрирует необходимые прокси (они могут быть нужны прелоадеру)
    3. Создает и запускает прелоадер
          a. load()
          b. validate() + препроцессинг
    4. Если прелоадер «дал добро», то закрывает текущую страницу
          a. отписывает от системы медиаторы
          b. подчищает «временные» данные (на вью компонентах и медиаторах!)
          c. отписывает медиатор от events компонентов
          d. добавляет ссылку на прелоадер с загруженными и подготовленными данными во viewParams
    5. Регистрирует команды
    6. Создает (или достает из кеша) медиаторы, передает им значения stateParams и viewParams и
       регистрирует их в системе.
          a. Если медиаторы создаются то это делается в следующем порядке
                   i. Конструктор
                  ii. Setup params (stateParamsиviewParams)
                 iii. create()
          b. если достается из кеша, то просто Setupparams (stateParams и viewParams)
    7. Активирует медиаторы. В процессе активации медиаторы должны создать свои компоненты,
       разместить их в иерархии DisplayLists, сделать видимыми и подписаться на необходимые events.
    8. После активации всех медиаторов вызывается update()



Прелоадер – Preloader
Прелоадерслужитдлятогочтобызагрузитьнесколько «частей» данных
(используяпроксиилинепосредственноделегаты). После полной загрузки всех частей возможна их валидация
и дополнительная обработка. Прелоадер отрабатывает при попытке открыть новую «страницу» до того как
она будет реально открыта. В случае если валидатор прелоадера выдает false, страница не будет открыта.
Вместо этого будет выведено сообщение об ошибке.

Фрагмент кода вызова прелоадера при открытии страницы
if(pageDesc.preloader != null) {
setLoadingState(true);
registerProxies(); // for preloadin purposes
varpreloaderMediator:BasePreloaderMediator = new (pageDesc.preloader)();
preloaderMediator.viewParams = params;
preloaderMediator.stateParams = pageDescriptor.stateParam;
preloaderMediator.callback = executeAfterPreload;
preloaderMediator.load();


BasePreloaderMediator
varstateParams:Object;
varviewParams:PageNotificationParams;
* Override it to create more preloader parts. You can use viewParams and stateParams.
function load():void
{
//...
addDelegatePreloaderPart("YYYid", ResourceTypeDelegate.instance.getResourceTypeById(id));
//...
addNotificationPreloaderPart("XXXid", XXXResultNotification, XXXFaultNotification);
xxxProxy.load(blablabla);
//...
activate();
}

* Override it to validate loaded data. If ERROR - you have to setup
* preloader.errorKey before returning false value.
function validate():Boolean
{
if(preloader.errorKey == null)
    {
// достаем нужное нам данное и проверяем
getPart("YYYid") – используем для проверки
    }
return (preloader.errorKey == null);
}



Пример прелоадера «детям до 16»
publicclassManageGroupMembershipPreloaderextendsBasePreloaderMediator
{
overridepublicfunction load():void
  {
varid:Number = getID(true);
if(!isNaN(id))
addDelegatePreloaderPart(ContactProxy.CONTACT,
ContactDelegate.instance.getContactById(id) );

addNotificationPreloaderPart(LanguageProxy.PLAIN_LANGUAGE_LIST,
SPNotification.PLAIN_LANGUAGE_LIST_LOADED,
SPNotification.PLAIN_LANGUAGE_LIST_FAILED);

LanguageProxy(facade.retrieveProxy(LanguageProxy.NAME)).getPlainLanguageList();

super.load();
  }

overridepublicfunction validate():Boolean
  {
if(super.validate())
    {
varcontact:Contact = getPart(ContactProxy.CONTACT) as Contact;
if(contact != null&&contact.age< 16)
setPreloaderError("Просмотр этой страницы не разрешен детям до 16");
returnsuper.validate();
    }
}



Модульность
В настоящее время система имеет:
 полторы сотни (150) основных страниц со связанными с ними rollover формами,
 несколько десятков popup-форм
 некоторое кол-во подсистем,
        работающих постоянно на экране,
        вызываемых по требованию пользователя
   или работающий в качестве «демонов» – daemon (ping, systemmessages)
          и«сервисов» (messages, multiple operations, progress indication etc.)

Кодовая база клиентской части проекта включает более 2000 файлов. Из них более 1600 as файлов и
около 400 mxml.

При этом с точки зрения заказчика это все разбито на 8 логических «модулей» и лицензия на
использование каждого из них может покупаться отдельно.

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

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

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

RSLдля этого использовать нельзя, так как для RSLприходилось бы вручную выбирать какие классы включать в эту
библиотеку а какие - нет. Что неприемлемо при колве классов больше 2000.

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

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

Таким образом, модуль подгружает ViewPageCommandв момент когда впервые понадобиться страница содержащаяся в
этом модуле. После загрузки модуля (и всех его классов) все PageDescriptors, содержавшиеся в этом модуле добаляются
в список доступных страниц в PagesProxy.


Структура иерархии модулей

       Module – единица компиляции и линковки
       ModuleDescription – содержитполныйнаборвсехPageDescriptorsмодуля
       PageDescriptionsXXX – логически сгруппированные наборы дескрипторов страниц
       moduleDescriptors.xml – справочник, содержащий информацию о том какая страница в каком
       модуле содержится

   flex-include-vo.xml – список всех VO системы для включения в ядро системы (решение проблемы
   регистрации VO для ремотинга)

   Соответствующие ANTскрипты. !!!НИКОГДА не включайте модули из среды FlashBuilder!!!



Проблема регистрации классов для римотинга.
[RemoteClass(alias="com.os.sp.domain.messaging.MessagesGroup")]
publicclassMessagesGroupextendsMessagesGroupBaseimplementsIOSSPMessage{}

Не регистрирует класс, если VOлинкуется через модуль!

Решение проблемы:

   1) вмодуледелаемregisterServerClass(MessagesGroup);

publicstaticfunctionregisterServerClass(classRef:Class):void
{
registerClassAlias(getClassServerName(classRef), classRef);
}

    2) линкуем все VOв ядро системы (flex-include-vo.xml

<?xmlversion="1.0"?>
<flex-config>
<includesappend="true">
<symbol>com.os.sp.domain.administration.authorization.ContactGroupIPRange</symbol>
<symbol>com.os.sp.domain.messaging.MessagesGroup</symbol>
<symbol>com.os.sp.domain.messaging.SmsCostConfig</symbol>
<symbol>com.os.sp.domain.integrity.InvalidRecord</symbol>
<symbol>com.os.sp.domain.reporting.monitoring.GenerationState</symbol>
</includes>
</flex-config>


publicfunctionModuleMain()
{
super();

    _moduleDescription = newModuleDescriptionMain();

//NameUtils.registerServerClass(ContactTask);
//NameUtils.registerServerClass(ReportResponse);

initializeModule();
}


publicfunctionModuleDescriptionMain()
{
super("ModuleMain", [], []
        .concat(PageDescriptionsMain.pageDescriptions)
        .concat(PageDescriptionsMyShadowPlanner.pageDescriptions)
        .concat(PageDescriptionsContacts.pageDescriptions)
        .concat(PageDescriptionsResourcesAssets.pageDescriptions)
        .concat(PageDescriptionsReporting.pageDescriptions)
        );
}

publicclassPageDescriptionsResourcesAssets

{
publicstaticconstcommonBreadcrumbs:Array = [Pages.R_A_VIEW_ROOT_RESOURCES];

publicstaticconstcommonProxies:Array = [ResourceTypeProxy, ParentListProxy,
SessionDataProxy,AssetProxy, ProcessEditProxy, ProcessesListProxy];

publicstaticconstpageDescriptions:Array = [/////////////////////////////////////////////


newPageDescription(Pages.UPDATE_RESOURCE_TYPE,
PageState.ADD_EDIT_VIEW,
ResourceTypeEditPreloader,
Pages.R_A_VIEW_ROOT_RESOURCES,
    [ResourcesSlotHolderMediator, ResourceTypeEditMediator],
    [SessionDataProxy, AssetProxy, ProcessEditProxy],
[
newCommandDescription(SPNotification.FIND_ASSET_FOR_DETAILS, LoadAssetCommand),
newCommandDescription(SPNotification.FIND_ASSET_FOR_EDIT, LoadAssetEditCommand),
],
    {accessArea: AccessArea.RESOURCE_TYPES},
commonBreadcrumbs.concat(),
"page.updateResourceType.title",
"page.updateResourceType.breadcrumbLabel",
null
).addAddPageState(null,
"page.createResourceType.title",
"page.createResourceType.breadcrumbLabel"
).addViewPageState(null,
"page.updateResourceType.viewTitle",
"page.updateResourceType.viewBreadcrumbLabel"
).addLibraryPageState(
Pages.R_A_UPDATE_RESOURCE_TYPE_FOR_LIBRARY
),

newPageDescription(
Pages.QUESTIONNAIRE, PageState.EDIT_VIEW,
QuestionnaireAnswerPreloader, null,
    [QuestionnaireAnswerMediator],
    [QuestionnairesProxy, ProcessEditProxy, ProcessTypesListProxy],
null,
    {questionnaireComponentState:QuestionnaireComponentState.ANSWER},
    [TreeNodeBase.NODE_BIA]
).registerRollover(Rollovers.QUESTIANNAIRE_RECOVERY_SCHEDULE, PageState.ADD_EDIT_VIEW
).registerRollover(Rollovers.QUESTIONNAIRE_ADD_PROCESS, PageState.ADD_EDIT_VIEW
)

] /////////////////////////////////////////////
}

moduleDescriptors.xml

<?xmlversion="1.0"encoding="UTF-8"?>
<Modules>
<Modulename="ModuleMain">
<Pages>
<Pagename="about"/>
<Pagename="myTasksPage"/>
<Pagename="myResponsibilitiesPage"/>
<Pagename="myPasswordPage"/>
</Pages>
<Dependencies/>
</Module>
<Modulename="ModuleAdministration">
<Pages>
<Pagename="adminLogo"/>
<Pagename="adminLanguageEditor"/>
<Pagename="auditTrail"/>
</Pages>
<Dependencies/>
</Module>
<Modulename="ModuleQuestionnaires">
<Pages>
<Pagename="questionnaires"/>
<Pagename="questionnaire"/>
<Pagename="questionnaireTemplates"/>
</Pages>
<Dependencies/>
</Module>
</Modules>


Фрагмент ant-task-а компиляции основного приложения (ядра системы)
<property name="FLEX_INCLUDE_VO_CONFIGURATION" value="${MAIN_SOURCE_FOLDER}/flex-include-vo.xml" />

<mxmlc file="${MAIN_SOURCE_FOLDER}/${ROOT_APPLICATION}.mxml"
output="@{output}.swf"
link-report="${BUILD_FOLDER}/${ROOT_APPLICATION}.${FULL_LINK_REPORT_POSTFIX}"
>
<load-configfilename="${FLEX_LOCAL_CONFIGURATION}"/>
</mxmlc>

Компиляциямодулей
<targetname="compile-modules"if="flex.modular.exist">
<compile-module-simplemoduleName="ModuleAdministration"/>
<compile-module-simplemoduleName="ModuleMessaging"/>
<compile-module-simplemoduleName="ModuleImportExport"/>
</target>

Фрагмент из макроопределения компиляции модуля
<mxmlcfile="${MODULE_SOURCE_FOLDER}/@{moduleName}.@{moduleType}"
output="${BUILD_FOLDER}/@{moduleName}.swf"
load-externs="${BUILD_FOLDER}/@{dependsOn}.${FULL_LINK_REPORT_POSTFIX}"
>
</mxmlc>




Приложение: особенности реализации SlotHolderмедиаторов
Задача по созданию и связыванию вью компонентов с медиаторами возложена на медиатор.

Медиатору известен класс его компонента. При создании медиатора – в методе create() – медиатор создает
инстанс класса вью компонента и отправляет его слот-холдермедиатору чтобы тот поместил его себе в
DisplayList.

protectedfunctionsetupMediator(…)
mediatorName:String = null,
// AUTO – такое же как имя класса – для регистрации в PureMVC

viewComponentReference:Object = null,
// 1) null must be null,
  // 2) component class
  // 3) component instance (descendant of UIComponent)

viewComponentName:String = null, // viewComponent custom name
// AUTO – по имени медиатора без постфикса Mediator
  // будет использовано как id и name в слотхолдере

viewComponentSlot:String = null, // specific slot for register/find viewComponent (if need)

viewComponentInitAction:int = 0 // special init action (0 - no action)
       INIT_NONE:int = 0; // no action

       INIT_FIND_OR_WAITING_FOR_THE_VIEW_COMPONENT:int = 1;
       // find view component with appropriate name in appropriate slot or (in not found)
       // listen for the BaseNotifications.SLOT_VIEW_COMPONENT_CREATION_COMPLETE notification

       INIT_FIND_VIEW_COMPONENT:int = 2;
       // find view component with appropriate name in appropriate slot

       INIT_ADD_TO_SLOT:int = 4;
       // add view component to appropriate slot using appropriate name


overridepublicfunctionlistNotificationInterests():Array
{
return (slots == null)
    ? super.listNotificationInterests()
    : super.listNotificationInterests().concat(
BaseNotifications.SLOT_VIEW_COMPONENT_ADD,
BaseNotifications.SLOT_VIEW_COMPONENT_REMOVE_BY_NAME,
BaseNotifications.SLOT_VIEW_COMPONENT_SHOW,
BaseNotifications.SLOT_VIEW_COMPONENT_HIDE,
BaseNotifications.SLOT_VIEW_COMPONENT_FIND
      );
}

overridepublicfunctionhandleNotification(notification:INotification):void
{
super.handleNotification(notification);
  …
}

privatefunction defaultSlotHolderNotificationHandler(notification:INotification):void
{
if(slots && slots[notification.getType()]) {
vardesc:SlotViewComponentDescription = notification.getBody() as SVCD;
varcomponent:UIComponent;
component = getViewComponentFromSlot(desc.viewComponentSlot, desc.viewComponentName);
switch(notification.getName()) {
caseBaseNotifications.SLOT_VIEW_COMPONENT_SHOW:
FlexUIComponentsUtils.showViewComponent(component, true);
break;
caseBaseNotifications.SLOT_VIEW_COMPONENT_HIDE:
component.visible = false;
break;
caseBaseNotifications.SLOT_VIEW_COMPONENT_ADD:
addViewComponentToInternalSlot(desc);
desc.viewComponent = null; // mark viewComponent as added
break;
caseBaseNotifications.SLOT_VIEW_COMPONENT_REMOVE_BY_NAME:
removeViewComponentFromSlotByName(desc);
break;
caseBaseNotifications.SLOT_VIEW_COMPONENT_FIND:
desc.viewComponent = component;
break;
}}}



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

* Register handler for Notification - works just like addEventListebner.
* It doesn't depend on registerMediator/removeMediator !BE AWARE!
functionaddNotificationListener(notificationName:String, handler:Function):void
{
org.puremvc.as3.core.View.getInstance().registerObserver(
notificationName, new Observer(handler, this) );
}

* Remove Notification handler - works just like removeEventListebner.
* It doesn't depend on registerMediator/removeMediator !BE AWARE!
functionremoveNotificationListener(notificationName:String):void
{
org.puremvc.as3.core.View.getInstance().removeObserver(
notificationName, this );
}

Примериспользования:
* Try to add view component in appropriate external slot. If there isn't
* appropriate slot then start waiting for SLOT_CREATION_COMPLETE.
functionaddViewComponentToExternalSlot():void {
vardesc:SlotViewComponentDescription = newSlotViewComponentDescription
viewComponentName, viewComponentSlot, viewComponentasUIComponent );

varaddNotification:INotification = new Notification(
BaseNotifications.SLOT_VIEW_COMPONENT_ADD, desc, viewComponentSlot);

facade.notifyObservers(addNotification);

if(desc.viewComponent != null) // егобыобнулилиеслибыонбылнайден
    {
addNotificationListener(BaseNotifications.SLOT_CREATION_COMPLETE,
handleSlotCreationCompleteNotificationToAddViewComponent);
    }
}

More Related Content

PPTX
Enterprise flex pure mvc, slides, russian
PDF
C# Web. Занятие 07.
PDF
C# Web. Занятие 09.
PDF
Модульное тестирование с помощью visual studio 2012 MS Test, Nunit, X-unit.ne...
PPTX
Ivan Shaban - Robotlegs 2+
PPT
Eleanor
PPTX
создание живых сайтов
PDF
Диагностика проблем в промышленной среде с помощью Intelli Trace и Visual Stu...
Enterprise flex pure mvc, slides, russian
C# Web. Занятие 07.
C# Web. Занятие 09.
Модульное тестирование с помощью visual studio 2012 MS Test, Nunit, X-unit.ne...
Ivan Shaban - Robotlegs 2+
Eleanor
создание живых сайтов
Диагностика проблем в промышленной среде с помощью Intelli Trace и Visual Stu...

What's hot (20)

PPT
Moxy – реализация MVP под Android. С щепоткой магии
PDF
Введение в тестирование с использованием закодированных автоматических тестов...
PDF
C# Web. Занятие 11.
PDF
Role based access-control
PDF
Визулизация ветвления и объединения в Visual Studio Team Foundation Server 2012
PPTX
MVP, Moxy. Как правильно пользоваться
PDF
Разработка оптимального ПО - создание раскадровок и сбор отзывов от заинтерес...
PPTX
Создание графического интерфейса пользователя мобильных Android приложений (ч...
PPTX
Разработка WPF приложений в стиле ViewModel First
PDF
Dw Shark Ru
PDF
C# Web. Занятие 13.
PPTX
Как написать XAML-приложение без Message Bus
PDF
Проектирование и выполнение ручных тестов с использованием Microsoft Test Man...
PPTX
FFCMS - вводная для пользователя
PDF
Изучение кода с использованием инструментов архитектуры в Visual Studio Ultim...
PDF
Гибкое управление проектами в Visual Studio Team Foundation Server 2012
PDF
«Как я научился не волноваться и полюбил Android-MVP», Никита Бартишок, ABBYY
PPTX
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
PPTX
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
PPT
Uafpug 8 Presentation Puremvc Papervision Gallery Kuriksha Dmitry
Moxy – реализация MVP под Android. С щепоткой магии
Введение в тестирование с использованием закодированных автоматических тестов...
C# Web. Занятие 11.
Role based access-control
Визулизация ветвления и объединения в Visual Studio Team Foundation Server 2012
MVP, Moxy. Как правильно пользоваться
Разработка оптимального ПО - создание раскадровок и сбор отзывов от заинтерес...
Создание графического интерфейса пользователя мобильных Android приложений (ч...
Разработка WPF приложений в стиле ViewModel First
Dw Shark Ru
C# Web. Занятие 13.
Как написать XAML-приложение без Message Bus
Проектирование и выполнение ручных тестов с использованием Microsoft Test Man...
FFCMS - вводная для пользователя
Изучение кода с использованием инструментов архитектуры в Visual Studio Ultim...
Гибкое управление проектами в Visual Studio Team Foundation Server 2012
«Как я научился не волноваться и полюбил Android-MVP», Никита Бартишок, ABBYY
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Uafpug 8 Presentation Puremvc Papervision Gallery Kuriksha Dmitry
Ad

Viewers also liked (7)

PPSX
Rồng Bay - Mai-Hương
PDF
Final magazine front cover
PPTX
If you can’t be found you won’t be found final
DOCX
maybomnuocpanasonic
PPTX
Tratando seu blog como um (ótimo) negócio
PPTX
Icdl Clase2
PPTX
AIESEC in Poland Expansion Process
Rồng Bay - Mai-Hương
Final magazine front cover
If you can’t be found you won’t be found final
maybomnuocpanasonic
Tratando seu blog como um (ótimo) negócio
Icdl Clase2
AIESEC in Poland Expansion Process
Ad

Similar to Enterprise flex pure mvc.v4 (20)

PPT
PureMVC and Papervision
PPT
UAFPUG6 - PureMVC
PDF
MWWM
PDF
Шаблоны проектирования в Magento
PPTX
Views обзор
PPT
Eleanor
PDF
Yury Glushkov.What should we build a website.Drupal Camp Kyiv 2011
PPT
Symfony2 practice
PPTX
«трудности при разработке сложных распределённых систем на Java. способы реше...
PPT
Drupal организация разработки
PPT
Drupal -organizaciya_razrabotki
PPTX
ASP.NET, MVC, ASP.NET MVC
PPTX
Backbone lesson 1
PDF
Аудит Active directory. Обзор программы NetWrix Active Directory Change Reporter
PPTX
Как пройти собеседование и получить первую работу на Swift
PPT
Eleanor CMS
PDF
Programming Guide
PDF
ВІТАЛІЙ ГОНЧАРУК «За допомогою чого пишуться серйозні веб додатки на .NET» O...
PPT
Сервлеты
PPTX
Web deployment
PureMVC and Papervision
UAFPUG6 - PureMVC
MWWM
Шаблоны проектирования в Magento
Views обзор
Eleanor
Yury Glushkov.What should we build a website.Drupal Camp Kyiv 2011
Symfony2 practice
«трудности при разработке сложных распределённых систем на Java. способы реше...
Drupal организация разработки
Drupal -organizaciya_razrabotki
ASP.NET, MVC, ASP.NET MVC
Backbone lesson 1
Аудит Active directory. Обзор программы NetWrix Active Directory Change Reporter
Как пройти собеседование и получить первую работу на Swift
Eleanor CMS
Programming Guide
ВІТАЛІЙ ГОНЧАРУК «За допомогою чого пишуться серйозні веб додатки на .NET» O...
Сервлеты
Web deployment

Enterprise flex pure mvc.v4

  • 1. FlexPure MVC архитектура для приложений enterprise уровня Описание: 1. Сначала мы вспомним основные составляющие микроахитектурыPureMVC, используя презентацию SamuelAsherRivello. 2. Далее рассмотрим структуру и задачи, возникающие при использовании PureMVC в больших проектах. a. Разновидности и жизненный цикл элементов системы b. Необходимость синхронизации работы медиаторов с асинхронно создаваемыми видимыми элементами. 3. «Страничная» архитектура для PureMVC проекта: a. Управление View компонентами в «страничной» архитектуре: i. отложенное (deferred) создание, ii. расположение в иерархии DisplayObjects- слоты, медиаторы-слотхолдеры и их взаимодействие с медиаторами-клиентами. b. Жизненный цикл «страницы», дескриптор «страницы» c. Прелоадер страницы 4. Взаимодействие View Components и Mediators a. Шаблон «страницы» b. Интерфейс «страничного» View Component c. Обмен данными между медиатором и View компонентом. d. Состояние «редактирования» (Editing state) e. Обработка нажатий кнопок (Buttons) f. Обработка других команд (пример – Chevrons) 5. Использование модулей в «страничной» архитектуре. Требования к слушателям: знание AS3, жизненного цикла компонентов Flex, PureMVChttp://puremvc.org Докладчик: Сергей Шичинов - SergiyShychynov (EPAM)Sergiy_Shychynov@epam.com Примечание: пункт 4 был исключён из доклада для того чтобы длительность доклада соответствовала регламенту. 2012-03-24
  • 2. Основные составляющие PureMVC (используя презентацию SamuelAsherRivello) http://www.adobe.com/newsletters/inspire/december2008/articles/article6/index.html MVC in PureMVC Схема взаимодействия компонентов PureMVC для выполнения элементарной операции: - Внешний вид компонента HelloGoogle – кнопка и текстовое поле для сообщения - Последовательность элементарных действий выполняемых при нажатии кнопки - Положение этих действий на диаграмме PureMVC
  • 4. Основные задачи, возникающие при использовании PureMVC в больших проектах. Когда речь идет о приложение enterprise уровня, то это означает что они БОЛЬШИЕ. То есть имеется БОЛЬШОЕ количество РАЗНЫХ форм и компонентов, которые необходимо будет использовать в разных ситуациях и в разное время. И речи о том, что все элементы будут созданы одновременно или заранее быть не может. Более того – было бы неплохо, чтобы и грузить их все сразу не требовалось. Рассмотрим несколько разных «видов» гипотетического большого приложения.
  • 6. Среди всего UIбольшого приложения можно выделить элементы (подсистемы): 1. Которые постоянно находятся на экране и доступны для взаимодействия. 2. Которые могут вызываться пользователем на экран по желанию и ведут себя относительно независимо от других элементов UI. 3. Которые имеют отношение к текущей операции, выполняемой пользователем, показываются на экране когда он начинает выполнять эту операцию и будут убраны с экрана когда он эту операцию завершит.Причем таких циклов появления и исчезновения с экрана может быть много. Этот третий тип подсистем мы будем в дальнейшем называть страницами – Pages.
  • 7. И если операции по обслуживанию жизненного цикла (описанные далее) для компонетов 1 и 2 типа можно выполнить один раз (например в StartupCommand), то выполнение всех нижеперечисленных действий для компонентов подсистем 3-его типа является рутинной операцией и нуждается в автоматизации. Жизненный цикл элементов системы Каждая подсистема 3 типа («страница») может состоять из одного или нескольких видимых элементов, функционирующих совместно в одном промежутке времени. И для функционирования любой отдельной подсистемы нашей большой системы нам будет необходимо: 1. Создать (инстанциировать) и разместить на экране (в иерархии DisplayObjects) необходимые компоненты - ViewComponents. (Здесь и дальше компонентом будет называться видимый элемент, занимающий место в иерархии DisplayObjects.) 2. создать и зарегистрировать необходимые медиаторы – Mediators. 3. связать медиаторы с соответствующими компонентами a. медиаторы должны иметь ссылки на свои компоненты, для того чтобы передавать в них данные и совершать действия b. медиаторы прослушивают события – Events от компонентов для того чтобы узнать что нужно что- то делать 4. зарегистрировать необходимые для работы подсистемы комманды – Commands (связать их с Notifications), 5. создать и зарегистрировать необходимые для работы прокси–Proxy, 6. отобразить в наших компонентах валидные данные и обеспечить взаимодействие a. Как правило, эти данные загружаются извне (с сервера). Это может занять определенное время. Эти данные возможно не получится загрузить в результате ошибки. И возможно имеются некие политики безопасности, которые в зависимости от того какие именно данные хочет увидеть пользователь, могут не разрешить ему увидеть эти данные – и мы не узнаем о том разрешено это или нет, пока не получим ответ с сервера. Во всех этих случаях неразумно показывать на экране новые компоненты, еще не заполненные данными, до того как эти данные будут получены с сервера. Это процесс мы будем называть предзагрузка – preloader. b. Во время работы пользователя с подсистемой, коммуникация с сервером обеспечивается по стандартной схеме изложенной выше.
  • 8. c. Отдельный вопрос – это обработка ошибок (серверных) – в зависимости от типа ошибок мы может либо дальше продолжать оставаться на странице, либо потребуется обновить ее, либо придется покинуть ее. 7. После того как пользователь завершил операцию нам необходимо корректно освободить ресурсы системы от текущей «страницы» (очевидно перед тем как будет открыта следующая). a. Удаление «актеров» PureMVC (медиаторов) i. «отписать» медиаторы от системы (unregister) чтобы они не прослушивали больше нотификейшены. ii. Можно также удалять медиаторы, но можно сохранять их в «кэше» до следующего использования. iii. Команды тоже можно «отписать» от системы, но это не обязательно. iv. Некоторые прокси можно было бы тоже отписывать от системы и удалять, но обычно этого не нужно. И даже вредно. b. Отписывание медиаторов от events, которые они прослушивали от компонентов. c. Удаление видимых компонентов. Вообще говоря, операция создания компонента и добавления его в DisplayList гораздо более ресурсоемкая, чем сделать невидимый компонент – видимым. Поэтому для увеличения перформанса и для того чтобы видимые элементы появлялись более плавно вместо удаления компонентов лучше делать их невидимыми. d. Если в процессе выполнения операций были загружены какие-то большие данные и ссылки на них сохранились в компонентах или «актерах» PureMVC – то нужно их очистить, чтобы позволить GarbageCollector –у освободить память. Необходимость синхронизации работы медиаторов с асинхронно создаваемыми видимыми элементами. Как известно видимые элементы – компоненты флекса не создаются мгновенно по запросу. Как правил от момента создание экземпляра компонента, установки его свойств и добавления его в иерархию DisplayObjectsпроходит некоторое время и несколько этапов, прежде чем событие «creationComplete»дает нам знать, что компонент и все его подкомпоненты полностью созданы и готовы к работе. До этого момента медиатор, даже если и будет иметь ссылку на компонент, не сможет полноценно с ним работать. DefferedMediator Решает проблему асинхронного создания компонентов. Откладывает вызовы initialize и activate до момента, когда компонент будет создан и инициализирован. А потом еще откладывает update до момента, когда все медиаторы из списка будут активированы. Constructor() – чистый конструктор (не выполняющий никаких действий) * Override it to complete setup mediator properties and associate mediator * with the View Component. Use setupMediator function to do most of the tasks. function create():void * The method is called after component <code>creationComplete</code> event. * Create it to add listeners and make additional view component initialization. function initialize():void; * This method is called every time when you show and activate the view component. * Override it to add additional mediator's and view component's activation. functionactivate():void; waitForMediatorsActivation(mediatorNames); * Override it to update view component in accordance with current viewParams. * You can process here both stateParams and viewParams. * Is called after initialize() (after creationComplete event) and
  • 9. * activate() methods. function update():void * Deactivate mediator (remove from pureMVC workflow). * You can override it to make additional deactivation. functiondeactivate():void; * Create it to remove listeners and make additional view component finalization. functionfinalize():void; Кто, когда и как создает видимые элементы. Слоты, медиаторы-слотхолдеры и их взаимодействие с компонентами-клиентами. Задача по созданию и связыванию вью компонентов с медиаторами возложена на медиатор. Медиатору известен класс его компонента. При создании медиатора – в методе create() – медиатор создает экемпляр вью компонента и помещает его в DisplayList. Делается это с помощью концепции медиаторов- слотхолдеров, которые имеют в составе своего компонента slot – контейнер (обычно ViewStack) в который можно поместить, найти и удалить комопнент. SlotViewComponentDescription viewComponentName:String, viewComponentSlot:String, viewComponent:UIComponent SlotAMediator M1Mediator медиатор-слотхолдер медиатор-клиент SlotAMediator получает M1Mediator создает нотификейшен, адресованный ему компонет и отправляет его и добавляет в свой слот (ViewStack слоту "SlotAMediator" - или другой контейнер) новый просит его хранить под компонет под именем "M1" именем "M1"
  • 11. «Страничная» архитектура для PureMVC проекта Мы ввели понятие страниц – Pages. «Страница»– это некоторое множество видимых элементов, функционирующих совместно в одном промежутке времени. Эти компоненты и обслуживающие их актеры PureMVC совместно создаются, работают и удаляются. Выполнением всех этих операций в PureMVC традиционно занимаются команды. Чтобы не писать отдельные команды для открытия каждой страницы, была создана единая команда открытия новой страницы ViewPageCommand. Эта команда выполняет все необходимые при открытии страницы действия в соответствии с переданным ей в качестве входного параметра дескриптором страницы. Она работает совместно с PagesProxy, которыйи является хранителем информации обо всех доступных в системе «страницах» и «состояния» текущей открытой страницы. Основные задачиPagesProxy: - хранение справочника всех страницах в системе functiongetPageDescription(pageName:String):PageDescription - хранение информации о текущей открытой странице (ее имя, дескриптор, стейт, состояние редактирование, helpContext и прочее) Соответственно все страницы имеют уникальное имя publicclass Pages { publicstaticconstABOUT:String = "about"; publicstaticconstADMIN_EMAIL_TEMPLATE_EDIT:String = "adminEmailTemplateEdit"; publicstaticconstDOCUMENT_TRANSLATION:String = "documentTranslation"; … } идескрипторы PageDescription pageName:String, preloader:Class, - наследникBasePagePreloader mediators:Array, - списокклассовнаследониковBaseMediator proxies:Array = null, commands:Array = null, - списокCommandDescription(name:String, commandClass:Class) stateParams:Object = null, - редко встречающиеся дополнительные параметры pageTitle:String = null, } - часто встречающиеся дополнительные параметры breadcrumbs:Array = null. … } Примердескриптора страницы: newPageDescription(Pages.QUESTIONNAIRE, QuestionnaireAnswerPreloader, [QuestionnaireSlotHolderMediator, QuestionnaireAnswerMediator], [QuestionnairesProxy, ProcessEditProxy, ProcessTypesListProxy, TimeScaleProxy], [ newCommandDescription(SPNotification.FIND_NEW_QUEST, LoadQuestiannairCommand), newCommandDescription(SPNotification.UPDATE_ASSET_QUEST, UpdateAssetForQuestCommand), ], {questionnaireComponentState: QuestionnaireComponentState.ANSWER}, "Questionnaire Registry" ) PageNotificationParams varpageName:String;
  • 12. varpageState:String; varid:Number; varbackPageName:String; varpreloader:BasePreloaderMediator; - создается и добавляется в процессе открытия страницы ViewPageCommand 1. Проверяет наличие дескриптора страницы и при необходимости подгружает модуль содержащий страницу. Входными параметрами для открытия страницы являются: a. pageDescriptor – находится по имени страницы b. stateParams : {} – является дополнительным набором параметров страницы (которые не вошли в сам дескрипотор – так как редко используются) c. viewParams: PageNotificationParams – дополнительные параметры страницы, заполняемые при ее открытии 2. Создает и регистрирует необходимые прокси (они могут быть нужны прелоадеру) 3. Создает и запускает прелоадер a. load() b. validate() + препроцессинг 4. Если прелоадер «дал добро», то закрывает текущую страницу a. отписывает от системы медиаторы b. подчищает «временные» данные (на вью компонентах и медиаторах!) c. отписывает медиатор от events компонентов d. добавляет ссылку на прелоадер с загруженными и подготовленными данными во viewParams 5. Регистрирует команды 6. Создает (или достает из кеша) медиаторы, передает им значения stateParams и viewParams и регистрирует их в системе. a. Если медиаторы создаются то это делается в следующем порядке i. Конструктор ii. Setup params (stateParamsиviewParams) iii. create() b. если достается из кеша, то просто Setupparams (stateParams и viewParams) 7. Активирует медиаторы. В процессе активации медиаторы должны создать свои компоненты, разместить их в иерархии DisplayLists, сделать видимыми и подписаться на необходимые events. 8. После активации всех медиаторов вызывается update() Прелоадер – Preloader Прелоадерслужитдлятогочтобызагрузитьнесколько «частей» данных (используяпроксиилинепосредственноделегаты). После полной загрузки всех частей возможна их валидация и дополнительная обработка. Прелоадер отрабатывает при попытке открыть новую «страницу» до того как она будет реально открыта. В случае если валидатор прелоадера выдает false, страница не будет открыта. Вместо этого будет выведено сообщение об ошибке. Фрагмент кода вызова прелоадера при открытии страницы if(pageDesc.preloader != null) { setLoadingState(true); registerProxies(); // for preloadin purposes varpreloaderMediator:BasePreloaderMediator = new (pageDesc.preloader)(); preloaderMediator.viewParams = params; preloaderMediator.stateParams = pageDescriptor.stateParam; preloaderMediator.callback = executeAfterPreload; preloaderMediator.load(); BasePreloaderMediator varstateParams:Object; varviewParams:PageNotificationParams;
  • 13. * Override it to create more preloader parts. You can use viewParams and stateParams. function load():void { //... addDelegatePreloaderPart("YYYid", ResourceTypeDelegate.instance.getResourceTypeById(id)); //... addNotificationPreloaderPart("XXXid", XXXResultNotification, XXXFaultNotification); xxxProxy.load(blablabla); //... activate(); } * Override it to validate loaded data. If ERROR - you have to setup * preloader.errorKey before returning false value. function validate():Boolean { if(preloader.errorKey == null) { // достаем нужное нам данное и проверяем getPart("YYYid") – используем для проверки } return (preloader.errorKey == null); } Пример прелоадера «детям до 16» publicclassManageGroupMembershipPreloaderextendsBasePreloaderMediator { overridepublicfunction load():void { varid:Number = getID(true); if(!isNaN(id)) addDelegatePreloaderPart(ContactProxy.CONTACT, ContactDelegate.instance.getContactById(id) ); addNotificationPreloaderPart(LanguageProxy.PLAIN_LANGUAGE_LIST, SPNotification.PLAIN_LANGUAGE_LIST_LOADED, SPNotification.PLAIN_LANGUAGE_LIST_FAILED); LanguageProxy(facade.retrieveProxy(LanguageProxy.NAME)).getPlainLanguageList(); super.load(); } overridepublicfunction validate():Boolean { if(super.validate()) { varcontact:Contact = getPart(ContactProxy.CONTACT) as Contact; if(contact != null&&contact.age< 16) setPreloaderError("Просмотр этой страницы не разрешен детям до 16"); returnsuper.validate(); } } Модульность В настоящее время система имеет:  полторы сотни (150) основных страниц со связанными с ними rollover формами,  несколько десятков popup-форм  некоторое кол-во подсистем,  работающих постоянно на экране,  вызываемых по требованию пользователя
  • 14. или работающий в качестве «демонов» – daemon (ping, systemmessages)  и«сервисов» (messages, multiple operations, progress indication etc.) Кодовая база клиентской части проекта включает более 2000 файлов. Из них более 1600 as файлов и около 400 mxml. При этом с точки зрения заказчика это все разбито на 8 логических «модулей» и лицензия на использование каждого из них может покупаться отдельно. Кроме того многие пользователи могут не иметь доступа к некоторым модулям (администрирование, отчеты) по соображениям секьюрности или в соответствии с их ролями. Понятно, что вопрос о том чтобы не грузить все это сразу при открытии приложения возник уже давно.То есть необходимо при запуске загрузить только ядро приложения а решение о необходимости подгрузки дополнительного функционала уже принимать по ситуации. Необходимо: при начальной загрузке загружать только ядро системы, а все дополнительные, не всегда или редко используемые возможности подгружать по требованию. Для уменьшения времени загрузки и потребляемого трафика. RSLдля этого использовать нельзя, так как для RSLприходилось бы вручную выбирать какие классы включать в эту библиотеку а какие - нет. Что неприемлемо при колве классов больше 2000. Флексовые модули для этого как бы не задумывались (это скорее неудачный способ монетизации флексовых приложений), но воспользоваться ими получилось. - Флексовый линкер при компиляции модуля позволяет собрать все классы, по цепочке зависимостей - есть возможность указать линкеру, что необходимо исключить классы, которые уже есть в ядре приложения. И в разбиении системы на модули очень помогло то, что мы имели большинство функционала, организованного в виде «страниц». Страницы были использованы как ноды для дерева линковки всех классов, необходимых для работы текущего модуля. Таким образом, модуль подгружает ViewPageCommandв момент когда впервые понадобиться страница содержащаяся в этом модуле. После загрузки модуля (и всех его классов) все PageDescriptors, содержавшиеся в этом модуле добаляются в список доступных страниц в PagesProxy. Структура иерархии модулей Module – единица компиляции и линковки ModuleDescription – содержитполныйнаборвсехPageDescriptorsмодуля PageDescriptionsXXX – логически сгруппированные наборы дескрипторов страниц moduleDescriptors.xml – справочник, содержащий информацию о том какая страница в каком модуле содержится flex-include-vo.xml – список всех VO системы для включения в ядро системы (решение проблемы регистрации VO для ремотинга) Соответствующие ANTскрипты. !!!НИКОГДА не включайте модули из среды FlashBuilder!!! Проблема регистрации классов для римотинга. [RemoteClass(alias="com.os.sp.domain.messaging.MessagesGroup")] publicclassMessagesGroupextendsMessagesGroupBaseimplementsIOSSPMessage{} Не регистрирует класс, если VOлинкуется через модуль! Решение проблемы: 1) вмодуледелаемregisterServerClass(MessagesGroup); publicstaticfunctionregisterServerClass(classRef:Class):void { registerClassAlias(getClassServerName(classRef), classRef);
  • 15. } 2) линкуем все VOв ядро системы (flex-include-vo.xml <?xmlversion="1.0"?> <flex-config> <includesappend="true"> <symbol>com.os.sp.domain.administration.authorization.ContactGroupIPRange</symbol> <symbol>com.os.sp.domain.messaging.MessagesGroup</symbol> <symbol>com.os.sp.domain.messaging.SmsCostConfig</symbol> <symbol>com.os.sp.domain.integrity.InvalidRecord</symbol> <symbol>com.os.sp.domain.reporting.monitoring.GenerationState</symbol> </includes> </flex-config> publicfunctionModuleMain() { super(); _moduleDescription = newModuleDescriptionMain(); //NameUtils.registerServerClass(ContactTask); //NameUtils.registerServerClass(ReportResponse); initializeModule(); } publicfunctionModuleDescriptionMain() { super("ModuleMain", [], [] .concat(PageDescriptionsMain.pageDescriptions) .concat(PageDescriptionsMyShadowPlanner.pageDescriptions) .concat(PageDescriptionsContacts.pageDescriptions) .concat(PageDescriptionsResourcesAssets.pageDescriptions) .concat(PageDescriptionsReporting.pageDescriptions) ); } publicclassPageDescriptionsResourcesAssets { publicstaticconstcommonBreadcrumbs:Array = [Pages.R_A_VIEW_ROOT_RESOURCES]; publicstaticconstcommonProxies:Array = [ResourceTypeProxy, ParentListProxy, SessionDataProxy,AssetProxy, ProcessEditProxy, ProcessesListProxy]; publicstaticconstpageDescriptions:Array = [///////////////////////////////////////////// newPageDescription(Pages.UPDATE_RESOURCE_TYPE, PageState.ADD_EDIT_VIEW, ResourceTypeEditPreloader, Pages.R_A_VIEW_ROOT_RESOURCES, [ResourcesSlotHolderMediator, ResourceTypeEditMediator], [SessionDataProxy, AssetProxy, ProcessEditProxy], [ newCommandDescription(SPNotification.FIND_ASSET_FOR_DETAILS, LoadAssetCommand), newCommandDescription(SPNotification.FIND_ASSET_FOR_EDIT, LoadAssetEditCommand), ], {accessArea: AccessArea.RESOURCE_TYPES}, commonBreadcrumbs.concat(), "page.updateResourceType.title",
  • 16. "page.updateResourceType.breadcrumbLabel", null ).addAddPageState(null, "page.createResourceType.title", "page.createResourceType.breadcrumbLabel" ).addViewPageState(null, "page.updateResourceType.viewTitle", "page.updateResourceType.viewBreadcrumbLabel" ).addLibraryPageState( Pages.R_A_UPDATE_RESOURCE_TYPE_FOR_LIBRARY ), newPageDescription( Pages.QUESTIONNAIRE, PageState.EDIT_VIEW, QuestionnaireAnswerPreloader, null, [QuestionnaireAnswerMediator], [QuestionnairesProxy, ProcessEditProxy, ProcessTypesListProxy], null, {questionnaireComponentState:QuestionnaireComponentState.ANSWER}, [TreeNodeBase.NODE_BIA] ).registerRollover(Rollovers.QUESTIANNAIRE_RECOVERY_SCHEDULE, PageState.ADD_EDIT_VIEW ).registerRollover(Rollovers.QUESTIONNAIRE_ADD_PROCESS, PageState.ADD_EDIT_VIEW ) ] ///////////////////////////////////////////// } moduleDescriptors.xml <?xmlversion="1.0"encoding="UTF-8"?> <Modules> <Modulename="ModuleMain"> <Pages> <Pagename="about"/> <Pagename="myTasksPage"/> <Pagename="myResponsibilitiesPage"/> <Pagename="myPasswordPage"/> </Pages> <Dependencies/> </Module> <Modulename="ModuleAdministration"> <Pages> <Pagename="adminLogo"/> <Pagename="adminLanguageEditor"/> <Pagename="auditTrail"/> </Pages> <Dependencies/> </Module> <Modulename="ModuleQuestionnaires"> <Pages> <Pagename="questionnaires"/> <Pagename="questionnaire"/> <Pagename="questionnaireTemplates"/> </Pages> <Dependencies/> </Module> </Modules> Фрагмент ant-task-а компиляции основного приложения (ядра системы) <property name="FLEX_INCLUDE_VO_CONFIGURATION" value="${MAIN_SOURCE_FOLDER}/flex-include-vo.xml" /> <mxmlc file="${MAIN_SOURCE_FOLDER}/${ROOT_APPLICATION}.mxml" output="@{output}.swf" link-report="${BUILD_FOLDER}/${ROOT_APPLICATION}.${FULL_LINK_REPORT_POSTFIX}"
  • 17. > <load-configfilename="${FLEX_LOCAL_CONFIGURATION}"/> </mxmlc> Компиляциямодулей <targetname="compile-modules"if="flex.modular.exist"> <compile-module-simplemoduleName="ModuleAdministration"/> <compile-module-simplemoduleName="ModuleMessaging"/> <compile-module-simplemoduleName="ModuleImportExport"/> </target> Фрагмент из макроопределения компиляции модуля <mxmlcfile="${MODULE_SOURCE_FOLDER}/@{moduleName}.@{moduleType}" output="${BUILD_FOLDER}/@{moduleName}.swf" load-externs="${BUILD_FOLDER}/@{dependsOn}.${FULL_LINK_REPORT_POSTFIX}" > </mxmlc> Приложение: особенности реализации SlotHolderмедиаторов Задача по созданию и связыванию вью компонентов с медиаторами возложена на медиатор. Медиатору известен класс его компонента. При создании медиатора – в методе create() – медиатор создает инстанс класса вью компонента и отправляет его слот-холдермедиатору чтобы тот поместил его себе в DisplayList. protectedfunctionsetupMediator(…) mediatorName:String = null, // AUTO – такое же как имя класса – для регистрации в PureMVC viewComponentReference:Object = null, // 1) null must be null, // 2) component class // 3) component instance (descendant of UIComponent) viewComponentName:String = null, // viewComponent custom name // AUTO – по имени медиатора без постфикса Mediator // будет использовано как id и name в слотхолдере viewComponentSlot:String = null, // specific slot for register/find viewComponent (if need) viewComponentInitAction:int = 0 // special init action (0 - no action) INIT_NONE:int = 0; // no action INIT_FIND_OR_WAITING_FOR_THE_VIEW_COMPONENT:int = 1; // find view component with appropriate name in appropriate slot or (in not found) // listen for the BaseNotifications.SLOT_VIEW_COMPONENT_CREATION_COMPLETE notification INIT_FIND_VIEW_COMPONENT:int = 2; // find view component with appropriate name in appropriate slot INIT_ADD_TO_SLOT:int = 4; // add view component to appropriate slot using appropriate name overridepublicfunctionlistNotificationInterests():Array { return (slots == null) ? super.listNotificationInterests() : super.listNotificationInterests().concat( BaseNotifications.SLOT_VIEW_COMPONENT_ADD, BaseNotifications.SLOT_VIEW_COMPONENT_REMOVE_BY_NAME,
  • 18. BaseNotifications.SLOT_VIEW_COMPONENT_SHOW, BaseNotifications.SLOT_VIEW_COMPONENT_HIDE, BaseNotifications.SLOT_VIEW_COMPONENT_FIND ); } overridepublicfunctionhandleNotification(notification:INotification):void { super.handleNotification(notification); … } privatefunction defaultSlotHolderNotificationHandler(notification:INotification):void { if(slots && slots[notification.getType()]) { vardesc:SlotViewComponentDescription = notification.getBody() as SVCD; varcomponent:UIComponent; component = getViewComponentFromSlot(desc.viewComponentSlot, desc.viewComponentName); switch(notification.getName()) { caseBaseNotifications.SLOT_VIEW_COMPONENT_SHOW: FlexUIComponentsUtils.showViewComponent(component, true); break; caseBaseNotifications.SLOT_VIEW_COMPONENT_HIDE: component.visible = false; break; caseBaseNotifications.SLOT_VIEW_COMPONENT_ADD: addViewComponentToInternalSlot(desc); desc.viewComponent = null; // mark viewComponent as added break; caseBaseNotifications.SLOT_VIEW_COMPONENT_REMOVE_BY_NAME: removeViewComponentFromSlotByName(desc); break; caseBaseNotifications.SLOT_VIEW_COMPONENT_FIND: desc.viewComponent = component; break; }}} Далее описанантипаттерн, который используется для оптимизации взаимодействия слотхолдеров. Но мне не стыдно, потому что это, с одной стороны – служит оптимизации (можно было и не делать), с другой – можно было бы без этого обойтись просто воспользовавшись синглтоном-менеджером. * Register handler for Notification - works just like addEventListebner. * It doesn't depend on registerMediator/removeMediator !BE AWARE! functionaddNotificationListener(notificationName:String, handler:Function):void { org.puremvc.as3.core.View.getInstance().registerObserver( notificationName, new Observer(handler, this) ); } * Remove Notification handler - works just like removeEventListebner. * It doesn't depend on registerMediator/removeMediator !BE AWARE! functionremoveNotificationListener(notificationName:String):void { org.puremvc.as3.core.View.getInstance().removeObserver( notificationName, this ); } Примериспользования: * Try to add view component in appropriate external slot. If there isn't * appropriate slot then start waiting for SLOT_CREATION_COMPLETE. functionaddViewComponentToExternalSlot():void { vardesc:SlotViewComponentDescription = newSlotViewComponentDescription
  • 19. viewComponentName, viewComponentSlot, viewComponentasUIComponent ); varaddNotification:INotification = new Notification( BaseNotifications.SLOT_VIEW_COMPONENT_ADD, desc, viewComponentSlot); facade.notifyObservers(addNotification); if(desc.viewComponent != null) // егобыобнулилиеслибыонбылнайден { addNotificationListener(BaseNotifications.SLOT_CREATION_COMPLETE, handleSlotCreationCompleteNotificationToAddViewComponent); } }