Best practices pour FLEX PureMVC

Préambule

Version logicielle

Pour cet article, les versions utilisées sont les suivantes

* Flex 3
* ActionScript 3
* PureMVC 2.0.1

A qui s’adresse ce document ?

Ce document s’adresse en priorité aux personnes ayant des connaissances en Flex et en Action Script et qui ont une vision du travail qu’effectue PureMVC. Il nécessite donc de bien comprendre les notions de classes Action Script 3 et d’avoir un vue d’ensemble de la réalisation d’une application PureMVC.

Que contient ce document ?

Ce document contient un ensemble de bonnes pratiques à appliquer sur le projet. Il décrit le fonctionnement rapide et l’intérêt des concepts de pure MVC, ainsi que les structures de fichiers des projects et les séquences de démarrage d’une application pure MVC couplée à Flex.

Principe de fonctionnement de PureMVC

Ce paragraphe a pour objectif de faire un rapide tour d’horizon des notions qui entoure PureMVC.

L’application Façade

L’application facade est, dans PureMVC, l’interlocuteur privilégié pour tous les “acteurs” du projet. C’est elle qui va démarrer le système PureMVC, en initialisant les différentes commandes et envoyant les messages de démarrage. C’est aussi à elle que l’on va s’adresser pour récupérer une instance sur un proxy, ou pour enregistrer un proxy.

Règles de nommage : La facade étant unique dans une application, on la nommera toujours ApplicationFacade.

Les notifications

Les principes de base dans PureMVC résident dans les notifications, qui permettent le dialogue entre les différentes couches de l’application. Une notification est capable de transporter un objet qui sera utilisé par l’intercepteur pour réaliser son travail.

Règles de création: La constante représentant une notification est toujours écrite dans le proxy qui est en charge de la notification. Dans le cas où aucun proxy n’intervient alors on devra faire en sorte que la constante se trouve dans la vue qui doit l’envoyer.

Règles de nommage : Les notifications seront suffixées par “_NOTIFICATION”. Cela permet en particulier de les distinguer des événements.

Les Médiators

Au sein de PureMVC, l’objectif des médiators est de faire l’interface entre la partie vue et la partie les couches sous-jacentes de l’application PureMVC. Dans un premier temps, il se charge simplement de recevoir les évènements en provenant de la vue et de créer les notifications qui correspond. Dans un deuxième temps, il reçoit les événements de la couche model et met à jour la vue en conséquence.

Règle de créations : On crée en général un médiator par composant qui envoie plusieurs évènements, afin de ne pas multiplier les médiators effectuant des actions qui pourraient être regroupées. Ainsi, si on a un panel avec 3 boutons et une liste, il sera conseillé de créer un médiator pour le panel qui réceptionnera les informations du panel et les informations des boutons et un médiator pour la liste qui prendra en charge les événements en provenance de la liste s’ils sont nombreux.

Règles de nommage
: Le nom d’un médiator doit être composé sous la forme “nomdelavueconcernée”+”Mediator”

Les Commands

Les commandes sont des classes qui vont être enregistrées sur les notifications qui transitent dans PureMVC. L’objectif de ces classes est d’effectuer des actions complexes pour le compte de la vue. La question qui se pose est de savoir pourquoi on n’utilise pas directement les médiators pour effectuer les actions sur le modèle. Sans l’utilisation des commandes, le médiator va contenir l’ensemble des actions à effectuer et de ce fait toute réorganisation de la vue va entrainer une réorganisation du code contenu dans les médiators. Par ailleurs si on souhaite effectuer la même commande sur une autre notification on va devoir dupliquer le code.

Règles de création : D’une manière générale on créera des commandes pour toutes interactions avec les modèles via les proxy, à partir du moment où l’on veut faire autre chose qu’une consultation. D’une manière plus spécifique, on créera une commande lorsque l’action qui devra être réalisée est complexe et lourde ou lorsque l’on souhaite connecter la même action sur plusieurs notifications.

Règles de nommage : Le nom d’une commande doit forcément se terminer par le mot clé “Command”.

Les Proxy

L’objectif des proxy est de venir masquer l’interaction avec les modèles métier et les services. Il offre un ensemble de méthode et prend en charge ensuite l’appel aux services, la modification de données etc… Le principal avantage du proxy est de permettre la modification de l’appel à un service sans avoir à impacter l’ensemble du code nécessitant ce service. Il masque l’utilisation des services et de ce fait permet la réalisation de services fictifs répondant à un besoin mais qui n’effectue pas d’action sur le modèle. Cette technique permet de travailler sur un interface en flex sans que l’appel réel aux services soit réalisé.

Règle de création : On créera un service par objet qui va transiter vers les services via par exemple AMF PHP.

Les Values Objects

Un value object est un conteneur d’information, qui permet de faire transiter des données entre les différents couches de l’application. Ces objets sont une représentation d’un groupe de données qui transitent au sein de l’application. On utilise principalement les values objects au moment de la présentation des données en les “bindant” aux champs du formulaire flex. Ceci permet ensuite la récupération d’un objet dont les données sont automatiquement complétées par les mécanismes Flex.

Règle de création : On créera un value object pour représenter un élément complexe de l’application et lorsque l’on aura besoin de faire transiter un groupe d’informations via les notifications. Par exemple si on dispose d’un ensemble de formulaire éditant tout ou une partie des propriétés d’un objet métier, on va donc créer un Value Object qui rassemblera l’ensemble des propriétés des différents formulaire afin de centraliser l’information.

Structure du projet

Le répertoire ressources

Le répertoire ressources contient l’ensemble des ressources qui vont être embarquées dans l’application avec la spécification embed. Il contient généralement les images et surtout les fichiers de locales qui vont être chargés lors du démarrage de l’application.

Le répertoire src

Ce répertoire contient les sources de l’application et est structuré en trois packages.

* Le package controller

On regroupera dans ce package l’ensemble des commandes de l’application.

* Le package view : c’est dans ce package seront regroupé l’ensemble des médiators mais aussi l’ensemble des composants Mxml de l’application. Il est important de suivre la règle de création suivante pour les sous packages : un sous package par groupe de médiators et composants. Tous médiators qui concernent un composant particulier seront groupés.

* Le package model: on retrouvera dans ce package l’ensemble des proxy ainsi que les values objets de l’application. Les value objets sont placés ici pour éviter les dépendances montant entre les proxy et les deux couches supérieures contenant les commandes et les vues.

Séquence de démarrage d’une application Flex 3 couplée à PureMVC

Dans le cadre d’un modèle MVC standard, il est généralement préférable de faire démarrer l’application par le biais d’un controller qui va initialiser l’ensemble des intervenants de l’application. Cette stratégie n’est pas applicable dans le cas de Flex, puisqu’il existe une application par défaut dans flex. L’application par défaut (fichier Mxml) qui va être appelée par le runtime de Flex au démarrage de l’application. On se retrouve donc dans un position inversée : La vue démarre l’application, sans être au préalable assurée de la disponibilité des autres intervenants.

application_startupExemple de démarrage d’un application PureMVC

Pour palier à ce problème il faut donc faire en sorte que la vue démarre l’ensemble de l’application de manière correcte afin d’éviter d’éventuels problèmes d’accès à des objets non existant. Pour ce faire, il est nécessaire d’utiliser l’application facade, qui elle va prendre en charge le démarrage de l’application. On suivra donc les étapes suivantes :

1. Création d’un médiator pour la vue application ApplicationMediator. Ce médiator va prendre en charge les évènements et le comportement de l’Application par défaut flex.
2. Créer une macro commande ApplicationCommand qui va prendre en charge le démarrage de l’application.
3. Rajouter dans l’initialisation de l’applicationCommand, deux sous commandes : ModelPreparationCommand et ViewPreparationCommand.
4. Le corps de la commande de préparation du model “ModelPreparationCommand” enregistre l’ensemble des proxys nécessaire à l’application.
5. Le corps de la commande de préparation de la vue “ViewPreparationCommand” enregistre le médiator de l’application (Application Mediator) et lui demande d’afficher la première vue (généralement, on effectue un changement d’index sur une stack).
6. L’application mxml récupère une instance de l’application facade et le onCreationComplete execute la fonction startup de l’applicationFacade.
7. A son initialisation l’applicationFacade enregistre la commande ApplicationCommande sur la notification Startup puis la fonction startup de l’applicationFacade envoie la notification de démarrage de l’application :

override protected function initializeController():void
{
 //initialise les controllers.
 super.initializeController();
 //register the application on the startup notification.
 this.registerCommand(STARTUP_NOTIFICATION,ApplicationStartupCommand);            
}
public function startup(flexApplication:ArchectypeApplication ):void
{
 this.sendNotification(STARTUP_NOTIFICATION, flexApplication);
}

Gestion de évènements et des notifications

Utilisation des values objects

Comme nous l’avons vue précédemment, il est important de disposer de value object pour pouvoir faire transiter l’information correctement entre les différentes couches. Un fois la classe value object définie, on va créer un instance dans la vue et la rentre public et bindable de la manière suivante:

[Bindable] public var categoryVO:CategoryVO = new CategoryVO();

L’objectif est ici que l’on puisse récupérer l’object par le médiator pour l’envoyer dans la notification.
Création et remontée des évènements dans la vues

L’objectif dans l’utilisation de PureMVC est que la vue Flex (fichier MXML) ne connaisse pas du tout son médiateur, afin que l’on puisse changer à volonter le médiateur gérant les évènements. Pour pouvoir effectuer ce découplage, la stratégie consiste faire remonter les évènements vers les éléments à l’écoute du composant.

Pour cela on crée un ensemble de constantes qui vont définir les évènements :

/**
* Type d’évènement envoyé lors du clique sur le bouton de suppression.
*/
public static const DELETE_EVENT:String = “DELETE_EVENT”;
/**
* Type d’évènement envoyé lorsque le composant à été crée complètement
*/
public static const CREATION_COMPLETE_EVENT:String = “CREATION_COMPLETE_EVENT”;

On créera ensuite un fonction pour renvoyer les évènements vers les listeners :

/**
* Méthode qui intercepte les évenments sur les boutons.
* @param type le type d’évènement qui est interceptés.
*/
public function handleButtonEvent(type:String):void
{
//Dispatch les évènements vers les listeners
dispatchEvent(new Event(type));
}

Et on branchera ensuite cette fonction sur les différents évènements comme par exemple sur un bouton :

<mx:Button x=”317″ y=”71″ width=”100″ label=”Supprimer” click=”handleButtonEvent(DELETE_EVENT)” id=”deleteButton”/>

Une des pratiques importantes est de documenter ces évènements grâce au méta-data :

<mx:Metadata>
/**
* Evènement déclenché lorsque la création du composant est terminée.
*/
[Event("CREATION_COMPLETE_EVENT")]

/**
* Evènement déclenché lors de la mise à jour de la sélection sur la liste.
*/
[Event("SELECTION_CHANGE_EVENT")]
</mx:Metadata>

On spécifie ici que la vue en question va envoyer un évènement SELECTION_CHANGE_EVENT et un évènement CREATION_COMPLETE_EVENT et les commentaires apparaitront dans la documentation générée grâce à asdoc.

Réception et traitement des évènements dans les médiators

Dans le paragraphe précédent nous avons vu que les vues remontaient des évènements suites à des actions de l’utilisateur. Sachant que le médiator d’une vue est la pour faire le dialogue avec les couches inférieures, on l’enregistrera de la manière suivante sur les évènements de la vue :

categoryScreen.addEventListener(CategoryScreen.DELETE_EVENT, onEvent);
categoryScreen.addEventListener(CategoryScreen.CREATION_COMPLETE_EVENT, onEvent);

Dans ce code, categoryScreen est le composant mxml sur lequel sont enregistrés le médiator et onEvent est la fonction qui va traiter l’évènement. Dans la fonction onEvent, on va faire en sorte d’envoyer une notification, qui sera interceptée par la couche commande pour effectuer le travail désiré:

public function onEvent(event:Event):void
{
[...]
switch (event.type)
{
[...]
case CategoryScreen.DELETE_EVENT:
//envoie d’une notification contenant l’élément à supprimer
category = this.categoryScreen.categoriesList.selectedItem as CategoryVO;
facade.sendNotification(CategoryProxy.CATEGORY_DELETE_NOTIFICATION, category);
break;
}
}

Il est à noté que l’on évitera de faire un succession d’accès par l’opérateur “.” aux éléments fils d’une vue. L’exemple présenté ici est en partie un contre exemple. Ce qu’il est important de noté est la récupération du value object.

Séquence de propagation des évènements

La séquence de propagation des évènements est la suivante

1. L’utilisateur clique sur un bouton ce qui déclenche un évènement
2. L’évènement est intercepté par la vue et dispatcher aux listeners
3. Le médiateur qui est à l’écoute sur l’évènement, est alerté et envoie un notification
4. La commande reçoit la notification et fait appel au proxy pour exécuter les appels
5. Si l’appel se déroule bien alors le proxy envoie une notification de mise à jour
1. le médiator intercepte la notification
2. le médiator fait appel au proxy récupérer les valeurs.
3. le médiator met à jour la vue avec les données du proxy.
6. Si l’appel ne se déroule pas bien le proxy ou la commande envoie un notification de problème
1. le médiator intercepte la notification de problème.
2. le médiator affiche le message d’erreur.

Stratégie d’enregistrement des médiateurs

Un médiator est toujours enregistré sur un vue existante. Cependant, en raison de l’initialisation à la demande des composants en Flex, on se retrouve régulièrement dans le cas d’un appel à une instance d’un object qui n’existe pas.
L’évènement onCreationComplete

Cet évènement est déclenché lorsque tout les composants ont été initialisé. Cependant dans le cas d’un accordéon qui contient deux canvas, eux même contenant deux boutons, l’évènement onCreationComplete apparait lorsque les deux canvas on été initialisé, mais les boutons du deuxième canvas n’ont pas été crée. On recoit donc un évènement lorsque les deux éléments fils ont été crée.

onCreationComplete with flex

Création des médiateurs

Pour palier aux problèmes évoqués précédemment, il est nécessaire de faire en sorte que la création des médiators fils se fasse de la manière suivante :

mediator_registering_strategyExemple de séquence de création de médiateurs

1. On part du postula que le médiator du composant parent ainsi que le composant parent ont été créés
2. Le mediator du composant parent s’enregistre sur l’évènement creation complete du composant parent
3. L’évènement CreationComplete du composant parent est déclenché, on sait alors que tout les composants fils de premier niveau ont été créés.
4. Le médiator du composant parent crée alors les médiators pour les composants fils et les enregistre sur les composants fils.
5. Les médiators du composants fils s’initialisent et s’enregistrent sur les évènements des composants concernés.

Résumé des règles

1. Utiliser des values objects pour le transport des informations
2. Utiliser la méthode “DispatchEvent” pour la gestion des évènements sur les vues
3. Utiliser le mediator comme simple intermédiaire entre les vues et les couches inférieures.
4. Préferer l’utilisation des commandes au placement de la logique métier dans la vue

Partager cet article
  • Print this article!
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • E-mail this story to a friend!
  • Live
  • Netvibes
  • Simpy
  • Technorati
  • Twitter
  • Wikio FR
  • Yahoo! Bookmarks
  • Yahoo! Buzz
  • Yigg
  • Scoopeo
  • LinkedIn
  • MSN Reporter

No related posts.

One Comment

  1. mski
    Posted September 21, 2010 at 5:50 pm | Permalink

    Salut,

    Le sujet date un peu peut être mais il m’a beaucoup aidé.

    MERCI !!!

Post a Comment

Your email is never shared. Required fields are marked *

*
*

This site uses a Hackadelic PlugIn, Hackadelic SEO Table Of Contents 1.6.0.