Semana 8: Herencia en Java - Ejemplo Exhaustivo: Sistema de Empleados
En esta sección desarrollaremos un ejemplo completo que demuestra todos los conceptos de herencia en Java de manera práctica y detallada.
Descripción del Sistema
Crearemos un sistema de gestión de empleados que incluye:
- Una clase padre Empleado con funcionalidades básicas
- Clases hijas especializadas: Desarrollador y Gerente
- Métodos específicos para cada tipo de empleado
- Cálculos de salarios y bonificaciones diferenciados
Implementación Completa
Clase Padre: Empleado
public class Empleado {
// Atributos protegidos para acceso desde clases hijas
protected String nombre;
protected String apellido;
protected String cedula;
protected double salarioBase;
protected int añosExperiencia;
protected String departamento;
// Constructor
public Empleado(String nombre, String apellido, String cedula,
double salarioBase, int añosExperiencia, String departamento) {
this.nombre = nombre;
this.apellido = apellido;
this.cedula = cedula;
this.salarioBase = salarioBase;
this.añosExperiencia = añosExperiencia;
this.departamento = departamento;
}
// Getters
public String getNombre() {
return nombre;
}
public String getApellido() {
return apellido;
}
public String getCedula() {
return cedula;
}
public double getSalarioBase() {
return salarioBase;
}
public int getAñosExperiencia() {
return añosExperiencia;
}
public String getDepartamento() {
return departamento;
}
// Setters con validaciones
public void setNombre(String nombre) {
if (nombre != null && !nombre.trim().isEmpty()) {
this.nombre = nombre;
}
}
public void setApellido(String apellido) {
if (apellido != null && !apellido.trim().isEmpty()) {
this.apellido = apellido;
}
}
public void setSalarioBase(double salarioBase) {
if (salarioBase >= 0) {
this.salarioBase = salarioBase;
}
}
public void setAñosExperiencia(int añosExperiencia) {
if (añosExperiencia >= 0) {
this.añosExperiencia = añosExperiencia;
}
}
public void setDepartamento(String departamento) {
this.departamento = departamento;
}
// Métodos de negocio
public double calcularSalarioTotal() {
// Salario base + bono por experiencia (5% por año)
double bonoExperiencia = salarioBase * (añosExperiencia * 0.05);
return salarioBase + bonoExperiencia;
}
public double calcularBonoAnual() {
// Bono anual equivalente a un mes de salario
return calcularSalarioTotal();
}
public String obtenerNombreCompleto() {
return nombre + " " + apellido;
}
public void mostrarInformacion() {
System.out.println("=== INFORMACIÓN DEL EMPLEADO ===");
System.out.println("Nombre: " + obtenerNombreCompleto());
System.out.println("Cédula: " + cedula);
System.out.println("Departamento: " + departamento);
System.out.println("Años de experiencia: " + añosExperiencia);
System.out.println("Salario base: $" + String.format("%.2f", salarioBase));
System.out.println("Salario total: $" + String.format("%.2f", calcularSalarioTotal()));
System.out.println("Bono anual: $" + String.format("%.2f", calcularBonoAnual()));
}
public boolean esElegibleParaPromocion() {
// Criterio básico: más de 3 años de experiencia
return añosExperiencia >= 3;
}
}
Clase Hija: Desarrollador
public class Desarrollador extends Empleado {
// Atributos específicos del desarrollador
private String lenguajePrincipal;
private int proyectosCompletados;
private String nivelSenioridad; // Junior, Mid, Senior
private boolean tieneCertificaciones;
// Constructor
public Desarrollador(String nombre, String apellido, String cedula,
double salarioBase, int añosExperiencia, String departamento,
String lenguajePrincipal, int proyectosCompletados,
String nivelSenioridad, boolean tieneCertificaciones) {
super(nombre, apellido, cedula, salarioBase, añosExperiencia, departamento);
this.lenguajePrincipal = lenguajePrincipal;
this.proyectosCompletados = proyectosCompletados;
this.nivelSenioridad = nivelSenioridad;
this.tieneCertificaciones = tieneCertificaciones;
}
// Getters específicos
public String getLenguajePrincipal() {
return lenguajePrincipal;
}
public int getProyectosCompletados() {
return proyectosCompletados;
}
public String getNivelSenioridad() {
return nivelSenioridad;
}
public boolean isTieneCertificaciones() {
return tieneCertificaciones;
}
// Setters específicos
public void setLenguajePrincipal(String lenguajePrincipal) {
this.lenguajePrincipal = lenguajePrincipal;
}
public void setProyectosCompletados(int proyectosCompletados) {
if (proyectosCompletados >= 0) {
this.proyectosCompletados = proyectosCompletados;
}
}
public void setNivelSenioridad(String nivelSenioridad) {
this.nivelSenioridad = nivelSenioridad;
}
public void setTieneCertificaciones(boolean tieneCertificaciones) {
this.tieneCertificaciones = tieneCertificaciones;
}
// Sobrescritura del método calcularSalarioTotal
@Override
public double calcularSalarioTotal() {
double salarioConExperiencia = super.calcularSalarioTotal();
// Bono por nivel de senioridad
double bonoSenioridad = 0;
switch (nivelSenioridad.toLowerCase()) {
case "junior":
bonoSenioridad = salarioBase * 0.10; // 10%
break;
case "mid":
bonoSenioridad = salarioBase * 0.25; // 25%
break;
case "senior":
bonoSenioridad = salarioBase * 0.40; // 40%
break;
}
// Bono por proyectos completados (2% por proyecto)
double bonoProyectos = salarioBase * (proyectosCompletados * 0.02);
// Bono por certificaciones
double bonoCertificaciones = tieneCertificaciones ? salarioBase * 0.15 : 0;
return salarioConExperiencia + bonoSenioridad + bonoProyectos + bonoCertificaciones;
}
@Override
public double calcularBonoAnual() {
double bonoBase = super.calcularBonoAnual();
// Bono adicional por alta productividad
if (proyectosCompletados >= 10) {
bonoBase += salarioBase * 0.5; // 50% adicional
} else if (proyectosCompletados >= 5) {
bonoBase += salarioBase * 0.25; // 25% adicional
}
return bonoBase;
}
// Métodos específicos del desarrollador
public void completarProyecto() {
proyectosCompletados++;
System.out.println(obtenerNombreCompleto() + " ha completado un nuevo proyecto.");
System.out.println("Total de proyectos: " + proyectosCompletados);
}
public void actualizarCertificaciones(boolean nuevasCertificaciones) {
this.tieneCertificaciones = nuevasCertificaciones;
String estado = nuevasCertificaciones ? "obtenido" : "perdido";
System.out.println(obtenerNombreCompleto() + " ha " + estado + " certificaciones.");
}
public double calcularProductividad() {
// Proyectos por año de experiencia
return añosExperiencia > 0 ? (double) proyectosCompletados / añosExperiencia : 0;
}
@Override
public void mostrarInformacion() {
super.mostrarInformacion();
System.out.println("=== INFORMACIÓN ESPECÍFICA DEL DESARROLLADOR ===");
System.out.println("Lenguaje principal: " + lenguajePrincipal);
System.out.println("Nivel de senioridad: " + nivelSenioridad);
System.out.println("Proyectos completados: " + proyectosCompletados);
System.out.println("Tiene certificaciones: " + (tieneCertificaciones ? "Sí" : "No"));
System.out.println("Productividad: " + String.format("%.2f", calcularProductividad()) + " proyectos/año");
}
@Override
public boolean esElegibleParaPromocion() {
// Criterios específicos para desarrolladores
return super.esElegibleParaPromocion() &&
proyectosCompletados >= 5 &&
tieneCertificaciones;
}
}
Clase Hija: Gerente
public class Gerente extends Empleado {
// Atributos específicos del gerente
private int equipoACargoTamaño;
private double presupuestoAnual;
private int metasCumplidas;
private int metasTotales;
private String tipoGerencia; // Operacional, Estratégica, Técnica
// Constructor
public Gerente(String nombre, String apellido, String cedula,
double salarioBase, int añosExperiencia, String departamento,
int equipoACargoTamaño, double presupuestoAnual,
int metasCumplidas, int metasTotales, String tipoGerencia) {
super(nombre, apellido, cedula, salarioBase, añosExperiencia, departamento);
this.equipoACargoTamaño = equipoACargoTamaño;
this.presupuestoAnual = presupuestoAnual;
this.metasCumplidas = metasCumplidas;
this.metasTotales = metasTotales;
this.tipoGerencia = tipoGerencia;
}
// Getters específicos
public int getEquipoACargoTamaño() {
return equipoACargoTamaño;
}
public double getPresupuestoAnual() {
return presupuestoAnual;
}
public int getMetasCumplidas() {
return metasCumplidas;
}
public int getMetasTotales() {
return metasTotales;
}
public String getTipoGerencia() {
return tipoGerencia;
}
// Setters específicos
public void setEquipoACargoTamaño(int equipoACargoTamaño) {
if (equipoACargoTamaño >= 0) {
this.equipoACargoTamaño = equipoACargoTamaño;
}
}
public void setPresupuestoAnual(double presupuestoAnual) {
if (presupuestoAnual >= 0) {
this.presupuestoAnual = presupuestoAnual;
}
}
public void setMetasCumplidas(int metasCumplidas) {
if (metasCumplidas >= 0 && metasCumplidas <= metasTotales) {
this.metasCumplidas = metasCumplidas;
}
}
public void setMetasTotales(int metasTotales) {
if (metasTotales >= 0) {
this.metasTotales = metasTotales;
// Ajustar metas cumplidas si es necesario
if (metasCumplidas > metasTotales) {
metasCumplidas = metasTotales;
}
}
}
public void setTipoGerencia(String tipoGerencia) {
this.tipoGerencia = tipoGerencia;
}
// Sobrescritura del método calcularSalarioTotal
@Override
public double calcularSalarioTotal() {
double salarioConExperiencia = super.calcularSalarioTotal();
// Bono por tamaño del equipo (2% por cada persona)
double bonoEquipo = salarioBase * (equipoACargoTamaño * 0.02);
// Bono por tipo de gerencia
double bonoTipo = 0;
switch (tipoGerencia.toLowerCase()) {
case "operacional":
bonoTipo = salarioBase * 0.20; // 20%
break;
case "estratégica":
bonoTipo = salarioBase * 0.35; // 35%
break;
case "técnica":
bonoTipo = salarioBase * 0.25; // 25%
break;
}
// Bono por cumplimiento de metas
double porcentajeMetas = metasTotales > 0 ? (double) metasCumplidas / metasTotales : 0;
double bonoMetas = salarioBase * (porcentajeMetas * 0.30); // Hasta 30%
return salarioConExperiencia + bonoEquipo + bonoTipo + bonoMetas;
}
@Override
public double calcularBonoAnual() {
double bonoBase = super.calcularBonoAnual();
// Bono adicional por gestión exitosa
double porcentajeMetas = metasTotales > 0 ? (double) metasCumplidas / metasTotales : 0;
if (porcentajeMetas >= 0.9) { // 90% o más de metas cumplidas
bonoBase += salarioBase * 1.0; // 100% adicional
} else if (porcentajeMetas >= 0.7) { // 70% o más
bonoBase += salarioBase * 0.5; // 50% adicional
}
return bonoBase;
}
// Métodos específicos del gerente
public void cumplirMeta() {
if (metasCumplidas < metasTotales) {
metasCumplidas++;
System.out.println(obtenerNombreCompleto() + " ha cumplido una nueva meta.");
System.out.println("Progreso: " + metasCumplidas + "/" + metasTotales + " metas");
} else {
System.out.println("Todas las metas ya han sido cumplidas.");
}
}
public void agregarMiembroEquipo() {
equipoACargoTamaño++;
System.out.println("Nuevo miembro agregado al equipo de " + obtenerNombreCompleto());
System.out.println("Tamaño actual del equipo: " + equipoACargoTamaño + " personas");
}
public double calcularPorcentajeMetas() {
return metasTotales > 0 ? (double) metasCumplidas / metasTotales * 100 : 0;
}
public double calcularPresupuestoPorPersona() {
return equipoACargoTamaño > 0 ? presupuestoAnual / equipoACargoTamaño : 0;
}
@Override
public void mostrarInformacion() {
super.mostrarInformacion();
System.out.println("=== INFORMACIÓN ESPECÍFICA DEL GERENTE ===");
System.out.println("Tipo de gerencia: " + tipoGerencia);
System.out.println("Tamaño del equipo: " + equipoACargoTamaño + " personas");
System.out.println("Presupuesto anual: $" + String.format("%.2f", presupuestoAnual));
System.out.println("Metas cumplidas: " + metasCumplidas + "/" + metasTotales);
System.out.println("Porcentaje de cumplimiento: " + String.format("%.1f", calcularPorcentajeMetas()) + "%");
System.out.println("Presupuesto por persona: $" + String.format("%.2f", calcularPresupuestoPorPersona()));
}
@Override
public boolean esElegibleParaPromocion() {
// Criterios específicos para gerentes
return super.esElegibleParaPromocion() &&
calcularPorcentajeMetas() >= 80.0 &&
equipoACargoTamaño >= 5;
}
}
Clase de Demostración Completa
public class SistemaEmpleados {
public static void main(String[] args) {
System.out.println("=== SISTEMA DE GESTIÓN DE EMPLEADOS ===");
System.out.println();
// Crear empleados de diferentes tipos
Empleado empleadoGeneral = new Empleado(
"Ana", "García", "12345678", 3000000, 2, "Recursos Humanos"
);
Desarrollador desarrollador = new Desarrollador(
"Carlos", "Rodríguez", "87654321", 4500000, 4, "Tecnología",
"Java", 8, "Mid", true
);
Gerente gerente = new Gerente(
"María", "López", "11223344", 8000000, 7, "Operaciones",
12, 500000000, 7, 10, "Estratégica"
);
// Mostrar información inicial
System.out.println("\n=== INFORMACIÓN INICIAL DE EMPLEADOS ===");
empleadoGeneral.mostrarInformacion();
System.out.println();
desarrollador.mostrarInformacion();
System.out.println();
gerente.mostrarInformacion();
System.out.println();
// Demostrar funcionalidades específicas
System.out.println("=== DEMOSTRANDO FUNCIONALIDADES ESPECÍFICAS ===");
// Desarrollador completa proyectos
desarrollador.completarProyecto();
desarrollador.completarProyecto();
System.out.println("Nueva productividad: " +
String.format("%.2f", desarrollador.calcularProductividad()) +
" proyectos/año\n");
// Gerente cumple metas
gerente.cumplirMeta();
gerente.cumplirMeta();
gerente.agregarMiembroEquipo();
System.out.println();
// Verificar elegibilidad para promoción
System.out.println("=== ELEGIBILIDAD PARA PROMOCIÓN ===");
System.out.println(empleadoGeneral.obtenerNombreCompleto() +
" elegible: " + empleadoGeneral.esElegibleParaPromocion());
System.out.println(desarrollador.obtenerNombreCompleto() +
" elegible: " + desarrollador.esElegibleParaPromocion());
System.out.println(gerente.obtenerNombreCompleto() +
" elegible: " + gerente.esElegibleParaPromocion());
System.out.println();
// Demostrar polimorfismo
System.out.println("=== DEMOSTRACIÓN DE POLIMORFISMO ===");
Empleado[] empleados = {empleadoGeneral, desarrollador, gerente};
double totalSalarios = 0;
double totalBonos = 0;
for (Empleado emp : empleados) {
System.out.println("Procesando: " + emp.obtenerNombreCompleto());
System.out.println("Salario total: $" + String.format("%.2f", emp.calcularSalarioTotal()));
System.out.println("Bono anual: $" + String.format("%.2f", emp.calcularBonoAnual()));
totalSalarios += emp.calcularSalarioTotal();
totalBonos += emp.calcularBonoAnual();
System.out.println();
}
System.out.println("=== RESUMEN FINANCIERO ===");
System.out.println("Total salarios mensuales: $" + String.format("%.2f", totalSalarios));
System.out.println("Total bonos anuales: $" + String.format("%.2f", totalBonos));
System.out.println("Costo anual total: $" + String.format("%.2f", (totalSalarios * 12) + totalBonos));
// Demostrar uso de setters
System.out.println("\n=== ACTUALIZANDO INFORMACIÓN ===");
desarrollador.setSalarioBase(5000000);
desarrollador.setNivelSenioridad("Senior");
System.out.println("Nuevo salario del desarrollador: $" +
String.format("%.2f", desarrollador.calcularSalarioTotal()));
gerente.setPresupuestoAnual(600000000);
System.out.println("Nuevo presupuesto por persona: $" +
String.format("%.2f", gerente.calcularPresupuestoPorPersona()));
}
}
Características del Ejemplo
✅ Cumple todos los requisitos:
- Herencia completa:
DesarrolladoryGerenteextiendenEmpleado - Solo constructores: No hay métodos abstractos
- Getters y setters: Todos los atributos tienen acceso controlado
- Métodos concretos: Toda la funcionalidad está implementada
- Lógica de negocio detallada: Cálculos de salarios, bonos, productividad
- Simplicidad: Código claro y fácil de entender
🎯 Funcionalidades implementadas:
- Cálculo de salarios con diferentes criterios por tipo de empleado
- Sistema de bonificaciones específico para cada rol
- Validaciones en setters para mantener integridad de datos
- Polimorfismo para tratar diferentes tipos de empleados uniformemente
- Métricas de rendimiento específicas (productividad, cumplimiento de metas)
- Sistema de elegibilidad para promociones con criterios diferenciados
Conceptos Demostrados
1. Herencia
- Las clases
DesarrolladoryGerenteheredan deEmpleado - Reutilización de código común en la clase padre
- Especialización en las clases hijas
2. Encapsulación
- Atributos privados con acceso controlado
- Validaciones en los setters
- Métodos públicos para interactuar con los objetos
3. Polimorfismo
- Mismo método (
calcularSalarioTotal) con comportamientos diferentes - Tratamiento uniforme de diferentes tipos de empleados
- Uso de arrays de la clase padre para manejar objetos de clases hijas
4. Sobrescritura de Métodos
@Overridepara claridad en el código- Llamadas a
super()para reutilizar funcionalidad del padre - Extensión del comportamiento base con lógica específica
Este ejemplo demuestra cómo la herencia permite crear sistemas flexibles y mantenibles, donde cada tipo de empleado puede tener su propia lógica de negocio mientras comparte funcionalidades comunes.