Ejemplo 5: Sistema de Gestión de Empleados
Conceptos clave
- Herencia: Permite que una clase (hija) herede atributos y métodos de otra clase (padre). Así podemos reutilizar código y establecer relaciones "es-un".
- Sobrecarga: Consiste en definir varios métodos con el mismo nombre pero diferentes parámetros (tipo, número o ambos). Se resuelve en tiempo de compilación.
Descripción del problema
Tenemos dos tipos principales de empleados:
- Empleado a tiempo completo: Tiene un salario mensual fijo.
- Empleado por horas: Se le paga según las horas trabajadas y una tarifa por hora.
Ambos comparten datos básicos: nombre, ID y departamento. Además, necesitamos calcular su pago, pero de forma distinta. También queremos flexibilidad para calcular pagos considerando horas extra o bonos.
Así mismo deberá de contar con un menú de interacción para agregar empleados, mostrar su información y calcular su pago, además de validar los datos ingresados por el usuario utilizando JOptionPane.
Implementación
Clase base Emplployee
package app.empresa;
public abstract class Employee {
private static int ID = 1;
protected final int idEmployee;
protected String name;
protected Department department;
public Employee(String name, Department department) {
this.idEmployee = ID++;
this.name = name;
this.department = department;
}
public abstract double getSalary();
public int getIdEmployee() {
return idEmployee;
}
public String getName() {
return name;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return String.format("ID: %d, Nombre: %s", idEmployee, name);
}
}
Clase FullTimeEmployee
package app.empresa;
import java.util.Arrays;
import java.util.stream.IntStream;
public class FullTimeEmployee extends Employee {
private final double salary;
private final double[] benefits;
public FullTimeEmployee(String name, Department department, double salary) {
super(name, department);
this.salary = salary;
this.benefits = new double[5];
}
public void addBenefit(double benefit) {
IntStream.range(0, benefits.length).filter(i -> benefits[i] == 0).findFirst().ifPresent(i -> benefits[i] = benefit);
}
public void deleteBenefit(double benefit) {
IntStream.range(0, benefits.length).filter(i -> benefits[i] == benefit).findFirst().ifPresent(i -> benefits[i] = 0);
}
@Override
public double getSalary() {
double finalSalary = salary;
finalSalary += Arrays.stream(benefits).sum();
return finalSalary;
}
public boolean isBenefitsFull() {
return Arrays.stream(benefits).allMatch(b -> b > 0);
}
public boolean hasBenefits() {
return Arrays.stream(benefits).anyMatch(b -> b > 0);
}
public Double[] getBenefits() {
return Arrays.stream(benefits).boxed().toArray(Double[]::new);
}
}
Clase HourlyEmployee
package app.empresa;
public class HourlyEmployee extends Employee {
private final double hourlyRate;
private int extraHours;
private int hoursWorked;
public HourlyEmployee(String name, Department department, double hourlyRate) {
super(name, department);
this.hourlyRate = hourlyRate;
this.hoursWorked = 0;
this.extraHours = 0;
}
public void addExtraHours(int hours) {
addHours(hours, false);
}
public void addHours(int hours) {
addHours(hours, true);
}
private void addHours(int hours, boolean isExtra) {
if (isExtra) {
this.extraHours += hours;
} else {
this.hoursWorked += hours;
}
}
@Override
public double getSalary() {
double baseSalary = hourlyRate * hoursWorked;
double extraSalary = (hourlyRate * 1.5) * extraHours;
return baseSalary + extraSalary;
}
}
Enum Department
package app.empresa;
public enum Department {
HR("Human Resources"),
IT("Information Technology"),
SALES("Sales"),
MARKETING("Marketing");
private final String name;
Department(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return getName();
}
}
Enum MenuOption
package app.empresa;
public enum MenuOption {
ADD_EMPLOYEE("Agregar Empleado"),
DELETE_EMPLOYEE("Eliminar Empleado"),
ADD_HOURS("Agregar Horas"),
ADD_EXTRA_HOURS("Agregar Horas Extra"),
ADD_BENEFIT("Agregar Beneficio"),
DELETE_BENEFIT("Eliminar Beneficio"),
CALCULATE_SALARY("Calcular Salario"),
CALCULAR_NOMINA("Calcular Nómina"),
EXIT("Salir");
private String name;
MenuOption(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return getName();
}
}
Clase EmployeeManagementSystem
package app.empresa;
import com.utils.validate.InputValidator;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Predicate;
public class EmployeeManagementSystem {
private final Employee[] employees;
public EmployeeManagementSystem() {
this.employees = new Employee[500];
showMenu();
}
public void showMenu() {
MenuOption option;
do {
option = InputValidator.validateInput(
"Seleccione una opción",
MenuOption.values());
switch (option) {
case ADD_EMPLOYEE -> addEmployee();
case DELETE_EMPLOYEE -> deleteEmployee();
case ADD_HOURS -> addHours();
case ADD_EXTRA_HOURS -> addExtraHours();
case ADD_BENEFIT -> addBenefit();
case DELETE_BENEFIT -> deleteBenefit();
case CALCULATE_SALARY -> showSalary();
case CALCULAR_NOMINA -> calculatePayroll();
case EXIT -> InputValidator.showInfoMessage("Saliendo de la aplicación...");
}
} while (option != MenuOption.EXIT);
}
private void showSalary() {
Employee employee = selectEmployee(employees);
if (employee != null) {
InputValidator.showInfoMessage(String.format("El salario de %s es: %.2f", employee.getName(), employee.getSalary()));
} else {
InputValidator.showErrorMessage("No se pudo calcular el salario, no hay empleados disponibles.");
}
}
private void calculatePayroll() {
double totalPayroll = Arrays.stream(employees)
.filter(Objects::nonNull)
.mapToDouble(Employee::getSalary)
.sum();
InputValidator.showInfoMessage(String.format("La nómina total es: %.2f", totalPayroll));
}
private void addBenefit() {
FullTimeEmployee employee = (FullTimeEmployee) selectFullTimeEmployee();
if (employee != null) {
if (employee.isBenefitsFull()) {
InputValidator.showErrorMessage("No se pueden agregar más beneficios a este empleado, la capacidad máxima ha sido alcanzada.");
} else {
double benefit = InputValidator.validateInput(
"Ingrese el monto del beneficio a agregar",
Double.class, s -> s > 0);
employee.addBenefit(benefit);
InputValidator.showInfoMessage("Beneficio agregado exitosamente.");
}
} else {
InputValidator.showErrorMessage("No se pudo agregar beneficios, no hay empleados de tiempo completo disponibles.");
}
}
private void deleteBenefit() {
FullTimeEmployee employee = (FullTimeEmployee) selectFullTimeEmployee();
if (employee != null) {
if (employee.hasBenefits()) {
double benefit = InputValidator.validateInput(
"Seleccione el monto del beneficio a eliminar",
employee.getBenefits());
employee.deleteBenefit(benefit);
InputValidator.showInfoMessage("Beneficio eliminado exitosamente.");
} else {
InputValidator.showErrorMessage("No se pueden eliminar beneficios de este empleado, no tiene beneficios registrados.");
}
} else {
InputValidator.showErrorMessage("No se pudo eliminar beneficios, no hay empleados de tiempo completo disponibles.");
}
}
private void addHour(boolean isExtra) {
HourlyEmployee employee = (HourlyEmployee) selectHoursEmployee();
if (employee != null) {
int hours = InputValidator.validateInput(
"Ingrese la cantidad de horas a agregar",
Integer.class, s -> s > 0);
if (isExtra) {
employee.addExtraHours(hours);
InputValidator.showInfoMessage("Horas extra agregadas exitosamente.");
} else {
employee.addHours(hours);
InputValidator.showInfoMessage("Horas agregadas exitosamente.");
}
} else {
InputValidator.showErrorMessage("No se pudo agregar horas, no hay empleados de horas disponibles.");
}
}
private void addHours() {
addHour(false);
}
private void addExtraHours() {
addHour(true);
}
private Employee selectEmployee(Employee[] employees) {
if (Arrays.stream(employees).noneMatch(Objects::nonNull)) {
InputValidator.showErrorMessage("No hay empleados disponibles para esta selección.");
return null;
} else {
return InputValidator.validateInput(
"Seleccione un empleado",
employees);
}
}
private Employee selectEmployee(Predicate<Employee> predicate) {
Employee[] filteredEmployees = Arrays.stream(employees)
.filter(predicate)
.toArray(Employee[]::new);
if (filteredEmployees.length == 0) {
InputValidator.showErrorMessage("No hay empleados disponibles para esta selección.");
return null;
} else {
return selectEmployee(filteredEmployees);
}
}
private Employee selectFullTimeEmployee() {
return selectEmployee(T -> T instanceof FullTimeEmployee);
}
private Employee selectHoursEmployee() {
return selectEmployee(T -> T instanceof HourlyEmployee);
}
private void deleteEmployee() {
Employee employee = selectEmployee(employees);
for (int i = 0; i < employees.length; i++) {
if (employees[i] != null && employees[i].equals(employee)) {
employees[i] = null;
InputValidator.showInfoMessage("Empleado eliminado exitosamente.");
return;
}
}
InputValidator.showErrorMessage("Empleado no encontrado.");
}
private void addEmployee() {
boolean isFull = InputValidator.validateInput(
"¿El empleado es de tiempo completo? (s/n)",
Boolean.class);
Predicate<Double> positiveDouble = s -> s > 0;
String name = InputValidator.validateInput(
"Ingrese el nombre del empleado",
String.class, s -> !s.trim().isEmpty());
Department department = selectDepartment();
Employee employee = isFull ?
new FullTimeEmployee(name, department, InputValidator.validateInput(
"Ingrese el salario del empleado",
Double.class, positiveDouble)) :
new HourlyEmployee(name, department, InputValidator.validateInput(
"Ingrese la tarifa por hora del empleado",
Double.class, positiveDouble));
addEmployee(employee);
}
private void addEmployee(Employee employee) {
for (int i = 0; i < employees.length; i++) {
if (employees[i] == null) {
employees[i] = employee;
InputValidator.showInfoMessage("Empleado agregado exitosamente.");
return;
}
}
InputValidator.showErrorMessage("No se pueden agregar más empleados, la capacidad máxima ha sido alcanzada.");
}
private Department selectDepartment() {
return InputValidator.validateInput(
"Seleccione el departamento",
Department.values());
}
}
Conclusión
En este ejemplo, hemos implementado un sistema de gestión de empleados utilizando herencia para definir diferentes tipos de empleados y sobrecarga para manejar la adición de horas y beneficios. Además, hemos utilizado JOptionPane para validar la entrada del usuario y proporcionar una interfaz interactiva. Este enfoque nos permite gestionar eficientemente la información de los empleados y calcular sus salarios de manera flexible.
Ejemplo 4: Clase con funciones genéricas de validación
En este ejemplo, crearemos una clase `InputValidator` que contiene funciones genéricas para validar diferentes tipos de entradas, como números enteros, correos electrónicos y fechas. Estas funciones lanzarán excepciones personalizadas si la validación falla.
Actividad 1: Infografía sobre el proceso de compilación
Crea una infografía que ilustre el proceso de compilación de un programa.