Capitalizando a herança prototípica do JavaScript

7

O JavaScript possui um sistema de objetos sem classe no qual os objetos herdam propriedades diretamente de outros objetos. Isso é realmente poderoso, mas não é familiar para programadores treinados classicamente. Se você tentar aplicar padrões clássicos de design diretamente ao JavaScript, ficará frustrado. Mas se você aprender a trabalhar com a natureza prototípica do JavaScript, seus esforços serão recompensados.
...
É Lisp na roupa de C.

- Douglas Crockford

O que isso significa para um desenvolvedor de jogos que trabalha com canvas e HTML5? Eu estive examinando essa questão sobre padrões de design úteis nos jogos, mas a herança prototípica é muito diferente da herança clássica, e certamente existem diferenças na melhor maneira de aplicar alguns desses padrões comuns.

Por exemplo, a herança clássica nos permite criar uma moveableEntityclasse, e estender isso com as classes que se movem em nosso mundo de jogo ( player, monster, bullet, etc.). Claro, você pode ativar o JavaScript para funcionar dessa maneira, mas ao fazê-lo, você está meio que lutando contra sua natureza. Existe uma abordagem melhor para esse tipo de problema quando temos herança prototípica na ponta dos dedos?

keithjgrant
fonte

Respostas:

8

Para obter um desempenho decente do JavaScript com os modernos mecanismos JS, é necessário escrever um código que reflita os padrões estruturais comuns de digitação ou de digitação de pato. A digitação estrutural é mais poderosa do que fornecer tipos apenas por herança, mas na verdade não altera os padrões de design usados ​​pelos jogos.

Por exemplo, em C ++ ou Java, onde geralmente se digita por herança, geralmente há hierarquias de tipos profundos com várias interfaces / classes virtuais para implementar o MVC. Mas os jogos já se esquivam desse tipo de padrão de design porque realmente não ajuda a modelar o tipo de dados que os jogos lidam.

Os padrões que consideramos mais úteis no design de jogos, como objetos de contexto, componentes, peso de mosca ou estratégia, não tendem a usar hierarquias de herança complicadas em primeiro lugar, mesmo quando implementadas em linguagens como C ++. Os componentes envolvem uma única classe virtual (no máximo). O Flyweight geralmente usa a digitação estrutural (estranha) do C ++ por meio de modelos. A estratégia geralmente usa algum tipo de apagamento de tipo para desempenho ou sanidade do programador.

A única exceção a isso é o próprio padrão de protótipo, que geralmente é encontrado de alguma forma nos jogos, especialmente para dados orientados a designers. O uso de JavaScript torna esse padrão trivial. Mas não é um padrão particularmente difícil de implementar, pelo menos mal, em qualquer linguagem que suporte estruturas de dados associativas. O uso do JS permitiria que você o usasse mais, pois é possível aproveitar o otimizador do mecanismo subjacente. Mas não tenho certeza se isso realmente mudaria a maneira como se programa - um padrão de protótipo ingênuo já está em muitos lugares em muitos jogos, apesar de seu desempenho geralmente fraco.


fonte
3
Recebi um voto positivo menos de um minuto depois de postar isso, que me recuso a acreditar que é tempo suficiente para lê-lo e decidir se é uma boa resposta ou não. Vi várias indicações de que algumas pessoas simplesmente votam em respostas longas e escritas sem uma gramática horrível. Por favor, não faça isso.
Essa resposta específica soa verdadeira para qualquer desenvolvedor experiente. Eu também votei quase imediatamente, porque quando eu leio, fico tipo "Sim, sim, sempre funciona assim".
Nevermind
bem dito! Eu sempre senti que JS é muito bem adaptado para o tipo de dados orientado e abordagens de componentes que são tão comuns em jogos, mas não conseguia articular me :)
oberhamsi
6

Se você realmente se interessar pela semântica, verá que o JavaScript é realmente um pouco mais próximo da maioria das linguagens baseadas em classes do que uma linguagem prototípica "real" como a Self. Da maneira que newfunciona, você quase sempre cria um objeto que contém estado em seus próprios campos e métodos nos campos de seu protótipo. Aperte os olhos e isso é uma aula.

Para ser realmente protótipo, não haveria new. Você está clonando objetos e fazendo herança diferencial.

Portanto, não se preocupe muito com a natureza semi-prototípica do JavaScript. Apenas entenda três partes:

  1. Na maioria das vezes, seus objetos serão essencialmente instâncias de uma "classe" em que o construtor define seus campos e as funções nos construtores prototypedefinem o método.

  2. Se você quiser uma herança única familiar ao estilo Java / C #, poderá obtê-la encadeando protótipos. A maioria das estruturas JS existentes (Closure, dojo etc.) possui alguns métodos auxiliares interessantes para conectá-la.

  3. Você pode adicionar métodos a qualquer "classe" a qualquer momento. Sinta-se à vontade para misturar funções nas quais os protótipos precisam deles. Você nem sempre precisa configurar uma cadeia de herança e uma hierarquia complexa. Se você tiver dez classes que precisam do mesmo método, sempre poderá adicioná-lo imperativamente a essas dez classes.

maravilhoso
fonte
Eu gosto da terceira maneira de fazer as coisas. Por exemplo, você pode ter uma função "danificável" que adiciona um atributo de integridade e um método damage (). Você pode digitar seu outro código com base na presença de dados / métodos reais, em vez dos valores do tipo sentinela "isDamageable".
drhayes
11
Exatamente certo. Existem várias estruturas JS que fornecem "mixins" que fazem praticamente isso: adicionar várias funções a um determinado protótipo.
191811 munificent