Quais são as vantagens do OOP baseado em protótipo sobre o OOP baseado em classe?


47

Quando comecei a programar o Javascript depois de lidar principalmente com OOP no contexto de linguagens baseadas em classes, fiquei confuso sobre o motivo pelo qual a OOP baseada em protótipo seria preferida à OOP baseada em classe.

  1. Quais são as vantagens estruturais do uso de OOP baseado em protótipo, se houver? (por exemplo, esperamos que seja mais rápido ou menos intensivo em memória em certos aplicativos?)
  2. Quais são as vantagens da perspectiva de um codificador? (por exemplo, é mais fácil codificar certos aplicativos ou estender o código de outras pessoas usando a criação de protótipos?)

Por favor, não olhe para esta pergunta como uma pergunta sobre Javascript em particular (que teve muitas falhas ao longo dos anos que não têm nenhuma relação com a prototipagem). Em vez disso, observe-o no contexto das vantagens teóricas da prototipagem versus classes.

Obrigado.


Respostas:


46

Eu tinha muita experiência nas duas abordagens ao escrever um jogo de RPG em Java. Originalmente, escrevi o jogo inteiro usando OOP baseado em classe, mas finalmente percebi que essa era a abordagem errada (estava se tornando impossível de manter à medida que a hierarquia de classes se expandia). Portanto, converti toda a base de código em código baseado em protótipo. O resultado foi muito melhor e mais fácil de gerenciar.

Código-fonte aqui se você estiver interessado ( Tyrant - Java Roguelike )

Aqui estão os principais benefícios:

  • É trivial criar novas "classes" - basta copiar o protótipo e alterar algumas propriedades e pronto ... nova classe. Eu usei isso para definir um novo tipo de poção, por exemplo, em 3-6 linhas de Java cada. Muito melhor do que um novo arquivo de classe e um monte de clichê!
  • É possível criar e manter um número extremamente grande de "classes" com relativamente pouco código - o Tyrant, por exemplo, tinha algo como 3000 protótipos diferentes, com apenas cerca de 42.000 linhas de código total. Isso é incrível para Java!
  • A herança múltipla é fácil - basta copiar um subconjunto das propriedades de um protótipo e colá-las sobre as propriedades em outro protótipo. Em um RPG, por exemplo, você pode querer que um "golem de aço" tenha algumas das propriedades de um "objeto de aço" e algumas das propriedades de um "golem" e algumas das propriedades de um "monstro não inteligente". Fácil com protótipos, tente fazer isso com uma hierarquia de herança ......
  • Você pode fazer coisas inteligentes com modificadores de propriedade - colocando uma lógica inteligente no método genérico de "propriedade de leitura", pode implementar vários modificadores. Por exemplo, era fácil definir um anel mágico que adicionava +2 de força a quem o usava. A lógica para isso estava no objeto ring, não no método "força de leitura"; portanto, você evitou fazer muitos testes condicionais em outros lugares da sua base de código (por exemplo, "o personagem está usando um aumento de força de anel?")
  • Instâncias podem se tornar modelos para outras instâncias - por exemplo, se você deseja "clonar" um objeto, é fácil, basta usar o objeto existente como protótipo para o novo objeto. Não há necessidade de escrever muita lógica de clonagem complexa para diferentes classes.
  • É muito fácil alterar o comportamento no tempo de execução - ou seja, você pode alterar uma propriedade e "transformar" um objeto de maneira arbitrária no tempo de execução. Permite efeitos interessantes no jogo, e se você associar isso a uma "linguagem de script", praticamente tudo será possível em tempo de execução.
  • É mais adequado a um estilo de programação "funcional" - você costuma escrever muitas funções que analisam objetos e agem de maneira apropriada, em vez de lógica incorporada em métodos anexados a classes específicas. Eu pessoalmente prefiro esse estilo de FP.

Aqui estão as principais desvantagens:

  • Você perde as garantias da digitação estática - pois está criando efetivamente um sistema de objetos dinâmicos. Isso tende a significar que você precisa escrever mais testes para garantir que o comportamento esteja correto e que os objetos sejam do "tipo" correto
  • Há alguma sobrecarga de desempenho - como as leituras das propriedades do objeto geralmente são forçadas a passar por uma ou mais pesquisas de mapa, você paga um pequeno custo em termos de desempenho. No meu caso, não foi um problema, mas pode ser um problema em alguns casos (por exemplo, um FPS 3D com muitos objetos sendo consultados em todos os quadros)
  • As refatorações não funcionam da mesma maneira - em um sistema baseado em protótipo, você está essencialmente "construindo" sua hierarquia de herança com código. As ferramentas de IDEs / refatoração não podem realmente ajudá-lo, pois não podem influenciar sua abordagem. Eu nunca achei isso um problema, mas poderia ficar fora de controle se você não tomar cuidado. Você provavelmente deseja testes para verificar se sua hierarquia de herança está sendo construída corretamente!
  • É um pouco estranho - as pessoas acostumadas a um estilo convencional de POO podem facilmente ficar confusas. "Como assim, há apenas uma classe chamada" Coisa "?!?" - "Como faço para estender esta aula final!?!" - "Você está violando os princípios do POO !!!" - "É errado ter todas essas funções estáticas que atuam em qualquer tipo de objeto!?!?"

Finalmente, algumas notas de implementação:

  • Usei um Java HashMap para propriedades e um ponteiro "pai" para o protótipo. Isso funcionou bem, mas teve as seguintes desvantagens: a) às vezes, as leituras de propriedades precisam ser rastreadas através de uma longa cadeia pai, prejudicando o desempenho b) se você modificou um protótipo pai, a alteração afetaria todos os filhos que não substituíram a propriedade em mudança. Isso pode causar erros sutis se você não tomar cuidado!
  • Se eu estivesse fazendo isso novamente, usaria um mapa persistente imutável para as propriedades (como os mapas persistentes do Clojure ) ou minha própria implementação persistente de mapa de hash Java . Você obteria o benefício de cópias / alterações baratas, juntamente com um comportamento imutável, e não precisaria vincular permanentemente objetos aos pais deles.
  • Você pode se divertir se incorporar funções / métodos nas propriedades do objeto. O hack que eu usei em Java para isso (subtipos anônimos de uma classe "Script") não foi muito elegante - se, ao fazer isso de novo, provavelmente usaria uma linguagem apropriada e facilmente incorporável para scripts (Clojure ou Groovy)


(+1) É uma boa análise. Embora seja mais baseado no modelo Java, por exemplo, Delphi, C #, VB.Net possui propriedades explícitas.
umlcat

3
@umlcat - Eu sinto que o modelo Java é praticamente o mesmo que o modelo Delphi / C # (além do bom açúcar sintático para acesso à propriedade) - você ainda precisa declarar estaticamente as propriedades que deseja na sua definição de classe. O ponto de um modelo protótipo é que esta definição não é estática e você não tem que fazer quaisquer declarações com antecedência ....
mikera

Isso perdeu um grande problema. Você pode alterar o protótipo, que altera efetivamente as propriedades em todas as instâncias, mesmo depois que elas são criadas sem tocar no construtor do protótipo.
Erik Reppen

Em relação à herança múltipla ser mais fácil, você a comparou com Java, que não suporta herança múltipla, mas é mais fácil comparado com linguagens que suportam, como C ++?
Piovezan

2

A principal vantagem do OOP baseado em protótipo é que objetos e "classes" podem ser estendidos em tempo de execução.

No OOP baseado em classe, existem vários recursos bons, infelizmente, isso depende da linguagem de programação.

O Object Pascal (Delphi), VB.Net & C # tem uma maneira muito direta de usar propriedades (não confundir com campos) e métodos de acesso de propriedades, enquanto Java e C ++, propriedades são acessadas por métodos. E o PHP tem uma mistura de ambos, chamados "métodos mágicos".

Existem algumas classes de digitação dinâmica, embora as principais linguagens OO da classe tenham digitação estática. Eu acho que a digitação estática com a Classe OO é muito útil, porque permite um recurso chamado Introspecção de Objetos que permite criar esse IDE e desenvolver páginas da Web, visual e rapidamente.


0

Eu tenho que concordar com @umlcat. A extensão de classe é uma grande vantagem. Por exemplo, suponha que você queira adicionar mais funcionalidade a uma classe de string por um longo período de tempo. Em C ++, você faria isso através da herança contínua de gerações anteriores de classes de string. O problema dessa abordagem é que cada geração se torna essencialmente um tipo diferente, o que pode levar à reescrita maciça das bases de código existentes. Com a herança prototípica, você simplesmente 'anexa' um novo método à classe base original ..., nenhuma compilação maciça de classes herdadas e relacionamentos de herança em todos os lugares. Eu adoraria ver o C ++ criar um mecanismo de extensão semelhante em seu novo padrão. Mas seu comitê parece ser dirigido por pessoas que desejam adicionar os recursos atraentes e populares.


1
Talvez você queira ler Monólitos "Não-amarrados" , std::stringjá possui muitos membros, que devem ser algoritmos independentes ou, pelo menos, não-amigos. De qualquer forma, novas funções de membro podem ser adicionadas sem alterar o layout da memória, se você puder modificar a classe original.
Deduplicator
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.