La recherche et l’éventuelle adoption d’un framework est une quête quasi systématique dans la vie d’un développeur et ce, peu importe le langage. Cet article présente un framework qui mérite que l’on s’y intéresse pour son excellent ratio puissance/légèreté : Robotlegs. L’article est agrémenté d’exemples issus d’un outil de recherche sur Twitter développé pour l’occasion, et disponible sur GitHub.
Présentation
Robotlegs est un framework MVC+S très simple d’utilisation, qui va permettre de cadrer le développement d’application Flash (ou Flex) en utilisant massivement l’injection de dépendance (IoC). Si la signification de MVC est évidente, le S signifie « Service », désignant les classes de votre application qui forment la couche métier (appel de webservices, etc.). Le détail qui fait plaisir, le projet est open source et vous pourrez le récupérer sur github.
Contrairement à des frameworks tels que Cairngorn ou PureMVC qui relèvent plus de l’usine à gaz que du véritable outil, Robotlegs ne cherchera pas à faire le café pour vous : l’ensemble de la logique métier de votre application sera à votre charge. (Vous noterez les pics lancés à Cairngorn et PureMVC, il s’agît d’un troll parfaitement assumé. C’est Vendredi.)
Pour comprendre cet article, la maitrise des bases d’ActionScript 3 sera nécessaire, principalement sur sa syntaxe orientée objet et la diffusion d’évènements au sein d’une application.
MVC et Robotlegs
Une réponse argumentée à la question « pourquoi opter le Design-Pattern MVC ? » risque de prêcher bon nombre de convertis. Il s’agît simplement, dans le cas de développement d’application Flash, du meilleur compromis pour assurer la maintenabilité de l’application. L’avantage d’un langage comme ActionScript est qu’il permet d’organiser son code en éléments bien distincts, qui vont communiquer entre eux à coup d’évènements. Robotlegs vous permet de cadrer ces éléments.
Structure d’un projet Robotlegs
Un projet faisant appel au framework MVC Robotlegs s’organise autour des différentes classes fournies :
– Context : le contexte est une classe ActionScript qui va hériter de la classe « Context », elle représente « le point d’entrée » d’une application et va mettre en place l’ensemble des dépendances entre les composants, évènements et commandes.
– Commands : une commande est une simple action effectuée par l’application
– Mediators : Les classes héritants de « Mediator » vont servir d’intermédiaire entre une vue et le reste de l’application. Elles permettent de s’assurer qu’aucune logique métier ne se retrouve dans une vue, parce que c’est mal.
– Models / Services : Les classes de modèle et de service héritent de la classe « Actor » et vont respectivement servir à stocker l’état de l’application et à communiquer avec la couche métier.
L’ensemble de ces éléments vont communiquer avec ce qu’ActionScript fait de mieux : les événements.
L’application développée
Pour l’exemple, l’application développée utilisera l’API de Twitter pour remonter des résultats de recherche. Rien de bien méchant ou de bien novateur, mais un concept simpliste sera toujours ça de moins à assimiler au fil de cet article.
Mise en place du projet
Nous allons créer un projet Actionscript avec le logiciel FlashBuilder qui, malgré une totale instabilité et une finition plus que médiocre (ça, c’est fait) reste un bon compromis pour le développement Flash.
Une fois le projet créé, nous allons lui spécifier d’ajouter à la compilation le binaire de Robotlegs qu’il est possible de récupérer sur le site officiel, ou sur github.
Création du contexte
La communication entre les composants d’une application Flash utilisant Robotlegs repose sur une classe de contexte, ici appelée « RobotlegsTwitterContext ». Cette classe est la première qui sera instanciée par l’application. Son constructeur prend en paramètre la « contextView », c’est à dire la vue principale de l’application. C’est lors de l’appel de la méthode « startup » que la magie opère, puisque le but va être de lier les vues avec les « mediators » qui vont bien, les commandes avec les « events » correspondant, et les classes qui seront accessibles dans l’ensemble de l’application grâce au pattern de Singleton [[Certains d’entres vous partisans de la cause anti-Singleton pour des raisons de maintenabilité et de testabilité ont probablement dû sauter au plafond en lisant le mot Singleton. Rassurez-vous, le framework Robotlegs est un framework fonctionnant par IoC, les injections sont simplement automatiquement gérées par le Framework.]].
package
<em>[SWF(width="600", height="600")]
public class RobotlegsTwitterDemo extends Sprite
{
protected var context : RobotlegsTwitterContext ;
protected var loader : Sprite ;
public function RobotlegsTwitterDemo()
{
context = new RobotlegsTwitterContext(this) ;
// ...
</em>
// ...
}
}
L’injection de dépendance selon Robotlegs
L’automatisation de l’injection des dépendances fonctionne via l’utilisation de « metatags » très courants dans le développement flash/flex (Embed, RemoteClasse, Bindable…). Dans le cas de robotlegs, c’est un « metatag » précis qui est utilisé pour annoter les propriétés de classes : Inject.
Par exemple, dans le cas d’une classe « Actor » ayant besoin d’avoir accès à une instance de la classe de service (décrite plus bas), voici là manière de procéder :
package com.cleverage.robotlegstwitter.commands
<em>public class SampleActor extends Actor
{
[Inject]
public var service : Twitter ;
public function PerformSearchCommand()
{
super() ;
</em>
}
}
Cette injection de dépendance fonctionne pour les classes de « mediator », les classes de services, les évènements, et doit être mise en place au niveau de la classe de contexte, comme expliqué dans la dernière partie de cet article.
Création des évènements
Le modèle évènementiel proposé par ActionScript 3 est au cœur de l’application. En effet, c’est par le biais des évènements que les composants indépendants de l’application vont communiquer. Pour prendre un exemple typique, un formulaire de recherche va diffuser un évènement signalant que les informations entrées par l’utilisateur sont prêtes à être utilisées. Son « mediator » va récupérer cet évènement, et en diffuser un autre, permettant par exemple de lancer une commande qui s’occupe d’appeler le service en charge de la communication avec la couche métier de l’application. Une fois cette sauvegarde effectuée, le service va diffuser un évènement signifiant que la sauvegarde s’est déroulée avec succès. Le « mediator » évoqué précédemment va intercepter cet évènement, et signifier à la vue d’afficher un message de confirmation.
Dans cette application, nous allons retrouver plusieurs évènements, au nom relativement explicite :
– FormEvent
– SearchEvent
– SearchResultEvent
– TweetEvent
– TweetListEvent
Création de la classe de service
Cette classe fait partie des classes requises à plusieurs endroits de l’application. Elle s’occupe de d’appeler la méthode de recherche de l’API Twitter, de récupérer les résultats au format ATOM, de les parser et enfin de les diffuser au sein de l’application.
package com.cleverage.robotlegstwitter.services
<em>public class Twitter extends Actor
{
public function Twitter()
{
super() ;
</em>
public function searchFor(keyword : String) : void
<em>var loader : URLLoader = new URLLoader() ;
loader.addEventListener(Event.COMPLETE, searchCompleteHandler) ;
var target : URLRequest = new URLRequest("http://search.twitter.com/search.atom") ;
target.method = URLRequestMethod.POST ;
target.data = new URLVariables() ;
target.data.q = keyword ;
loader.load(target) ;
dispatch(new SearchEvent(SearchEvent.START)) ;
</em>
protected function searchCompleteHandler(e : Event) : void
<em>var results : XML = new XML(e.target.data as String) ;
var tweet : Tweet ;
var tweets : Array = [] ;
var atom : Namespace = new Namespace("http://www.w3.org/2005/Atom") ;
for each(var node : XML in results..atom ::entry)
{
tweet = new Tweet() ;
tweet.fromAtomEntry(node, atom) ;
tweets.push(tweet) ;
</em>
dispatch(new SearchResultEvent(SearchResultEvent.RESULTS, tweets)) ;
}
}
}
Cette classe va diffuser via la méthode dispatch()
(et non dispatchEvent()
!) un évènement pour signaler que la recherche a retourné des résultats. En aucun cas, elle ne va se soucier de la mise à jour de la vue ou d’un quelconque autre composant de l’application.
Création des vues et des « médiateurs »
L’une des vues de l’application sera un formulaire de recherche composé d’un champ recevant la requête, et d’un bouton soumettant cette requête. Cette classe hérite de Sprite
et n’a rien à voir avec Robotlegs pour le moment. Pour communiquer avec le reste de l’application, nous allons passer par ce que l’on appelle « Mediator », un terme dont la traduction littérale, « médiateur » explique très bien le rôle : il va jouer l’intermédiaire entre la vue et le reste de l’application. Chaque composant a son propre médiateur, la classe SearchFormMediator
est le médiateur du formulaire de recherche. Qui a dit que le développement Flash avec Robotlegs était compliqué ?
La déclaration d’un médiateur implique de surcharger deux méthodes de la classe parente : onRegister()
et onRemove()
. Ces méthodes sont automatiquement appelées par Robotlegs lorsque les vues associées sont respectivement ajoutées et supprimées de l’application (on parle d’ajout à la scène, en aucun cas de la destruction de l’instance). Enfin, ce médiateur aura accès à la vue, toujours par injection de dépendance, grâce au « metatag » [Inject].
package com.cleverage.robotlegstwitter.views
<em>public class SearchFormMediator extends Mediator
{
[Inject]
public var form : SearchForm ;
public function SearchFormMediator()
{
super() ;
</em>
override public function onRegister() : void
<em>form.addEventListener(FormEvent.SUBMIT, loginFormSubmitHandler) ;
</em>
override public function onRemove() : void
<em>form.removeEventListener(FormEvent.SUBMIT, loginFormSubmitHandler) ;
</em>
protected function loginFormSubmitHandler(e : FormEvent) : void
<em>dispatch(new SearchEvent(SearchEvent.SEARCH, form.keyword.text)) ;
</em>
}
}
Dans le cas du formulaire de recherche, celui-ci va diffuser un évènement déclarant que la valeur enregistrée dans le champs de recherche est valide. Cet évènement va être récupéré par le médiateur, qui a son tour va diffuser un évènement déclenchant le processus de recherche.
Création des commandes
Les commandes sont des classes appelées soit manuellement, soit lors de la diffusion d’un évènement spécifique. Elles sont généralement très courtes, et se limitent à appeler une méthode d’une classe de service spécifique, etc. La déclaration d’une commande implique de surcharger la méthode execute()
et, au même titre que l’ensemble des composants d’une application utilisant Robotlegs, une classe de commande peut utiliser l’injection de dépendance. Dans le cas où la commande est appelée par un évènement, elle peut recevoir l’instance de cet évènement et ainsi avoir accès aux éventuelles informations ajoutées à la diffusion.
Dans le cas de la recherche, une commande « PerformSearchCommand » s’occupe de déclencher la recherche en appelant la méthode search()
de la classe de service « Twitter » :
package com.cleverage.robotlegstwitter.commands
<em>public class PerformSearchCommand extends Command
{
<a href="http://github.com/cleverage/Robotlegs-Twitter-Demo">Inject]
public var dispatched : SearchEvent ;
[Inject]
public var service : Twitter ;
public function PerformSearchCommand()
{
super() ;
</em>
override public function execute() : void
<em>service.searchFor(dispatched.keyword) ;
</em>
}
}
Mise en place des dépendances
Maintenant que ces acteurs sont en place, il s’agît de lier l’ensemble des composants pour faire fonctionner l’application, on va parler de « mapping » des composants : lier un évènement à une commande pour déclencher son exécution à la diffusion (via la propriété « commandMap »), lier une vue à un médiateur (via la propriété « mediatorMap ») ou encore définir une classe en tant que « Singleton » (via la propriété « injector »). Tout ceci s’effectue en surchargeant la méthode startup()
de la classe de de contexte.
package com.cleverage.robotlegstwitter
<em>public class RobotlegsTwitterContext extends Context
{
public function RobotlegsTwitterContext(contextView:DisplayObjectContainer=null, autoStartup:Boolean=true)
{
super(contextView, autoStartup) ;
</em>
override public function startup() : void
<em>commandMap.mapEvent(SearchEvent.SEARCH, PerformSearchCommand, SearchEvent) ;
commandMap.mapEvent(SearchResultEvent.RESULTS, UpdateTweetListModelCommand, SearchResultEvent) ;
mediatorMap.mapView(RobotlegsTwitterDemo, ContextViewMediator) ;
mediatorMap.mapView(SearchForm, SearchFormMediator) ;
mediatorMap.mapView(TweetScreen, TweetScreenMediator) ;
injector.mapSingleton(TweetList) ;
injector.mapSingleton(Twitter) ;
super.startup() ;
</em>
}
}
Conclusion
Il existe beaucoup de frameworks Actionscript facilitant l’implémentation du design pattern MVC au sein d’une application Flash ou Flex. Robotlegs fait partie de ces outils faciles d’accès, très simple à mettre en place, et sur lesquels il est possible de compter pour organiser les composants d’une application complexe.
Vous trouverez sur [Github l’application développée (puisqu’ici, seules quelques portions ont été affichée) permettant d’effectuer une recherche sur Twitter et de naviguer à travers les résultats.