IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Programmation orientée objet par prototype avec jQuery


précédentsommairesuivant

XIII. $.dvjhClass

$.dvjhClass est l'outil qui permet d'écrire du code poopj. C'est un objet JSON comprenant trois méthodes : _create(), _base() et toString(). L'outil permet de réunir dans un même corpus la fonction d'initialisation et son prototype.

(1) $.dvjhClass simplifie l'usage de la méthode d'héritage simple décrite par Thierry Templier au chapitre 1.4 de la deuxième partie de son tutoriel : "Programmation orientée objet avec le langage JavaScript".

Cette technique d'héritage ne permet pas le polymorphisme (objX instanceof objY), mais c'est la plus appropriée dans la plupart des cas. Si le polymorphisme et l'héritage multiple vous manquent, le tutoriel de Thierry Templier décrit d'autres techniques d'héritage.

La notion de classe n'existe pas en JavaScript, mais le besoin de qualifier la chose existant bien, je désigne le code généré par $.dvjhClass._create() sous le vocable "fonction classe poopj".

J'ai testé plusieurs ajouts à $.dvjhClass, mais je les ai tous abandonnés, ils n'apportaient rien de plus au système POOPJ et ils pesaient lourd dans l'occupation mémoire des objets instanciés.

Formaliser la notion d'interface, de propriété, d'accesseurs et de mutateurs en n'ayant pas de support pour ces notions dans les mécanismes du langage n'apporte qu'une satisfaction intellectuelle encombrante.

Tel qu'il est, je crois que l'objet $.dvjhClass est le meilleur compromis entre les services rendus et l'encombrement mémoire dû à son utilisation.

$.dvjhClass (lib/dvjh/poopj.js)
Sélectionnez
$.dvjhClass = {
  _create: function(){
    var classMere = null;
    var corpus = null;
    var options = {
        _abstract: false,
        _auteur: "Auteur : Daniel Hagnoul " + 
        "(https://www.developpez.net/forums/u285162/danielhagnoul/)",
        _copyright: "Creative Commons License : " + 
    "Attribution-Share Alike 2.0 Belgium " + 
    "(https://creativecommons.org/licenses/by-sa/2.0/be/legalcode.fr)",
        _version: "$.dvjhClass, Daniel Hagnoul, v1.0.0 2010-08-16"
    }
    
    // si le premier argument est une fonction
    if ($.isFunction(arguments[0])){
        classMere = arguments[0];
        
        // il doit y avoir un objet comme deuxième argument
        if ($.isPlainObject(arguments[1])){
            corpus = arguments[1];
        } else {
            throw(dvjhExceptions.create1);
        }
        
        // il peut y avoir un objet comme troisième argument
        if ($.isPlainObject(arguments[2])){
            $.extend(options, arguments[2]);
            
            if (typeof options._abstract != "boolean"){
                options._abstract = true;
            }
            
        } else if (arguments[2]){
            throw(dvjhExceptions.create2);
        }
    } else if ($.isPlainObject(arguments[0])){
        // le premier argument est un objet
        corpus = arguments[0];
        
        // il peut y avoir un objet comme second argument
        if ($.isPlainObject(arguments[1])){
            $.extend(options, arguments[1]);
            
            if (typeof options._abstract != "boolean"){
                options._abstract = true;
            }
            
        } else if (arguments[1]){
            throw(dvjhExceptions.create3);
        }
    } else {
        // le premier argument n'est pas un objet
        throw(dvjhExceptions.create4);
    }
    
    if (!corpus._builder){
        throw(corpus + dvjhExceptions.corpus1);
    }
    
    if (classMere && !classMere.prototype._builder){
        throw(classMere + dvjhExceptions.classMere1);
    }
    
    // fonction à partir de laquelle le nouvel objet est instancié
    function build(){
        try {
            /*
             * Dans le cas d'une classe abstraite
             * ( _abstract == true) on choisit de retourner
             * un objet vide (new Object) plutôt que de
             * déclencher une exception, car dans ce cas
             * l'opérateur new construit un objet invalide.
             */
            if (this._options._abstract){
                return {};
            }
            
            this._builder.apply(this, arguments);
        }
        catch(err){
            alert(err);
        }
    }
    
    if (classMere){
        // incorporer classMere en premier
        $.extend(build.prototype, classMere.prototype);
        
        // conserver un accès aux méthodes originales de classMere
        build.prototype._super = {};
        
        for (var item in classMere.prototype){
            if (item.slice(0,1) != "_"){
                build.prototype._super[item] = 
                                    classMere.prototype[item];
            }
        }
    }
    
    // incorporer l'objet $.dvjhClass et le corpus
    $.extend(build.prototype, this, corpus);
    
    // incorporer les options
    build.prototype._options = options;
    
    // retourne la fonction à partir de laquelle 
    // le nouvel objet est instancié
    return build;
  },
  _base: function(doNotTouchMe_YouIdiot, classMere, args){
    /*
     * doNotTouchMe_YouIdiot est arguments.callee
     *
     * arguments.callee est l'équivalent de this pour une 
     * fonction anonyme
     *
     * il permet l'initialisation des classes mères par un 
     * appel récursif
    */
    if (!classMere.prototype._builder){
        throw(classMere + dvjhExceptions.classMere1);
    }
    
    // initialisation de la classe mère
    classMere.prototype._builder.apply(this, args);
  },
  toString: function(){
    return this._options._version;
  }
};

précédentsommairesuivant