Laboratorio 05 — Polimorfismo en Java - jorgealbertopineda78-del/java GitHub Wiki

Laboratorio 05 — Polimorfismo en Java

Unidad: 5 — Polimorfismo
Duración estimada: 90 minutos
Nivel: Intermedio


Conexión con el laboratorio anterior

En el Lab04 terminaste con este bloque en Main:

Personaje[] equipo = { guerrero, mago, arquero };
for (Personaje p : equipo) {
    p.atacar();   // ¿por qué cada uno responde diferente?
}

Esa pregunta es exactamente lo que responde este laboratorio.
Bienvenido al polimorfismo.


Objetivo

Comprender e implementar polimorfismo en Java a través de:

  • Referencias de tipo padre apuntando a objetos de tipo hijo
  • Despacho dinámico de métodos en tiempo de ejecución
  • Sobrecarga de métodos (overloading) como forma estática de polimorfismo
  • Uso de instanceof para identificar el tipo real de un objeto

Contexto del problema

El videojuego RPG del Lab04 ha crecido. Ahora el sistema necesita gestionar equipos mixtos de personajes sin saber de antemano qué tipo exacto es cada uno. El motor del juego debe poder:

  • Procesar una batalla con cualquier combinación de personajes
  • Calcular el daño de ataque de diferentes maneras según el tipo
  • Registrar eventos de combate en un historial

Tu tarea es ampliar la jerarquía existente aplicando polimorfismo real.


Diagrama UML

        ┌─────────────────────────────────┐
        │           Personaje             │
        │         (del Lab04)             │
        ├─────────────────────────────────┤
        │ + atacar()          : void      │
        │ + defender()        : void      │
        │ + calcularDanio()   : int       │  ← NUEVO
        │ + toString()        : String    │
        └──────────────┬──────────────────┘
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
     Guerrero        Mago        Arquero
  calcularDanio() calcularDanio() calcularDanio()
  (fuerza * nivel) (mana * nivel) (precision * flechas)

        ┌─────────────────────────────────┐
        │          GestorBatalla          │  ← NUEVA CLASE
        ├─────────────────────────────────┤
        │ - historial : ArrayList<String> │
        ├─────────────────────────────────┤
        │ + ejecutarAtaque(Personaje)     │  ← sobrecarga
        │ + ejecutarAtaque(Personaje,     │
        │                  Personaje)     │  ← sobrecarga
        │ + ejecutarAtaque(Personaje[])   │  ← sobrecarga
        │ + mostrarHistorial()            │
        └─────────────────────────────────┘

Instrucciones

Parte 1 — Ampliar la jerarquía del Lab04 (en clase)

Reutiliza o recrea las clases Personaje, Guerrero, Mago y Arquero del Lab04.

Agrega el método calcularDanio() en cada clase:

En Personaje (método base):

public int calcularDanio() {
    return getNivel() * 10;  // daño base según nivel
}

En Guerrero — sobreescribe calcularDanio():

  • Retorna fuerza * getNivel()

En Mago — sobreescribe calcularDanio():

  • Retorna mana * getNivel()

En Arquero — sobreescribe calcularDanio():

  • Retorna precision * flechasDisponibles (o los atributos que hayas definido)

💡 Esto es polimorfismo: el mismo método calcularDanio() produce resultados distintos según el tipo real del objeto, aunque la variable sea de tipo Personaje.


Parte 2 — Clase GestorBatalla (en clase con el profesor)

Crea GestorBatalla.java con los siguientes métodos sobrecargados:

// Versión 1: un solo atacante
public void ejecutarAtaque(Personaje atacante)

// Versión 2: atacante vs defensor
public void ejecutarAtaque(Personaje atacante, Personaje defensor)

// Versión 3: todo un equipo ataca
public void ejecutarAtaque(Personaje[] equipo)

Cada versión debe:

  1. Calcular el daño con atacante.calcularDanio()
  2. Llamar a atacante.atacar()
  3. Si hay defensor, llamar a defensor.recibirDanio(danio) y a defensor.defender()
  4. Registrar el evento en el historial (un ArrayList<String>)

Agrega también:

  • mostrarHistorial() — imprime todos los eventos registrados
  • limpiarHistorial() — vacía el historial

💡 Esto es sobrecarga: mismo nombre de método (ejecutarAtaque), diferente firma (distintos parámetros). El compilador elige cuál usar según los argumentos.


Parte 3 — Demostración de polimorfismo (trabajo independiente)

En Main.java, demuestra el polimorfismo de las siguientes formas:

3a — Variable de tipo padre apunta a objeto hijo:

Personaje p1 = new Guerrero("Thorin", 5, 200, 85, "Cota de Malla");
Personaje p2 = new Mago("Gandalf", 8, 120, 150, "Fuego");
Personaje p3 = new Arquero("Legolas", 6, 150, "Arco Largo", 30, 95);

System.out.println(p1.calcularDanio()); // ejecuta el de Guerrero
System.out.println(p2.calcularDanio()); // ejecuta el de Mago
System.out.println(p3.calcularDanio()); // ejecuta el de Arquero

3b — Arreglo polimórfico:

Personaje[] equipo = { p1, p2, p3 };
for (Personaje p : equipo) {
    System.out.println(p.getNombre() + " daño: " + p.calcularDanio());
}

3c — Usar GestorBatalla con las tres sobrecargas:

GestorBatalla gestor = new GestorBatalla();

gestor.ejecutarAtaque(p1);            // versión 1 persona
gestor.ejecutarAtaque(p2, p3);        // versión atacante vs defensor
gestor.ejecutarAtaque(equipo);        // versión equipo completo

gestor.mostrarHistorial();

3d — Identificar tipo real con instanceof:

for (Personaje p : equipo) {
    if (p instanceof Guerrero) {
        System.out.println(p.getNombre() + " es un Guerrero.");
    } else if (p instanceof Mago) {
        System.out.println(p.getNombre() + " es un Mago.");
    } else if (p instanceof Arquero) {
        System.out.println(p.getNombre() + " es un Arquero.");
    }
}

Parte 4 — Sobrecarga adicional (trabajo independiente)

Agrega en Personaje (o en cualquier subclase) al menos dos versiones sobrecargadas de un método nuevo. Por ejemplo:

// En Personaje:
public void mostrarEstado()                    // imprime info básica
public void mostrarEstado(boolean detallado)   // imprime info completa
public void mostrarEstado(String formato)      // imprime con prefijo personalizado

O en una subclase:

// En Guerrero:
public void entrenar()                  // sube fuerza en 5
public void entrenar(int sesiones)      // sube fuerza en 5 * sesiones
public void entrenar(int sesiones, boolean intensivo)  // intensivo duplica el efecto

Demuestra las tres versiones en Main.


La diferencia clave

POLIMORFISMO (Override)     vs     SOBRECARGA (Overload)
─────────────────────────────────────────────────────────
Mismo método                       Mismo nombre
Diferente clase                    Diferente firma (parámetros)
Se resuelve en EJECUCIÓN           Se resuelve en COMPILACIÓN
Requiere herencia                  No requiere herencia

Ejemplo de salida esperada

=== RPG — Demostración de Polimorfismo ===

-- calcularDanio() por tipo --
Thorin  (Guerrero) daño: 425    ← fuerza(85) * nivel(5)
Gandalf (Mago)     daño: 1200   ← mana(150)  * nivel(8)
Legolas (Arquero)  daño: 2850   ← precision(95) * flechas(30)

-- Arreglo polimórfico --
[Thorin]  ataca con un golpe básico.
¡Thorin golpea con su espada causando 85 de daño!
[Gandalf] ataca con un golpe básico.
¡Gandalf lanza una bola de fuego causando 150 de daño mágico!
[Legolas] ataca con un golpe básico.
Legolas dispara una flecha. Flechas restantes: 29

-- GestorBatalla --
[BATALLA] Thorin ataca solo → daño: 425
[BATALLA] Gandalf ataca a Legolas → daño: 1200
Legolas recibe 1200 puntos de daño. Vida restante: 0
Legolas ha sido derrotado.
[BATALLA] Equipo completo ataca → 3 personajes

-- Historial --
1. Thorin atacó solo (daño: 425)
2. Gandalf atacó a Legolas (daño: 1200) - Legolas derrotado
3. Ataque en equipo: Thorin, Gandalf, Legolas

-- instanceof --
Thorin es un Guerrero.
Gandalf es un Mago.
Legolas es un Arquero.

Criterios de evaluación

Criterio Puntos
calcularDanio() sobreescrito correctamente en las 3 subclases 20
GestorBatalla con las 3 sobrecargas de ejecutarAtaque() 20
Historial funciona y registra eventos correctamente 10
Demostración con arreglo polimórfico Personaje[] 15
Uso correcto de instanceof para identificar tipos 10
Sobrecarga adicional propia (Parte 4) con 3 versiones 15
Main integra todo y la salida es coherente 10
Total 100

Preguntas de reflexión

Responde en respuestas.txt:

  1. ¿Cuál es la diferencia entre sobreescritura (@Override) y sobrecarga (overload)? Da un ejemplo de cada uno tomado de este laboratorio.
  2. Si tienes Personaje p = new Mago(...) y llamas p.calcularDanio(), ¿Java usa el método de Personaje o el de Mago? ¿Por qué?
  3. ¿Para qué sirve instanceof? ¿En qué situaciones lo usarías en un sistema real?

Entregable

Sube al repositorio en la carpeta lab05_polimorfismo:

lab05_polimorfismo/
├── Combatiente.java
├── Personaje.java
├── Guerrero.java
├── Mago.java
├── Arquero.java
├── GestorBatalla.java
└── Main.java

Laboratorio preparado para el curso de Programación Orientada a Objetos — Ago-Dic 2025

⚠️ **GitHub.com Fallback** ⚠️