Quels sont les avantages de la POO basée sur un prototype par rapport à la POO basée sur une classe?


47

Lorsque j'ai commencé à programmer Javascript après avoir principalement traité de la programmation orientée objet dans le contexte de langages basés sur des classes, je ne savais pas trop pourquoi la programmation orientée objet basée sur un prototype serait jamais préférée à la programmation orientée objet.

  1. Quels sont les avantages structurels de l'utilisation de la POO basée sur un prototype, le cas échéant? (Par exemple, est-ce que nous nous attendions à ce que la mémoire soit plus rapide ou moins intensive dans certaines applications?)
  2. Quels sont les avantages du point de vue d'un codeur? (Par exemple, est-il plus facile de coder certaines applications ou d'étendre le code d'autres personnes en utilisant le prototypage?)

Ne considérez pas cette question comme une question sur Javascript en particulier (qui a eu de nombreux défauts au cours des années et qui n’ont aucun rapport avec le prototypage). Au lieu de cela, examinez-le dans le contexte des avantages théoriques du prototypage par rapport aux classes.

Je vous remercie.


Réponses:


46

J'avais beaucoup d'expérience des deux approches lors de la rédaction d'un jeu de rôle en Java. A l'origine, j'avais écrit tout le jeu en utilisant la POO basée sur les classes, mais j'ai finalement réalisé que c'était une mauvaise approche (elle devenait impossible à maintenir à mesure que la hiérarchie des classes se développait). J'ai donc converti l'ensemble de la base de code en code basé sur un prototype. Le résultat était bien meilleur et plus facile à gérer.

Code source ici si vous êtes intéressé ( Tyrant - Java Roguelike )

Voici les principaux avantages:

  • Il est facile de créer de nouvelles "classes" - il suffit de copier le prototype et de modifier quelques propriétés et le tour est joué ... nouvelle classe. Je l'ai utilisé pour définir un nouveau type de potion, par exemple dans 3-6 lignes de Java chacune. Bien mieux qu’un nouveau fichier de classe et des charges de passe-partout!
  • Il est possible de créer et de gérer un très grand nombre de "classes" avec relativement peu de code - Tyrant, par exemple, avait quelque 3 000 prototypes différents avec seulement environ 42 000 lignes de code. C'est assez incroyable pour Java!
  • L'héritage multiple est simple : il vous suffit de copier un sous-ensemble des propriétés d'un prototype et de les coller sur les propriétés d'un autre prototype. Dans un RPG, par exemple, vous voudrez peut-être qu'un "golem en acier" ait certaines des propriétés d'un "objet en acier", certaines propriétés d'un "golem" et certaines propriétés d'un "monstre inintelligent". Facile avec des prototypes, essayez de le faire avec un héritage, une hiérarchie ......
  • Vous pouvez faire des choses intelligentes avec les modificateurs de propriété - en insérant une logique astucieuse dans la méthode générique "read property", vous pouvez implémenter divers modificateurs. Par exemple, il était facile de définir un anneau magique ajoutant +2 à la force de celui qui le portait. La logique de ceci était dans l'objet anneau, pas dans la méthode "force de lecture", vous évitiez ainsi de mettre beaucoup de tests conditionnels ailleurs dans votre base de code (par exemple, "le personnage porte-t-il un anneau d'augmentation de la force?")
  • Les instances peuvent devenir des modèles pour d'autres instances . Par exemple, si vous souhaitez "cloner" un objet, rien de plus simple, utilisez simplement l'objet existant comme prototype du nouvel objet. Pas besoin d'écrire beaucoup de logique de clonage complexe pour différentes classes.
  • Il est assez facile de changer le comportement au moment de l'exécution - en d'autres termes, vous pouvez modifier une propriété et "transformer" un objet de manière arbitraire au moment de l'exécution. Permet de créer de jolis effets dans le jeu. Si vous associez ceci à un "langage de script", pratiquement tout est possible au moment de l'exécution.
  • C'est plus adapté à un style de programmation "fonctionnel" - vous avez tendance à vous retrouver à écrire de nombreuses fonctions qui analysent les objets et à agir correctement, plutôt que la logique intégrée dans les méthodes attachées à des classes spécifiques. Personnellement, je préfère ce style de PF.

Voici les principaux inconvénients:

  • Vous perdez l'assurance du typage statique , car vous créez effectivement un système d'objet dynamique. Cela signifie généralement que vous devez écrire davantage de tests pour vous assurer que le comportement est correct et que les objets sont du "type" approprié.
  • Il existe une surcharge de performances : dans la mesure où les lectures de propriétés d'objet sont généralement obligées de passer par une ou plusieurs recherches de carte, vous payez un léger coût en termes de performances. Dans mon cas, ce n’était pas un problème, mais dans certains cas, cela pouvait être le cas (par exemple, un FPS 3D avec beaucoup d’objets interrogés dans chaque image)
  • Les refactorisations ne fonctionnent pas de la même manière : dans un système basé sur un prototype, vous construisez essentiellement votre hiérarchie d'héritage avec du code. Les IDE / outils de refactoring ne peuvent pas vraiment vous aider car ils ne peuvent pas adopter votre approche. Je n'ai jamais trouvé que c'était un problème, mais cela pourrait vous échapper si vous ne faites pas attention. Vous voulez probablement que les tests vérifient que votre hiérarchie d'héritage est construite correctement!
  • C'est un peu étrange - les gens habitués à un style de POO conventionnel peuvent facilement s'embrouiller. "Qu'est-ce que tu veux dire, il n'y a qu'une seule classe appelée" Chose "?!? - "Comment puis-je prolonger ce dernier cours Thing!?!" - "Vous violez les principes de la POO !!!" - "C'est faux d'avoir toutes ces fonctions statiques qui agissent sur n'importe quel type d'objet!?!?"

Enfin quelques notes d'implémentation:

  • J'ai utilisé un Java HashMap pour les propriétés et un pointeur "parent" pour le prototype. Cela fonctionnait bien mais présentait les inconvénients suivants: a) les lectures de propriété devaient parfois remonter dans une longue chaîne parentale, nuisant aux performances b) si vous mutiez un prototype parent, le changement affecterait tous les enfants qui n'auraient pas annulé la propriété en cours de modification. Cela peut causer des bugs subtils si vous ne faites pas attention!
  • Si je le faisais à nouveau, j'utiliserais une carte persistante immuable pour les propriétés (un peu comme les cartes persistantes de Clojure ) ou ma propre implémentation de carte de hachage persistante Java . Vous obtiendrez alors l'avantage d'une copie / modification peu coûteuse associée à un comportement immuable et vous n'auriez pas besoin de lier de manière permanente des objets à leurs parents.
  • Vous pouvez vous amuser si vous incorporez des fonctions / méthodes dans les propriétés de l'objet. Le hack que j'ai utilisé en Java pour cela (sous-types anonymes d'une classe "Script") n'était pas très élégant - si je le faisais à nouveau, j'utiliserais probablement un langage facilement intégrable pour les scripts (Clojure ou Groovy).


(+1) C'est une bonne analyse. Bien que son modèle repose davantage sur Java, par exemple, Delphi, C #, VB.Net possède des propriétés explicites.
Umlcat

3
@umlcat - Je pense que le modèle Java est à peu près le même que le modèle Delphi / C # (mis à part le très bon sucre syntaxique pour l'accès aux propriétés) - vous devez toujours déclarer de manière statique les propriétés que vous souhaitez dans votre définition de classe. L’intérêt d’un modèle de prototype est que cette définition n’est pas statique et que vous n’avez pas à faire de déclaration à l’avance ....
mikera le

Cela a manqué un gros. Vous pouvez modifier le prototype, ce qui modifie efficacement les propriétés de chaque instance, même après leur création, sans toucher au constructeur du prototype.
Erik Reppen

S'agissant de la simplification de l'héritage multiple, vous l'avez comparée à Java qui ne prend pas en charge l'héritage multiple, mais est-il plus facile comparé aux langages qui le prennent en charge, tel que C ++?
Piovezan

2

Le principal avantage de la POO basée sur un prototype est que les objets et les "classes" peuvent être étendus au moment de l'exécution.

Dans la POO basée sur les classes, il existe plusieurs fonctionnalités intéressantes. Malheureusement, cela dépend du langage de programmation.

Object Pascal (Delphi), VB.Net & C # ont un moyen très direct d'utiliser les propriétés (à ne pas confondre avec les champs) et les méthodes d'accès aux propriétés, tandis que les propriétés en Java & C ++ sont accessibles par les méthodes. Et PHP a un mélange des deux, appelé "méthodes magiques".

Il existe quelques classes de typage dynamiques, bien que les langages OO de la classe principale aient un typage statique. Je pense que le typage statique avec Class OO est très utile car il permet une fonctionnalité appelée Object Introspection qui permet de créer ces IDE et de développer des pages Web, visuellement et rapidement.


0

Je suis d'accord avec @umlcat. L'extension de classe est un avantage majeur. Par exemple, supposons que vous souhaitiez ajouter davantage de fonctionnalités à une classe de chaînes sur une longue période. En C ++, vous pouvez le faire en poursuivant l'héritage des générations précédentes de classes de chaînes. Le problème avec cette approche est que chaque génération devient essentiellement un type différent, ce qui peut entraîner une réécriture massive des bases de code existantes. Avec l'héritage prototype, vous «attachez» simplement une nouvelle méthode à la classe de base d'origine ..., pas de construction massive de classes héritées ni de relations d'héritage partout. J'aimerais que C ++ propose un mécanisme d'extension similaire dans leur nouveau standard. Mais leur comité semble être dirigé par des personnes qui souhaitent ajouter des fonctionnalités accrocheuses et populaires.


1
Vous voudrez peut-être lire Monoliths "Unstrung" , a std::stringdéjà trop de membres qui doivent être des algorithmes indépendants ou au moins des non-membres non amis. Et de toute façon, de nouvelles fonctions membres peuvent être ajoutées sans modifier la disposition en mémoire si vous pouvez modifier la classe d'origine.
Déduplicateur
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.