Publicado en Tema 5 • Ejercicios

Ejercicio 8: Gestión de Empleados y Polimorfismo

Enunciado

El objetivo de este ejercicio es desarrollar un pequeño sistema de gestión de empleados utilizando los pilares de la Programación Orientada a Objetos. Todos los empleados comparten comportamientos comunes, pero cada tipo calcula su salario de forma distinta.

Requisitos

  • Clase base Empleado: Debe contener el atributo nombre y el método public double calcularSalario() (que devuelve 0 en la base).
  • Clases hijas:
    • EmpleadoFijo: Atributo salarioMensual. Sobrescribe el cálculo devolviendo dicho salario.
    • EmpleadoPorHoras: Atributos horasTrabajadas y precioHora. Sobrescribe el cálculo devolviendo el producto de ambos.
  • Programa principal: En la clase Main, crear una colección de empleados, añadir instancias de cada tipo y mostrar su información recorriendo la lista.

Restricciones

No se permite el uso de instanceof, ni estructuras if o switch para distinguir los tipos de empleados. El comportamiento debe resolverse exclusivamente mediante polimorfismo desde una referencia de tipo Empleado.
---

Solución Propuesta

A continuación se presenta la implementación completa incluyendo la ampliación opcional: el tipo EmpleadoComision, el uso de toString() y la aplicación de una subida salarial común.

1. Jerarquía de Clases

Definimos la clase base y sus especializaciones. Es fundamental que aumentarSalario esté presente en la clase base para poder invocarlo de forma polimórfica.

package Tema5.Ejercicios.ejercicio8;

// Clase base Empleado
class Empleado {
    private String nombre;

    public Empleado(String nombre) {
        this.nombre = nombre;
    }

    public String getNombre() {
        return nombre;
    }

    public double calcularSalario() {
        return 0.0;
    }

    // Método para subir salario común (establece el contrato)
    public void aumentarSalario(double porcentaje) {
        // Implementación genérica o vacía según el diseño
    }

    // Uso de toString para formatear la salida
    @Override
    public String toString() {
        return String.format("Nombre: %s - Salario: %.2f", nombre, calcularSalario());
    }
}

// Empleado con salario mensual fijo
class EmpleadoFijo extends Empleado {
    private double salarioMensual;

    public EmpleadoFijo(String nombre, double salarioMensual) {
        super(nombre);
        this.salarioMensual = salarioMensual;
    }

    @Override
    public double calcularSalario() {
        return salarioMensual;
    }

    @Override
    public void aumentarSalario(double porcentaje) {
        this.salarioMensual += this.salarioMensual * (porcentaje / 100);
    }
}

// Empleado por horas
class EmpleadoPorHoras extends Empleado {
    private int horasTrabajadas;
    private double precioHora;

    public EmpleadoPorHoras(String nombre, int horasTrabajadas, double precioHora) {
        super(nombre);
        this.horasTrabajadas = horasTrabajadas;
        this.precioHora = precioHora;
    }

    @Override
    public double calcularSalario() {
        return horasTrabajadas * precioHora;
    }

    @Override
    public void aumentarSalario(double porcentaje) {
        this.precioHora += this.precioHora * (porcentaje / 100);
    }
}

// Empleado a comisión (Ampliación)
class EmpleadoComision extends Empleado {
    private double ventasTotales;
    private double porcentajeComision;

    public EmpleadoComision(String nombre, double ventasTotales, double porcentajeComision) {
        super(nombre);
        this.ventasTotales = ventasTotales;
        this.porcentajeComision = porcentajeComision;
    }

    @Override
    public double calcularSalario() {
        return ventasTotales * porcentajeComision;
    }

    @Override
    public void aumentarSalario(double porcentaje) {
        this.porcentajeComision += this.porcentajeComision * (porcentaje / 100);
    }
}

2. Clase Main

En el programa principal, tratamos a todos los objetos de forma genérica a través de la clase Empleado.

package Tema5.Ejercicios.ejercicio8;

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Crear colección de empleados
        ArrayList<Empleado> listaEmpleados = new ArrayList<>();

        // Añadir diferentes tipos de empleados
        listaEmpleados.add(new EmpleadoFijo("Ana García", 2000.0));
        listaEmpleados.add(new EmpleadoPorHoras("Luis Pérez", 40, 15.5));
        listaEmpleados.add(new EmpleadoComision("Sofia Martinez", 50000.0, 0.05));

        System.out.println("--- Listado de nóminas inicial ---");
        imprimirNominas(listaEmpleados);

        System.out.println("\nAplicando una subida salarial del 10%...");
        for (Empleado e : listaEmpleados) {
            e.aumentarSalario(10);
        }

        imprimirNominas(listaEmpleados);
    }

    // Método polimórfico para recorrer y mostrar información
    public static void imprimirNominas(ArrayList<Empleado> empleados) {
        for (Empleado emp : empleados) {
            System.out.println(emp.toString());
        }
    }
}

Pregunta de Reflexión

¿Por qué este diseño sigue funcionando aunque se añadan nuevos tipos de empleados sin modificar el main?

Este diseño sigue funcionando gracias al Polimorfismo y al cumplimiento del Principio de Abierto/Cerrado. El método main está programado para trabajar con la abstracción Empleado. Java utiliza la ligadura dinámica (late binding) para determinar en tiempo de ejecución qué implementación específica de calcularSalario o aumentarSalario debe llamar según el objeto real almacenado en la referencia. Esto permite extender el sistema con nuevas clases sin alterar la lógica de control ya existente.