paint-brush
Héritage vs composition : utilisation d'un jeu de rôle en JavaScript comme exemplepar@kliukovkin
13,348 lectures
13,348 lectures

Héritage vs composition : utilisation d'un jeu de rôle en JavaScript comme exemple

par Georgii Kliukovkin4m2022/12/21
Read on Terminal Reader

Trop long; Pour lire

Avec la composition, vous pouvez éviter les problèmes d'héritage et javascript est un langage parfait pour le faire.
featured image - Héritage vs composition : utilisation d'un jeu de rôle en JavaScript comme exemple
Georgii Kliukovkin HackerNoon profile picture

Problèmes d'héritage

  • Duplication de code chez les enfants
  • Complexité excessive dans la hiérarchie d'héritage
  • Changer le comportement du parent peut entraîner des erreurs chez les enfants


Dans cet article, nous verrons en quoi consistent ces problèmes et comment nous pouvons les résoudre en utilisant la composition.


le problème avec les langages orientés objet est qu'ils ont tout cet environnement implicite qu'ils transportent avec eux. Tu voulais une banane mais ce que tu as eu c'est un gorille tenant la banane et toute la jungle. - Joe Armstrong, créateur d'Erlang

Jeu de rôle Héritage

Considérez le processus de création d'une hiérarchie de personnages de jeux de rôle. Initialement, deux types de personnages sont requis - Guerrier et Mage, chacun ayant une certaine santé et un nom. Ces propriétés sont publiques et peuvent être déplacées vers la classe parent Character.

 class Character { constructor(name) { this.name = name; this.health = 100; } }


Un guerrier peut frapper en dépensant son endurance :

 class Warrior extends Character { constructor(name) { super(name); this.stamina = 100; } fight() { console.log(`${this.name} takes a mighty swing!`); this.stamina--; } }


Et un mage peut lancer des sorts qui dépensent une certaine quantité de mana :

 class Mage extends Character { constructor(name) { super(name); this.mana = 100; } cast() { console.log(`${this.name} casts a fireball!`); this.mana--; } }

Problème de classe Paladin

Introduisons maintenant une nouvelle classe, Paladin . Un paladin peut à la fois combattre et lancer des sorts. comment pouvons nous résoudre ceci? Voici quelques solutions qui partagent le même manque d'élégance :


  • Nous pouvons faire de Paladin un descendant de Character et y implémenter à la fois les méthodes fight() et cast() à partir de rien. Dans ce cas, le principe DRY est violé car chacune des méthodes sera dupliquée à la création et nécessitera une synchronisation constante avec les méthodes des classes Mage et Fighter pour suivre les changements.


  • Les méthodes fight() et cast() peuvent être implémentées au niveau de la classe Character afin que les trois types de caractères les aient. C'est une solution légèrement meilleure, mais dans ce cas, le développeur doit remplacer la méthode fight() pour le mage et la méthode cast() pour le guerrier, en les remplaçant par des méthodes vides ou en consolant une erreur.

Composition

Ces problèmes peuvent être résolus avec une approche fonctionnelle utilisant la composition. Il suffit de partir non de leurs types, mais de leurs fonctions. Fondamentalement, nous avons deux caractéristiques clés qui déterminent les capacités des personnages : la capacité de se battre et la capacité de lancer des sorts.


Ces fonctionnalités peuvent être définies à l'aide de fonctions d'usine qui étendent l'état qui définit le caractère :

 const canCast = (state) => ({ cast: (spell) => { console.log(`${state.name} casts ${spell}!`); state.mana--; } }) const canFight = (state) => ({ fight: () => { console.log(`${state.name} slashes at the foe!`); state.stamina--; } })


Ainsi, un personnage est défini par un ensemble de ces capacités et propriétés initiales, à la fois générales (nom et santé) et privées (endurance et mana) :

 const fighter = (name) => { let state = { name, health: 100, stamina: 100 } return Object.assign(state, canFight(state)); } const mage = (name) => { let state = { name, health: 100, mana: 100 } return Object.assign(state, canCast(state)); } const paladin = (name) => { let state = { name, health: 100, mana: 100, stamina: 100 } return Object.assign(state, canCast(state), canFight(state)); }

Conclusion

Avec la composition, vous pouvez éviter les problèmes d'héritage en utilisant la composition, et javascript est le langage parfait pour le faire.