Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Enumérations - Type-safe

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Compatible ActionScript 2. Cliquer pour en savoir plus sur les compatibilités.Par ekameleon (Marc Alcaraz), le 05 décembre 2006

Généralités

Les énumérations sont de plus en plus utilisées par les développeurs pour réduire les redondances de leur code et garantir le maximum de sécurité.

En ActionScript il n'existe pas de vraie implémentation précise à ce sujet. Il existe donc plusieurs recettes de cuisines pour les utiliser et je vais essayer de vous montre ma façon d'utiliser les énumérations. Ma technique s'inspire largement de tout ce que j'ai pu observer au sein du framework AS3, dans des frameworks comme Pixlib et surtout de tout ce que j'ai pu mettre en place dans VEGAS au fur et à mesure de son évolution.

1 - Exemple simple d'énumération en utilisant des types primitifs :

class ApplicationColor
{
 
 
    public function getColor( color:Number ):Number
    {
        switch (color)
        {
 
            case 0xD97F00 : // abricot
            case 0xFFFFFF : // blanc
            case 0xD70079 : // bonbon
            case 0xAE293B : // cerise
            case 0xFF6633 : // corail
 
                return color ;
 
            default :
 
                return 0x000000 ;
        }
 
    }
 
}

Dans la méthode getColor() ci dessus nous pouvons constater tout d'abord que la lecture des couleurs n'est pas forcément des plus simples (à moins d'avoir un panneau mélangeur câblé en permanence dans votre esprit), il est assez difficile de définir précisément les couleurs représentées dans la condition switch..case.

De plus si l'on doit réutiliser ces couleurs plus loin dans le code, il va falloir reprendre à chaque fois toutes les primitives… Si au cours de votre développement une couleur change il va falloir mettre en place un refactoring plus ou moins long pour remettre à jour l'ensemble de l'application.

A noter que j'ai tout de même mi en place quelques petits commentaires pour rendre un peu plus facile la lecture du code mais la lisibilité s'arrête là, il va devenir très lourd de commenter à chaque fois le code.

2 - Exemple un peu plus évolué avec des constantes

class ApplicationColor
{
 
 
    // ----o Constant Summary
 
    static public var ABRICOT :Number = 0xD97F00 ;
    static public var BLANC   :Number = 0xFFFFFF ;
    static public var BONBON  :Number = 0xD70079 ;
    static public var CERISE  :Number = 0xAE293B ;
    static public var CORAIL  :Number = 0xFF6633 ;
    static public var NOIR    :Number = 0x000000 ;
 
    static public function getColor( color:Number ):Number
    {
        switch (color)
        {
 
            case ABRICOT  :
            case BLANC    :
            case BONBON   : 
            case CERISE   :
            case CORAIL   :
 
                return color ;
 
            default :
 
                return NOIR ;
        }
 
    }
 
}

Le code au dessus est déjà bien plus clair. Il n'est plus nécessaire de commenter celui ci car le nom des énumérations est assez explicite. De plus il est assez simple de modifier une couleur sans avoir à retoucher par la suite l'intégralité du code, il suffit d'utiliser ces constantes dans tout le code et si il faut modifier une couleur il suffit de redéfinir la valeur d'une des constantes.

Cette solution est bien plus élégante que la précédente et reste simple et rapide.

3 - "Typesafe" Enumeration.

Nous allons maintenant pousser l'exemple précédent en intégrant des énumérations typées avec une classe spécifique pour nos couleurs.

class ApplicationColor extends Number
{
 
 
    /**
     * Creates a new ApplicationColor
     */
    public function ApplicationColor( value:Number )
    {
       super(value) ;
    }
 
    // ----o Constant Summary
 
    static public var ABRICOT :ApplicationColor = new ApplicationColor(0xD97F00) ;
    static public var BLANC   :ApplicationColor = new ApplicationColor(0xFFFFFF) ;
    static public var BONBON  :ApplicationColor = new ApplicationColor(0xD70079) ;
    static public var CERISE  :ApplicationColor = new ApplicationColor(0xAE293B) ;
    static public var CORAIL  :ApplicationColor = new ApplicationColor(0xFF6633) ;
    static public var NOIR    :ApplicationColor = new ApplicationColor(0x000000) ;
 
    // ----o Public Method Summary
 
    static public function getColor( color:ApplicationColor ):Number
    {
        switch (color)
        {
 
            case ABRICOT  :
            case BLANC    :
            case BONBON   : 
            case CERISE   :
            case CORAIL   :
 
                return color.valueOf() ;
 
            default :
 
                return NOIR.valueOf() ;
        }
 
    }
 
}

L'exemple au dessus est un peu plus complexe que l'exemple précédent mais il permet cette fois ci une meilleure sécurité des objets utilisés.

En effet, les énumérations sont maintenant typées avec la classe ApplicationColor qui hérite de la classe Number et cela assure que l'utilisateur pourra puiser dans les couleurs de l'application sans se tromper. Il sera par exemple impossible de passer dans la méthode getColor() un autre objet qu'une instance de la classe ApplicationColor. Les énumérations avec des types primitifs (exemple 2) sont rapides à mettre en place mais manquent cruellement de sécurité, ici ce problème est résolu.

Il est maintenant possible d'appliquer un design pattern intéressant que l'on appelle le “Type-safe Enum”. Ce pattern permet de sécuriser les énumérations en utilisant une fonction constructeur privée. Il est donc impossible de déclarer des nouvelles instances de la classe en dehors de celle ci. De plus en utilisant une classe pour typer les énumérations il est possible de leur ajouter une implémentation avec des interfaces ou des fonctionnalités via quelques méthodes plus facilement que sur un type primitif. Voyons de plus prêt une version finalisée de notre classe ApplicationColor avec un exemple en AS2 :

class ApplicationColor extends Number
{
 
    /**
     * Creates a new ApplicationColor
     */
    private function ApplicationColor( value:Number, name:String )
    {
       super(value) ;
       this.name = name ;
    }
 
    // ----o Constantes
 
    static public var ABRICOT :ApplicationColor = new ApplicationColor(0xD97F00, "abricot") ;
    static public var BLANC   :ApplicationColor = new ApplicationColor(0xFFFFFF, "blanc") ;
    static public var BONBON  :ApplicationColor = new ApplicationColor(0xD70079, "bonbon") ;
    static public var CERISE  :ApplicationColor = new ApplicationColor(0xAE293B, "cerise") ;
    static public var CORAIL  :ApplicationColor = new ApplicationColor(0xFF6633, "corail") ;
    static public var NOIR    :ApplicationColor = new ApplicationColor(0x000000, "noir") ;
 
    static private var __ASPF__ = _global.ASSetPropFlags(ApplicationColor, null, 7, 7) ;
 
    // ----o Public Properties
 
    public var name:String = null ;
 
    // ----o public methods
 
    static public function getColor( color:ApplicationColor ):Number
    {
        switch (color)
        {
 
            case ABRICOT  :
            case BLANC    :
            case BONBON   : 
            case CERISE   :
            case CORAIL   :
 
                return color.valueOf() ;
 
            default :
 
                return NOIR.valueOf() ;
        }
 
    }
 
    public function toString():String
    {
       return this.name ;
    }
 
}

Points positifs de ce pattern.

- Le constructeur est privé et il est impossible de créer de nouvelles énumérations en dehors de la classe.

- L'utilisation du ASSetPropFlags permet de transformer les propriétés statiques en constantes, il est impossible de supprimer ou de modifier les énumérations.

- J'écrase la méthode toString() de la classe pour renvoyer le nom de l'instance et le valueOf() renvoi la valeur numérique de l'instance ce qui permet de combiner à la fois des manipulations classiques avec les opérateurs sur ces énumérations mais également de renvoyer dans un panneau de sortie des messages plus explicites (pour ma part je préfère lire dans mon panneau de sortie le mot “abricot” que la valeur 14253824 qui lui correspond en base 10). Points négatifs de ce pattern :

Le point qui peut sembler négatif est à mon avis que cette technique alourdie un peu le poids de vos applications et prend un peu plus de temps pour être mise en place qu'une simple variable primitive. Il est certain que lorsque l'on commence à utiliser ce genre de technique on perd en “vitesse”. Ces techniques s'utilisent dans des cadres précis et impliquent une bonne réflection sur l'architecture de son code.

Remarques

1 - L'utilisation des constantes écrites comme ci-dessus uniquement avec des lettres en majuscules restent une nomenclature très utilisée dans la communauté des développeurs ActionScript, ECMAScript ou même JAVA etc…

2 - En AS3 il n'est pas possible d'utiliser de constructeur privé (il existe beaucoup de discussion à ce sujet d'ailleurs avec la notion de pattern Sigleton par exemple) mais il est possible d'utiliser des exceptions pour sécuriser le constructeur de la fonction. Il est également possible d'utiliser un simple singleton “objet”. (Faudra voir si Adobe va remettre le constructeur en private ou pas dans les prochaines mises à jours du framework AS3 à la sortie de Flash9 ou de Apollo par exemple ? Je pense que tout dépendra des décisions prises avec la version finale de l'ECMASCript version 4.)

Ressources

- Un bon article sur le sujet sur le blog de Darron Schall : Enumerated Types in ActionScript (en anglais) avec un lien vers les mots clés réservés pour la future version 4 de l'ECMAscript, on observe donc bien que le mot clé enum peut apparaitre dans les prochaines version de l'ActionScript.

Par ALCARAZ Marc aka EKAMELEON (2006). Vous pouvez consulter ce tutorial et des commentaires sur mon blog.