Hola.
A ver, para meterme en contexto, he comenzado a hacer el programa a mi manera, aunque no he podido terminarlo por completo todavía, porque ando escaso de tiempo.
Las clases principales apenas hay diferencias respecto a las tuyas.
Tenemos una clase Contrato, preferiblemente abstracta, de la que derivan dos clases: Consultor y Tecnico
Contrato
ublic abstract class Contrato implements Serializable {
protected String id;
protected String nombre;
protected LocalDate fechaInicio;
protected LocalDate fechaFin;
protected double salario;
public Contrato(String id, String nombre, double salario) {
this.id = id;
this.nombre = nombre;
this.salario = salario;
fechaInicio = LocalDate.now();
fechaFin = null;
}
public String getId() {
return id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public LocalDate getFechaInicio() {
return fechaInicio;
}
public LocalDate getFechaFin() {
return fechaFin;
}
public void darDeBaja() {
fechaFin = LocalDate.now();
}
public double getSalario() {
return salario;
}
public void setSalario(double salario) {
this.salario = salario;
}
@Override
public String toString() {
return String.format("ID: %s, Nombre: %s, Salario: %.2f\nInicio: %s, Fin :%s",
id, nombre, salario, fechaInicio.toString(), fechaFin==null?"":fechaFin.toString());
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Contrato) {
Contrato otroContrato = (Contrato) obj;
return id.equals(otroContrato.id);
}
else
return false;
}
}
Consultor
public final class Consultor extends Contrato {
private String labor;
public Consultor(String id, String nombre, double salario, String labor) {
super(id, nombre, salario);
this.labor = labor;
}
public String getLabor() {
return labor;
}
public void setLabor(String labor) {
this.labor = labor;
}
@Override
public String toString() {
return String.format("%s\nLabor: %s", super.toString(), labor);
}
}
Tecnico
public class Tecnico extends Contrato {
private int proyectosLiderados;
public Tecnico(String id, String nombre, double salario) {
super(id, nombre, salario);
proyectosLiderados = 0;
}
public int getProyectosLiderados() {
return proyectosLiderados;
}
public void contarProyectoLiderado() {
proyectosLiderados++;
}
@Override
public double getSalario() {
double salario = super.getSalario();
double bonus = salario * 5 / 100;
//Incremento del 5% por cada proyecto liderado
return salario + bonus * proyectosLiderados;
}
@Override
public String toString() {
return String.format("%s\nProyectos Liderados: %d",
super.toString(), proyectosLiderados);
}
}
De esta última clase, derivan otras dos subclases:
Programador
public final class Programador extends Tecnico {
private String lenguaje;
public Programador(String id, String nombre, double salario, String lenguaje) {
super(id, nombre, salario);
this.lenguaje = lenguaje;
}
@Override
public double getSalario() {
//Incrementa 20% si es experto en Java
if (lenguaje.equals("Java"))
return super.getSalario() + (super.getSalario() * 20 / 100);
else
return super.getSalario();
}
@Override
public String toString() {
return String.format("%s\nLenguaje: %s", super.toString(), lenguaje);
}
}
Lider
public class Lider extends Tecnico {
private ArrayList<Tecnico> equipo;
public Lider(String id, String nombre, double salario) {
super(id, nombre, salario);
equipo = new ArrayList<Tecnico>();
}
public boolean agregarTecnico(Tecnico tec) {
if (equipo.contains(tec)) {
System.out.println("Este Técnico ya forma parte del Equipo");
return false;
}
else {
equipo.add(tec);
System.out.println("Técnico añadido al Equipo");
return true;
}
}
@Override
public String toString() {
StringBuilder resumen = new StringBuilder();
resumen.append("\n\tDATOS LIDER");
resumen.append("\nID: " + id);
resumen.append(", Nombre: " + nombre);
resumen.append("\n\tEQUIPO TECNICO");
for (Tecnico tec: equipo)
resumen.append("\n- " + tec.getNombre());
return resumen.toString();
}
}
Luego, además de los Contratos y sus subclases, tenemos la clase Proyecto.
public class Proyecto {
private String id;
private String nombre;
private Lider lider;
private LocalDate fechaInicio;
private LocalDate fechaFin;
public Proyecto(String id, String nombre, Lider lider) {
this.id = id;
this.nombre = nombre;
this.lider = lider;
fechaInicio = LocalDate.now();
fechaFin = null;
}
public String getId() {
return id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public Lider getLider() {
return lider;
}
public void setLider(Lider lider) {
this.lider = lider;
}
public void finalizarProyecto() {
fechaFin = LocalDate.now();
}
public boolean estaFinalizado() {
return fechaFin != null;
}
@Override
public String toString() {
StringBuilder resumen = new StringBuilder();
resumen.append("\n\tPROYECTO #" + id);
resumen.append("\nNombre: " + nombre);
resumen.append("\nFecha Inicio: " + fechaInicio.toString());
resumen.append("\tFecha Fin: " + (estaFinalizado()?fechaFin.toString():"ACTIVO"));
resumen.append(lider.toString());
return resumen.toString();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Proyecto) {
Proyecto otroProyecto = (Proyecto) obj;
return id.equals(otroProyecto.id);
}
else
return false;
}
}
Así que nuestro programa ha de gestionar Contratos y Proyectos, con una serie de menús y con la posibilidad de guardar/recuperar datos en disco.
Bien, aquí ya sí que hemos tomado caminos distintos tú y yo.
En mi caso he pensado en crear clases específicas para la gestión de cada cosa.
Una clase GestionContratos, con un ArrayList de contratos y con todos los métodos necesarios para su gestión.
No la he terminado, pero ya da una idea de la estructura que le voy a dar:
public class GestorContratos implements Serializable {
private ArrayList<Contrato> contratos;
public GestorContratos() {
contratos = new ArrayList<Contrato>();
}
public void registrarConsultor() {
Consultor cons = nuevoConsultor();
if (contratos.contains(cons))
System.out.println("Ya existe un Contrato con el ID: " + cons.getId());
else {
contratos.add(cons);
System.out.println("Consultor registrado");
}
}
public void registrarProgramador() {
Programador prog = nuevoProgramador();
if (contratos.contains(prog))
System.out.println("Ya existe un Contrato con el ID: " + prog.getId());
else {
contratos.add(prog);
System.out.println("Programador registrado");
}
}
public void finalizarContrato() {
Scanner teclado = new Scanner(System.in);
System.out.print("ID del contrato: ");
String id = teclado.nextLine();
boolean encontrado = false;
for (Contrato cont: contratos)
if (cont.getId().equals(id)) {
cont.darDeBaja();
encontrado = true;
}
if (encontrado)
System.out.println("Contrato finalizado con exito");
else
System.out.println("No se encuentra Contrato con el ID: " + id);
}
public void listarContratosActivos() {
System.out.println("\n\tCONTRACTOS ACTIVOS\n");
for (Contrato cont: contratos)
if (cont.fechaFin == null)
System.out.println(cont);
}
private Consultor nuevoConsultor() {
Scanner teclado = new Scanner(System.in);
System.out.print("Id: ");
String id = teclado.nextLine();
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Salario: ");
double salario = Double.parseDouble(teclado.nextLine());
System.out.print("Labor en la empresa: ");
String labor = teclado.nextLine();
return new Consultor(id, nombre, salario, labor);
}
private Programador nuevoProgramador() {
Scanner teclado = new Scanner(System.in);
System.out.print("Id: ");
String id = teclado.nextLine();
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Salario: ");
double salario = Double.parseDouble(teclado.nextLine());
System.out.print("Lenguaje programación: ");
String lenguaje = teclado.nextLine();
return new Programador(id, nombre, salario, lenguaje);
}
}
Y una clase similar, para la gestión de proyectos.
Esta ni siquiera la he empezado, pero su estructura será análoga a la anterior:
public class GestorProyectos implements Serializable {
private ArrayList<Proyecto> proyectos;
public GestorProyectos() {
proyectos = new ArrayList<Proyecto>();
}
/*
* Faltan todos los métodos necesarios para gestionar proyectos
*/
}
Estos dos gestores, puesto que contienen los ArrayList, serán los que nos interesen poder guardar en disco.
Y no solo hay que guardarlos a ellos, también hay que guardar al Lider que ha sido nombrado como director general de la empresa.
Para facilitar el guardado de todo esto, he creado otra clase donde agrupo estos tres elementos con el único propósito de luego poder guardarlo todo en disco como un único objeto.
Así que a priori, no tendrá métodos ni nada. Básicamente es una especie de "contenedor".
La he llamado RecursosHumanos.
Fíjate que el director se lo paso por constructor, esto es porque dicho Lider lo construiremos desde otra clase.
public class RecursosHumanos implements Serializable {
GestorContratos contratos;
GestorProyectos proyectos;
final Lider director;
public RecursosHumanos(Lider director) {
contratos = new GestorContratos();
proyectos = new GestorProyectos();
this.director = director;
}
}
Y ya por último, tendría la clase principal, con un método main para poner en marcha la aplicación, y será aquí donde se mostrarán distintos menús, se pedirán entradas por teclado, se le pedirá a las clases de gestión que hagan una cosa o la otra y también se controlará la persistencia de los datos.
Al iniciarse la aplicación, lo que hará es buscar el archivo de datos guardados y recuperarlo, por lo que obtendremos un objeto RecursosHumanos con el director general y los gestores(y sus ArrayList)
Si no lo encuentra, o bien no consigue recuperar los datos que contiene, se creará un nuevo Director General y los ArrayList se inicializarán sin datos.
Esta clase tampoco la he terminado, pero tiene suficiente código para ver como se van a estructurar los menús, como llamamos al objeto RecursosHumanos para acceder a sus gestores y sobre todo vemos como se guarda y se recupera en disco.
public final class Empresa {
private static Scanner teclado = new Scanner(System.in);
private static RecursosHumanos RRHH;
public static void main(String[] args) {
File fichero = new File("d:/empresa.dat");
if (fichero.exists()) {
try {
ObjectInputStream lector = new ObjectInputStream(new FileInputStream(fichero));
RRHH = (RecursosHumanos) lector.readObject();
lector.close();
} catch (Exception e) {
System.out.println("Error recuperando datos de empresa. Se generará una nueva.\n");
RRHH = new RecursosHumanos(crearDirector());
}
}
else //No hay datos de Empresa guardados
RRHH = new RecursosHumanos(crearDirector());
menuPrincipal();
}
private static Lider crearDirector() {
System.out.println("Indique a continuación los datos del Técnico que será Dir. General de la Empresa");
System.out.print("Id: ");
String id = teclado.nextLine();
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Salario: ");
double salario = Double.parseDouble(teclado.nextLine());
return new Lider(id, nombre, salario);
}
private static void menuPrincipal() {
int opcion = 0;
while(opcion != 5) {
System.out.println("\n\n\t\tMENU PRINCIPAL");
System.out.println("\t\t---- ---------\n");
System.out.println("[1] -- Gestion Contratos");
System.out.println("[2] -- Gestion Nominas");
System.out.println("[3] -- Gestion Proyectos");
System.out.println("[4] -- Guardar Datos");
System.out.println("[5] -- TERMINAR PROGRAMA");
System.out.print("Opcion: ");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
menuContratos();
break;
case 2:
//menuNominas();
break;
case 3:
//menuProyectos();
break;
case 4:
guardarDatos();
break;
case 5:
System.out.println("\n\n\t\tFIN DE PROGRAMA");
break;
default:
System.out.println("Opcion equivocada");
}
}
}
private static void menuContratos() {
int opcion = 0;
while (opcion != 8) {
System.out.println("\n\n\t\tGESTION CONTRATOS");
System.out.println("\t\t------- ---------\n");
System.out.println("[1] -- Nuevo Contrato: Consultor");
System.out.println("[2] -- Nuevo Contrato: Programador");
System.out.println("[3] -- Dar de baja un Contrato");
System.out.println("[4] -- Listar Contratos activos");
System.out.println("[5] -- Listar Contratos finalizados");
System.out.println("[6] -- Listar Estructura Jerárquica");
System.out.println("[7] -- Mostrar datos de un Lider");
System.out.println("[8] -- Volver a Menú Principal");
System.out.print("Opcion: ");
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
RRHH.contratos.registrarConsultor();
break;
case 2:
RRHH.contratos.registrarProgramador();
break;
case 3:
RRHH.contratos.finalizarContrato();
break;
case 4:
RRHH.contratos.listarContratosActivos();
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
//Opcion para volver al menu principal, nada que hacer aquí
break;
default:
System.out.println("Opcion equivocada");
}
}
}
private static void guardarDatos() {
File fichero = new File("d:/empresa.dat");
try {
if (!fichero.exists())
fichero.createNewFile();
ObjectOutputStream escritor = new ObjectOutputStream(new FileOutputStream(fichero));
escritor.writeObject(RRHH);
escritor.close();
} catch (Exception e) {
System.out.println("No ha sido posible guardar los datos.\n" + e.getMessage());
e.printStackTrace();
}
}
}
En tu caso parece que has tirado por caminos más complejos.
Parece que los menús quieres tratarlos también como objetos, en lugar de hacer simples métodos.
Aunque lo cierto es que es un camino interesante, quizás esa complejidad luego proporcione alguna ventaja..., no lo se...
Y tu forma de guardar/recuperar datos si que me parece complicada en exceso.
Para salvar los datos también haces una clase, que hereda de la clase menus (¿por qué una "acción de guardar" es un "menú"?) y además implementa la interfaz Serializable, lo cuál es innecesario porque no creo que vayas a guardar en disco un objeto Salvar_Datos
La interfaz Serializable es para las clases que van a ser guardados en disco.
Y por otro lado, no TODO tiene por qué ser una clase.
Las clases normalmente se usan para modelar una entidad o un concepto: un contrato, un proyecto, un perro, una persona, una escuela, un hotel, una factura, un teatro, etc...
Acciones como mostrar un menú o guardar datos en disco, en realidad son funcionalidades y pueden resolverse mediante métodos (que por eso también se le llaman "funciones") escritos en la clase principal o tal vez en alguna otra clase.
Pueden resolverse creando clases, por supuesto, pero luego puede ser más difícil buscar la manera de encajarlos en el proyecto y que se comuniquen con el resto de clases que intervienen.
En fin, revisa el camino que yo he tomado. Decide tú si te parece mejor, o si quizás te da alguna inspiración para lograr resolver mejor tu camino.
Insisto en que no he terminado el programa, así que luego pueden salir errores o necesidad de cambiar algo de lo que ya tengo hecho.
Pero en esencia, el programa ya ha quedado estructurado.
Pregunta lo que no haya quedado claro.
Un saludo.