2015-10-09 9 views
6

Как часть игровой части RPG, я хочу иметь возможность применить временные эффекты к персонажам. Характер этих эффектов может варьироваться довольно много, но я хочу, чтобы метод их определения был очень простым.Хорошая практика: как я могу обеспечить, чтобы конструктор JavaScript имел доступ к функциям mixin?

Я использую пользовательские обработки как Mixin событие:

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

Я хочу, чтобы определить Ауры (временные эффекты) в качестве конструктора с кодом обработки события:

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

Затем применяется как MyCharacter.addAura(new MyAura(any, args));. Как вы можете видеть, я хочу, чтобы функция this.on() была доступна в конструкторе. Если я продлю прототип MyAura с mixin ($rpg.Event.enable(MyAura.prototype)), то каждый экземпляр MyAura ссылается на тот же объект _events от прототипа.

Я хочу знать, если следующее решение является хорошей практикой:

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

Вы можете также изменить mixin так, чтобы он лениво инициировал элемент '_events' на объекте (когда вызывается' on'). Это подход, используемый [asEvented] (https://github.com/mkuklis/asEvented/blob/master/asevented.js). – plalx

+0

Как насчет композиционного подхода к миксинам? 'Aura = eventMixin.mixInto (Aura);' где это применяется mixin и возвращает новый сложенный конструктор. Единственное, что с этим связано, это приложение mixin не может быть отложено, так как на данный момент никто не должен ссылаться на старый конструктор. – plalx

+0

Спасибо за ваши комментарии. Я думал о создании экземпляров событий, как вы описали, я не совсем уверен, что оттолкнуло меня от этой идеи, так что это может быть то, что я использую в конце концов. Не могли бы вы уточнить, что вы подразумеваете под «композиционным»? – Traverse

ответ

1

Есть символы (экземпляры), действующие в этой игре. Иногда возникает потребность в обогащении их дополнительным поведением или применением новых ролей, например ауры, для некоторых из них.

Чистый подход уже использует traits/mixins в этот момент ...

Если я продлить прототип MyAura с Mixin $rpg.Event.enable(MyAura.prototype)

... как это происходит с обеспеченными например Ор в.

Поскольку JavaScript на это уровень ядра не предусматривает mixin mechanics бесплатно на основе шаблона в Function и делегации ...

Я хочу, чтобы определить Ауры (временные эффекты) в качестве конструктора с событием код обработки:

... Aura «s реализация должна быть переключена от его конструктор подход к так называемой Flight Mixin подход ...

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*/); 

В любой момент в игре, обогащая игроков навыка или поведенческий набор с Aura, будет столь же просто, как вызов делегации в одной строке ...

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

Удаление такого поведения является другой задачей и может обсуждаться впоследствии/отдельно.

+0

Мне очень нравится, где это происходит, поэтому спасибо за ваше время! Конечно, я чувствую, что работаю над лучшим кодом. Однако я не уверен, как реализовать этот шаблон для создания различных аур. Вещь, выскакивающая для меня, заключается в том, что, если я не ошибаюсь, события включаются и применяются к классу Player, а не к объекту Aura. – Traverse

+0

@Traverse - ответ будет слишком длинным. Для этого требуется собственный блок ответа. –

0

Я не уверен, как реализовать этот шаблон для создания различных аур.

Один Hase, чтобы принять решение, либо проектировании Aura constructor-/завод на основе и не отрезая прототипичные делегации, чтобы быть в состоянии идти позже для более вариаций ауры использования наследования или разложения полнофункциональный Aura в различные смесины, каждый из которых имеет четко определенный набор мелкозернистого поведения .

базировался Экземпляр аура подход наиболее Propably усложняется, когда он опирается на переадресацию, если один будет «добавить» экземпляр ауры (что в данном примере способен случае диспетчерских) на характер ... MyCharacter.addAura(new MyAura(any, args)) ... Метод конструктора игрока должен быть реализован способом , который уже учитывает, что аура сама по себе включает в себя методы, связанные с событиями. Таким образом, этот код должен придумать собственное решение для переадресации, чтобы включить игрока, отправляющего события на время проведения ауры.

Следующая дается сводились но работает пример (ы) может указывать на то, что только что было сказано ...

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);}); 

Для всех вышеперечисленных, я лично всегда предпочитаю функцию, основанную на методе смешивания микширования .

Дело выпрыгнув мне, что, если я не неправильно, события быть включен на и применяется к классу игрока, а не сам объект Aura.

Если следовать подходу дело только с плеера/символа экземпляров, тогда как все остальное, что можно рассматривать как набор дополнительных поведения, которые игрок может приобрести или может избавиться от (или которые могут быть награждены или может быть снят с любого игрока) во время выполнения, затем для последнего, говорится о ролевой композиции. Таким образом, и Observable, и Aura являются миксинами.

Не будет такой вещи, как объект ауры.

Если игрок уже не имеет наблюдаемое поведения, но поведения ауры действительно полагается на него, то Aura Mixin должен использовать прежний. Тогда фабрика игрока не имеет дело с Observable, но объект игрока будет иметь поведение Observable , как только на него будет применен миксин Aura.

... и теперь возвращаться снова ...

Я не уверен, как я бы реализовать этот шаблон, чтобы создать различную ауру.

Придерживаясь функциональных миксинов, в основном используются два решения. Во-первых, как и в примере, который я привел в своем первом ответе, можно использовать для подошву, но настраиваемый Mixin. Или, во-вторых, кто-то предпочитает уже упомянутые мно- гие фрагменты ауры.

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

Player завод код из выше, то сократится до ...

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);}); 

Подсказка: " meta "mixins также может быть легко cons созданных из меньших. Таким образом, создание композиций поведенческих наборов.

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); 
}; 

Примечания: На практике я в основном в конечном итоге с настраиваемым Mixins, в то время как выше приведен пример составления Примеси из другого Mixins, кажется, редкий случай.

0

Спасибо за все комментарии по этому вопросу. Я узнал много через последующее чтение и решил на лучшее решение для того, что я хочу сделать - на основе подхода «Полет Mixin» разместил 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; 

     } 

    }; 

}; 

Который при использовании, выглядит как:

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

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