Il peut être trés pratique, voire parfois indispensable pour obtenir un code "clean", de ne pouvoir passer une valeur à une méthode, qui doit obligatoirement être une des valeurs "possibles"... C'est ce que l'on appelle des énumérations. Java gère cela nativement grâce à la classe Enum.

De même, avoir la possibilité de créer des constantes est également bien utile. Dans la pratique, il s'agit en fait de créer une propriété statique en lecture seule. Le moyen le plus sûr de faire cela serait de créer une variable statique privée, et un getter statique public.

Voici ce que cela pourrait donner dans le cadre d'une classe d'encryptage:
class Crypter {
        // Private static members
        private static var _NONE:String = "none";
        private static var _MD5:String = "md5";
        private static var _SHA1:String = "sha1";
        // Public constants
        public static function get NONE():String {
                return _NONE;
        }
        public static function get MD5():String {
                return _MD5;
        }
        public static function get SHA1():String {
                return _SHA1;
        }
        private var _cryptMode:String;
        function Crypter(mode:String) {
                _cryptMode = mode;
        }
        function encrypt(s:String):String {
                // Case use private vars, to avoid decreasing
                // performances by using a getter
                switch(_cryptMode) {
                        case _NONE:
                                return s;
                        case _MD5:
                                var md5crypted:String = "";
                                // Perform MD5 cryptage
                                return md5crypted;
                        case _SHA1:
                                var sha1crypted:String = "";
                                // Perform SHA1 cryptage
                                return sha1crypted;
                }
        }
}
// Usage
var myCrypter:Crypter = new Crypter(Crypter.MD5);
trace(myCrypter.encrypt("String to be encrypted"));
Bien que tout cela soit quand-même assez clean, cette implémentation pose quelques problèmes:

  • Tout accés aux constantes (de l'exterieur) fait appel à un getter, et donc les performances peuvent s'en ressentir, selon la fréquence de ces accés
  • Il est possible de passer au constructeur une chaine de caractères qui ne fasse pas partie des valeurs attendues en entrée... Cela peut se résoudre avec un default: dans le case, mais ce n'est pas vraiment une solution satisfaisante...
Dans les premières betas de la librairie de petepx, pixLib, j'avais noté l'utilisation trés élégante qu'avait fait Francis de l'héritage de String pour le typage de ses types d'évenements (classe EventType si je ne m'abuse). A priori, il s'agissait surtout, dans ce cas, de polymorphisme avec EventDispatcher de Macromedia, mais j'ai alors étendu le concept pour pouvoir créer mes énumérations, dans une classe externe.// Enum
class CryptMode
        extends String {
        public static var NONE:CryptMode = new CryptMode("none");
        public static var MD5:CryptMode = new CryptMode("md5");
        public static var SHA1:CryptMode = new CryptMode("sha1");
        private function CryptMode(content:String) {
                super(content);
        }
}
class Crypter {
        private var _cryptMode:CryptMode;
        function Crypter(mode:CryptMode) {
                _cryptMode = mode;
        }
        function encrypt(s:String):String {
                // Case use private vars, to avoid decreasing
                // performances by using a getter
                switch(_cryptMode) {
                        case CryptMode.NONE:
                                return s;
                        case CryptMode.MD5:
                                var md5crypted:String = "";
                                // Perform MD5
                                return md5crypted;
                        case CryptMode.SHA1:
                                var sha1crypted:String = "";
                                // Perform SHA1
                                return sha1crypted;
                }
        }
}
// Usage
var myCrypter:Crypter = new Crypter(CryptMode.MD5);
trace(myCrypter.encrypt("String to be encrypted"));
Cette méthode à tous les avantages:
  • Impossible de rajouter des constantes non-prévues au runtime, du fait du constructeur privé.
  • Toujours grâce au constructeur privé, impossible de modifier la valeur d'une constante, bien qu'il s'agisse d'une propriété publique.
  • Impossible de passer une valeur non prévue à une méthode, si l'argument est typé avec l'énumération.
  • Une compatibilité avec les types de bases. Bien que l'exemple se base sur un héritage de String, il est bien sûr possible de l'utiliser sur n'importe quel type "de base" (Number par exemple) comme sur des types plus complexes. Le '==' continue de fonctionner par exemple :trace(CryptMode.MD5 == "md5"); // true
  • Petit bémol, le switch...case semble utiliser une comparaison stricte (===), donc ne pas oublier de faire un toString() ou valueOf() dans ce cas... ;)
  • Remplace presque parfaitement le fait de ne pas pouvoir créer de constantes dans les interfaces, offrant ainsi la possibilité de disposer d'une série de valeurs dans la création d'une API "pure" (sans aucune implémentation).

Bref, je suis assez content d'avoir trouvé ce moyen de rendre mes codes un peu plus clean. J'utilise dorénavant énormément cette astuce, notamment concernant la diffusion d'évenements, en créant une classe collection contenant les évenements diffusables par une classe définie, puis en utilisant le typage dans la signature de mes addEventListener (en parlant de ça, bientôt un tool de diffusion d'évenements maison).

Wala, wala! ^^