paint-brush
Herencia frente a composición: usar un juego de rol en JavaScript como ejemplopor@kliukovkin
13,348 lecturas
13,348 lecturas

Herencia frente a composición: usar un juego de rol en JavaScript como ejemplo

por Georgii Kliukovkin4m2022/12/21
Read on Terminal Reader

Demasiado Largo; Para Leer

Con la composición, puede evitar problemas de herencia y javascript es un lenguaje perfecto para hacerlo.
featured image - Herencia frente a composición: usar un juego de rol en JavaScript como ejemplo
Georgii Kliukovkin HackerNoon profile picture

Problemas de herencia

  • Duplicación de código en niños
  • Complejidad excesiva en la jerarquía de herencia
  • Cambiar el comportamiento de los padres puede conducir a errores en los hijos


En este artículo, veremos de qué se tratan estos problemas y cómo podemos resolverlos usando la composición.


el problema con los lenguajes orientados a objetos es que tienen todo este entorno implícito que llevan consigo. Querías una banana pero lo que obtuviste fue un gorila sosteniendo la banana y toda la jungla. - Joe Armstrong, creador de Erlang

Herencia de juegos de rol

Considere el proceso de crear una jerarquía de personajes de juegos de rol. Inicialmente, se requieren dos tipos de personajes: Guerrero y Mago, cada uno de los cuales tiene una cierta cantidad de salud y un nombre. Estas propiedades son públicas y se pueden mover a la clase de carácter principal.

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


Un guerrero puede atacar, gastando su energía:

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


Y un mago puede lanzar hechizos que gastan cierta cantidad de maná:

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

Problema de clase de paladín

Ahora, presentemos una nueva clase, Paladín . Un paladín puede luchar y lanzar hechizos. ¿Cómo podemos solucionar esto? Aquí hay un par de soluciones que comparten la misma falta de elegancia:


  • Podemos hacer que Paladin sea un descendiente de Character e implementar los métodos fight() y cast() desde cero. En este caso, se viola el principio DRY porque cada uno de los métodos se duplicará en el momento de la creación y necesitará una sincronización constante con los métodos de las clases Mage y Fighter para realizar un seguimiento de los cambios.


  • Los métodos fight() y cast() se pueden implementar en el nivel de clase de personaje para que los tres tipos de personajes los tengan. Esta es una solución un poco mejor, pero en este caso, el desarrollador debe anular el método fight() para el mago y el método cast() para el guerrero, reemplazándolos con métodos vacíos o consolando un error.

Composición

Estos problemas se pueden resolver con un enfoque funcional utilizando la composición. Basta partir no de sus tipos, sino de sus funciones. Básicamente, tenemos dos características clave que determinan las habilidades de los personajes: la habilidad para luchar y la habilidad para lanzar hechizos.


Estas características se pueden configurar mediante funciones de fábrica que amplían el estado que define el carácter:

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


Así, un personaje se define por un conjunto de estas habilidades y propiedades iniciales, tanto generales (nombre y salud) como privadas (resistencia y maná):

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

Conclusión

Con la composición, puede evitar problemas de herencia usando la composición, y javascript es el lenguaje perfecto para hacerlo.