# Abstracción

En programación orientada a objetos, la abstracción se refiere a la capacidad de representar un objeto del mundo real de una manera simplificada y abstracta. En otras palabras, se trata de ocultar los detalles innecesarios y centrarse en lo esencial del objeto.

En el caso de los Pokémon, podemos utilizar la abstracción para representar un Pokémon de una manera simplificada, sin necesidad de conocer todos los detalles internos que conforman al Pokémon. Por ejemplo, podríamos definir una clase Pokemon que tenga solo las propiedades y métodos necesarios para representar un Pokémon básico:

class Pokemon {

  #name;
  #type;
  #hp;

  constructor(name, type, hp) {
    this.#name = name;
    this.#type = type;
    this.#hp = hp;
  }

  get name() {
    return this.#name;
  }

  get type() {
    return this.#type;
  }

  get hp() {
    return this.#hp;
  }

  set hp(value) {
    this.#hp = value;
  }

  attack() {
    console.log(`${this.#name} ha atacado`);
  }
}

En este ejemplo, hemos definido una clase Pokemon con tres propiedades: #name (nombre), #type (tipo) y #hp (puntos de salud). Además, hemos definido dos métodos get para obtener el nombre y el tipo del Pokémon, y un método set para establecer los puntos de salud del Pokémon.

También hemos definido un método attack que simplemente imprime un mensaje indicando que el Pokémon ha atacado.

Esta abstracción nos permite representar un Pokémon de manera simplificada y trabajar con él en el código sin tener que preocuparnos por los detalles internos que conforman al Pokémon. Por ejemplo, podríamos crear un objeto pikachu de la clase Pokemon y trabajar con él de la siguiente manera:

const pikachu = new Pokemon('Pikachu', 'Electric', 100);
console.log(`El tipo de ${pikachu.name} es ${pikachu.type}`);
pikachu.attack();

En este código, hemos creado un objeto pikachu de la clase Pokemon, estableciendo su nombre, tipo y puntos de salud. Luego, hemos utilizado los métodos get para obtener el nombre y el tipo de pikachu, y hemos llamado al método attack para que pikachu ataque. Todo esto se ha hecho de manera simplificada, sin tener que preocuparnos por los detalles internos que conforman a pikachu.

Además, la abstracción también nos permite crear clases más especializadas que heredan de la clase Pokemon y que contienen propiedades y métodos específicos para cada tipo de Pokémon.

Por ejemplo, podríamos crear una clase ElectricPokemon que herede de Pokemon y que tenga propiedades y métodos específicos para los Pokémon eléctricos:

class ElectricPokemon extends Pokemon {

  #electricAttack;

  constructor(name, hp) {
    super(name, 'Electric', hp);
    this.#electricAttack = 'Impactrueno';
  }

  get electricAttack() {
    return this.#electricAttack;
  }

  useElectricAttack() {
    console.log(`${this.name} ha usado ${this.#electricAttack}`);
  }
}

En este ejemplo, hemos creado una clase ElectricPokemon que hereda de la clase Pokemon. Además de las propiedades y métodos de la clase Pokemon, la clase ElectricPokemon tiene una propiedad adicional #electricAttack (ataque eléctrico) y un método adicional useElectricAttack (usar ataque eléctrico).

Cuando creamos un objeto de la clase ElectricPokemon, como por ejemplo un objeto raichu, podemos utilizar tanto las propiedades y métodos de la clase Pokemon como las propiedades y métodos específicos de la clase ElectricPokemon:

const raichu = new ElectricPokemon('Raichu', 120);
console.log(`El tipo de ${raichu.name} es ${raichu.type}`);
raichu.attack();
raichu.useElectricAttack();

En este código, hemos creado un objeto raichu de la clase ElectricPokemon, estableciendo su nombre y puntos de salud. Luego, hemos utilizado los métodos get para obtener el nombre y el tipo de raichu, hemos llamado al método attack para que raichu ataque, y finalmente hemos llamado al método useElectricAttack para que raichu use su ataque eléctrico.

En resumen

La abstracción nos permite representar objetos de manera simplificada y trabajar con ellos sin tener que preocuparnos por los detalles internos que conforman al objeto. Además, nos permite crear clases más especializadas que heredan de una clase base y que contienen propiedades y métodos específicos para cada tipo de objeto.

¡Ojo!

El operador super se utiliza en las clases de JavaScript para acceder a la implementación de un método en la clase padre de una clase. Sin embargo, no es posible utilizar el operador super para acceder a propiedades privadas de la clase padre que estén definidas como #propiedad.

Si se intenta acceder a una propiedad privada de la clase padre utilizando super.#propiedad, se producirá un error en tiempo de ejecución. Esto se debe a que el uso de #propiedad está restringido al ámbito de la clase donde se define, lo que significa que no puede ser accedido por clases hijas o por cualquier otro código fuera de la clase.

En su lugar, se puede utilizar un método público de la clase padre para acceder a la propiedad privada y devolver su valor. Luego, el método público de la clase padre puede ser llamado utilizando super.metodoPublico() en la clase hija para obtener el valor de la propiedad privada.