2015-10-09 10 views
6

Come parte di un gioco di ruolo RPG, voglio essere in grado di applicare effetti temporanei ai personaggi. La natura di questi effetti potrebbe variare parecchio, ma voglio mantenere il metodo per definirli molto semplice.Buona pratica: come posso garantire che un costruttore JavaScript abbia accesso alle funzioni di mixaggio?

sto usando la manipolazione come mixin evento personalizzato:

var EvtObject = {}; 
$rpg.Event.enable(EvtObject); // Add the 3 methods and set EvtObject._events = {} 

voglio definire Aure (gli effetti temporanei) come costruttore con il codice di gestione degli eventi:

var MyAura = function(any, args){ 
    this.group = "classification"; 
    this.on("tick", function(){}); 
    this.on("remove", function(){}); 
}; 

poi applicato come MyCharacter.addAura(new MyAura(any, args));. Come puoi vedere, voglio che la funzione this.on() sia disponibile nel costruttore. Se estendo il prototipo MyAura con il mixin ($rpg.Event.enable(MyAura.prototype)), ogni istanza di MyAura fa riferimento allo stesso oggetto _events dal prototipo.

voglio sapere se la seguente soluzione è buona norma:

Aura.create = function(Constructor) 
{ 
    Constructor.prototype = Aura.getPrototype(); // Aura specific prototype 

    return function() 
    { 
     var newAura = Object.create(Constructor.prototype); 
     $rpg.Event.enable(newAura); 
     Constructor.apply(newAura, arguments); 
     return newAura; 
    }; 

}; 

// Then creating new Auras: 
var MyAura = $rpg.Aura.create(function(any, args){ 
    // this.on() is available 
}; 
+0

Si può anche cambiare il mixin in modo che installi pigramente il membro '_events' sull'oggetto (quando viene chiamato' on' per esempio). Questo è l'approccio adottato da [asEvented] (https://github.com/mkuklis/asEvented/blob/master/asevented.js). – plalx

+0

Che dire di un approccio compositivo ai mixin? 'Aura = eventMixin.mixInto (Aura);' dove questo applica il mixin e restituisce un nuovo costruttore composto. L'unica cosa è che l'applicazione di mixin non può essere differita poiché nessuno dovrebbe fare riferimento al vecchio costruttore a questo punto. – plalx

+0

Grazie per i vostri commenti. Ho pensato di istanziare _eventi come hai descritto, non sono abbastanza sicuro di cosa mi abbia allontanato da quell'idea, quindi potrebbe essere quello che uso alla fine. Potresti elaborare ciò che intendi per "compositivo"? – Traverse

risposta

1

ci sono personaggi (casi) che agisce in questo gioco. Occasionalmente è necessario arricchirli con il comportamento aggiuntivo o applicare nuovi ruoli, come un'aura, ad alcuni di essi.

Un approccio pulita utilizza già traits/mixins a questo punto ...

Se estendo il prototipo MyAura con mixin $rpg.Event.enable(MyAura.prototype)

... come succede con l'esempio fornito del PO.

Poiché JavaScript al suo livello di base non prevede mixin mechanics gratuitamente da un modello basato Function e delegazione, ...

voglio definire Aure (gli effetti temporanei) come costruttore con l'evento che gestisce il codice:

... un'implementazione Aura s' devono passare da esso è approccio costruttore verso il cosiddetto Flight Mixin approccio ...

var withAura = (function (Event) { // mixin module implementation . 

    var 
    tickHandler = function() {}, 
    removeHandler = function() {}, 

    Mixin = function withAura (config) { // not a constructor but a function based "flight mixin". 
     var aura = this; 

     Event.enable(aura); 

     aura.group = config.group; 

     aura.on("tick", tickHandler);  // referencing ... 
     aura.on("remove", removeHandler); // ... shared code. 
    } 
    ; 
    return Mixin; 

}($rpg.Event)); 


var Player = (function() { // factory module implementation. 

    var 
    // ... 
    // ... 
    createPlayer = function (config) { // factory. 
     var player = {}; 
     // ... 
     // ... 
     return player; 
    } 
    ; 

    return { 
    create: createPlayer 
    }; 

}()); 


var somePlayer = Player.create(/*config*/); 

In qualsiasi punto all'interno del gioco, arricchendo un'abilità giocatori o set comportamentale con una Aura, sarà semplice come una chiamata delegazione di una linea ...

withAura.call(somePlayer, {group: "classification"}); 

Rimuovere questo comportamento è un altro compito e può essere discusso in seguito/separatamente.

+0

Mi piace davvero dove sta andando, quindi grazie per il tuo tempo! Sicuramente sento che sto lavorando per un codice migliore. Tuttavia non sono sicuro di come implementare questo modello per creare diverse aure. La cosa che mi salta fuori è che, a meno che non abbia letto male, gli eventi vengono abilitati e applicati alla classe Player piuttosto che all'oggetto Aura stesso. – Traverse

+0

@Traverse - La risposta sarà troppo lunga. Richiede un proprio blocco di risposta. –

0

Non sono sicuro di come implementare questo modello per creare diverse aure.

Una Hase per prendere una decisione, sia la progettazione di un Aura constructor-/fabbrica a base di e non tagliare delegazione prototipale al fine di essere in grado di andare più tardi per più variazioni aura facendo uso di eredità, o di decomposizione di un a tutti gli effetti Aura in vari mix, ognuno con un insieme ben definito di comportamento a grana fine.

L'approccio basato esempio aura più propably diventa complicato in cui si basa su inoltro se uno sta per "aggiungere" un'istanza aura (che nell'esempio dato è in grado di evento dispacciamento) su un personaggio ... MyCharacter.addAura(new MyAura(any, args)) ... Il metodo addAura del costruttore di un giocatore deve essere implementato in un modo che tiene già conto che l'aura di per sé presenta i metodi di destinazione dell'evento. Quindi questo codice deve venire con una propria soluzione per l'inoltro al fine di abilitare un giocatore di dispiegamento di eventi per il tempo che porta un'aura lungo.

La prossima data bollito giù, ma ad esempio (s) di lavoro potrebbe puntare a quello che è appena stato detto ...

var Player = (function() { // sort of factory module implementation. 

    var 
    // ... 
    // ... 

    addAura = function (player, aura) { // simplified forwarding or delegation. 
     // ... 
     player.on = function (type, handler) { 

     aura.on(type, handler);    // forwarding. 

     // please try switching it to delegation ... 

     //aura.on.apply(player, arguments);  // delegation. 
     //aura.on.call(player, type, handler); // delegation. 

     console.log("player.on :: player.getName() ", player.getName()); 
     }; 
     // ... 
    }, 


    Constructor = function Player (name) { 
     // ... 
     // ... 
     this.getName = function() { 
      return name; 
     }; 
     //this.addAura = function (aura) { 
     // addAura(this, aura) 
     //}; 
    }, 


    createPlayer = function (config) { // factory. 
     var 
     // ... 
     //player = (new Constructor) 
     player = (new Constructor(config.name)) 
     // ... 
     ; 
    //player.name = config.name; 

     player.addAura = function (aura) { 
     addAura(player, aura) 
     }; 

     return player; 
    } 
    ; 

    return { 
    create: createPlayer 
    }; 

}()); 

...

var   // simplified aura object - for demonstration only. 
    aura = { 
    on: function (type, handler) { 
     if (typeof handler == "function") { 
     handler.call(this); 
     } 
     console.log("aura :: on - [this, arguments] ", this, arguments); 
    } 
    }, 
    p1 = Player.create({name:"p1"}), // 1st character/player. 
    p2 = Player.create({name:"p2"}) // 2nd character/player. 
; 

...

p1.addAura(aura); 
p2.addAura(aura); 

p1.on("tick", function() {console.log("tick :: this", this);}); 
p2.on("remove", function() {console.log("remove :: this", this);}); 

Per tutto quanto sopra, personalmente preferisco sempre la funzione basata sull'approccio alla composizione mix .

La cosa che salta fuori per me è che, a meno che non ho letto male, gli eventi sono in corso abilitato su e applicato alla classe Player piuttosto che l'oggetto in sé Aura.

Se si segue il metodo di fare unicamente con lettore/personaggio casi, mentre qualsiasi altra cosa che può essere visto come insieme di ulteriori comportamenti che un giocatore può acquisire o può sbarazzarsi di (o che può essere assegnato a o può essere ritirato da qualsiasi giocatore) in fase di esecuzione, quindi per quest'ultimo, si parla di composizione basata sui ruoli. Quindi sia Observable e Aura sono mixin.

Non ci sarà nulla come un oggetto aura.

Se un giocatore non è già dispongono di comportamento osservabile, ma un comportamento aura fa affidamento su di essa, quindi il mixin Aura dovrebbe fare uso del primo. La factory di un giocatore quindi non ha a che fare con Observable ma un oggetto giocatore conterrà il comportamento Observable non appena viene applicato un mixin Aura.

... e ora tornare di nuovo a ...

io non sono certo come vorrei implementare questo modello per creare l'aura variabili.

Attaccandosi ai mixin basati su funzioni ci sono ancora principalmente due soluzioni. In primo luogo, come nell'esempio che ho fornito nella mia prima risposta, si può andare a un Mixin unico ma configurabile. Oppure, in secondo luogo, si preferisce il già menzionato molti frammenti di mix di aura.

var $rpg = { // mocking and faking heavily. 

    Event: { 
    enable: function (observable) { 

     observable.on = function (type, handler) { 
     if (typeof handler == "function") { 
      handler.call(this); 
     } 
     console.log("aura :: on - [this, arguments] ", this, arguments); 
     }; 

     return observable; 
    } 
    } 
}; 

...

var withAuraBase = function AuraBaseMixin (group) { // not a constructor but a simple function based "flight mixin". 
    var auraEnabled = this; 

    // ... 
    $rpg.Event.enable(auraEnabled); 
    // ... 

    auraEnabled.group = group; 
    // ... 
}; 
var withFencinessFactorAura = function FencinessFactorAuraMixin (fencinessFactor) { 
    // ... 
    this.fencinessFactor = fencinessFactor; 
    // ... 
}; 
var withCheatingFeaturesAura = function CheatingFeaturesAuraMixin (config) { 
    // ... 
    this.cheatingFeatures = config; 
    // ... 
}; 

Il codice Player fabbrica dall'alto poi si ridurrà a ...

var Player = (function() { // sort of factory module implementation. 

    var 
    // ... 
    // ... 
    Constructor = function Player (name) { 
     // ... 
     // ... 
     this.getName = function() { 
     return name; 
     }; 
    }, 

    createPlayer = function (config) { // factory. 
     var 
     // ... 
     player = (new Constructor(config.name)) 
     // ... 
     ; 
    //player.name = config.name; 

     return player; 
    } 
    ; 

    return { 
    create: createPlayer 
    }; 

}()); 

...

var 
    p1 = Player.create({name:"player1"}), // 1st character/player. 
    p2 = Player.create({name:"player2"}) // 2nd character/player. 
; 

// applying mixin examples differently. 

withAuraBase.call(p1, "classification"); 
withAuraBase.call(p2, "classification"); 

withFencinessFactorAura.call(p1, 10); 
withCheatingFeaturesAura.call(p2, {/*cheatingFeaturesConfig*/}); 


console.log("p1.getName(), p1", p1.getName(), p1); 
console.log("p2.getName(), p2", p2.getName(), p2); 

p1.on("tick", function() {console.log("tick :: this", this);}); 
p2.on("remove", function() {console.log("remove :: this", this);}); 

Suggerimento: " meta "anche i mixin possono essere facilmente considerati guidato da quelli più piccoli. Abilitando così le composizioni di insiemi comportamentali su misura.

var withFencyAura = function FencyAurMixin (group, fencinessFactor) { 
    var auraEnabled = this; 

    withAuraBase.call(auraEnabled, group); 
    withFencinessFactorAura.call(auraEnabled, fencinessFactor); 
}; 
var withCheatingAura = function CheatingAuraMixin (group, config) { 
    var auraEnabled = this; 

    withAuraBase.call(auraEnabled, group); 
    withCheatingFeaturesAura.call(auraEnabled, config); 
}; 

Nota: In pratica Principalmente finire con mixins configurabili, mentre il sopra proposta esempio di composizione mixins da altri mixin sembra essere un caso raro.

0

Grazie per tutti i commenti su questa domanda. Ho imparato molto attraverso la lettura di follow-up e hanno deciso la soluzione migliore per quello che voglio fare - sulla base dell'approccio "Flight Mixin" Pubblicato da plalx

Mixin.use = function(){ 

    var i, ApplyMixins = arguments; 

    return function(constructor) 
    { 

     if(typeof constructor !== "function") 
     { 

      for(var i = 0; i < ApplyMixins.length; i++) 
      { 

       if(ApplyMixins[i].init && typeof ApplyMixins[i].init == "function") 
       { 
        ApplyMixins[i].init.call(constructor); 
       } 

       copyMembersTo(constructor, ApplyMixins[i].functions || {}); 

      } 

      return constructor; 

     } 
     else 
     { 

      var init = [], 
       newPrototype = constructor.prototype; 

      var newConstructor = function() 
      { 

       var self = this; 

       init.forEach(function(thisInit){ 
        thisInit.call(self, null); 
       }); 

       constructor.apply(this, arguments); 

      }; 


      Object.setPrototypeOf(newConstructor, constructor); 
      Object.defineProperty(newConstructor, "name", { value: constructor.name }); 

      newConstructor.prototype = newPrototype; 
      newPrototype.constructor = newConstructor; 

      for(var i = 0; i < ApplyMixins.length; i++) 
      { 

       if(ApplyMixins[i].init && typeof ApplyMixins[i].init == "function") 
       { 
        init.push(ApplyMixins[i].init); 
       } 

       copyMembersTo(newPrototype, ApplyMixins[i].functions || {}); 

      } 

      return newConstructor; 

     } 

    }; 

}; 

che quando viene utilizzato, si presenta come:

var MyConstruct = function(){}; 
MyConstruct = Mixin.use(mixinA, mixinB)(MyConstruct); 

// Or all in one go: 
var MyConstruct = Mixin.use(my, mix, ins)(function(){ 
    // Constructor 
}); 
Problemi correlati