Robotlegs : un framework ActionScript qui vaut le détour

Publié le Mis à jour le

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
     [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);
    // ...
    
    // ...
    }
}
 

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
public class SampleActor extends Actor
{
    [Inject]
        public var service : Twitter;
    
    public function PerformSearchCommand()
    {
        super();
    
    }
}

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
     public class Twitter extends Actor
{
    public function Twitter()
    {
        super();


        public function searchFor(keyword : String) : void
        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));
        
        protected function searchCompleteHandler(e : Event) : void
        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);
        
            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
     public class SearchFormMediator extends Mediator
{
    [Inject]
    public var form : SearchForm;

    public function SearchFormMediator()
    {
        super();

        override public function onRegister() : void
        form.addEventListener(FormEvent.SUBMIT, loginFormSubmitHandler);
    
        override public function onRemove() : void
        form.removeEventListener(FormEvent.SUBMIT, loginFormSubmitHandler);
    
        protected function loginFormSubmitHandler(e : FormEvent) : void
        dispatch(new SearchEvent(SearchEvent.SEARCH, form.keyword.text));

    }
}

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
public class PerformSearchCommand extends Command
{
    Inject]
        public var dispatched : SearchEvent;
    
    [Inject]
    public var service : Twitter;

    public function PerformSearchCommand()
    {
        super();
    
        override public function execute() : void
    service.searchFor(dispatched.keyword);
    
    }
}

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
public class RobotlegsTwitterContext extends Context
{
    public function RobotlegsTwitterContext(contextView:DisplayObjectContainer=null, autoStartup_Boolean=true)
         {
            super(contextView, autoStartup);
         

            override public function startup() : void
            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();
        
        }
}

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.


  • Développement

  • Flash

  • Framework

  • Logiciels libres / Open Source