Ejemplo de relaciones entre clases Java: Relación de Composición. Clase Persona y Clase Fecha

Una clase compuesta es una clase que tienen entre sus atributos una referencia a otra clase. En este ejercicio vamos a ver un ejemplo de composición de clases.
Vamos a crear dos clases:
Una clase Fecha con los atributos dia, mes y año, todos de tipo int.
Una clase Persona con los atributos: nombre (String), población (String) y fecha de nacimiento. El atributo fecha de nacimiento es de tipo Fecha. 
La clase Persona es una clase compuesta porque entre sus atributos hay uno, fechaNacimiento, que es una referencia a un objeto de otra clase, en concreto, es una atributo de tipo Fecha.
Las clases Persona y Fecha tienen por tanto una relación de composición.
En UML la composición de clases se representa mediante una línea con un rombo junto a la clase compuesta (la clase que contiene el atributo del tipo de la otra clase)
Para ver cómo se trabaja con clases compuestas realizaremos este programa:
Programa que pida por teclado los datos de una serie de personas y los guarde en un ArrayList. El número total de personas a introducir en el ArrayList se introduce por teclado.
A continuación se mostrará por pantalla lo siguiente:
  • Todas las personas contenidas en el ArrayList
  • La persona de mayor edad. Si hay varias con esa edad se mostrará la primera introducida.
  • Todas las personas que viven en una población que se introduce por teclado.
  • Número de personas mayores de edad.
Clase Fecha
Comenzamos creando la clase Fecha. Además de los constructores, métodos getters y setters, y método toTring, en esta clase escribiremos un método boolean esMenorQue(Fecha f) . El método devuelve true si la fecha que contiene el objeto es menor que la fecha que recibe como parámetro y false en caso contrario. Este método lo utilizaremos para saber cuál es la persona de mayor edad. Además en la clase Fecha escribiremos un método para comprobar si la fecha es correcta o no.
//Clase Fecha
public class Fecha {

    private int dia;
    private int mes;
    private int año;

    //Constructor por defecto
    public Fecha() {
    }

    //Constructor con parámetros
    public Fecha(int dia, int mes, int año) {
        this.dia = dia;
        this.mes = mes;
        this.año = año;
    }

    //Constructor copia
    public Fecha(Fecha fecha) {
        this.dia = fecha.dia;
        this.mes = fecha.mes;
        this.año = fecha.año;
    }

    //Métodos get y set
    public int getDia() {
        return dia;
    }

    public void setDia(int dia) {
        this.dia = dia;
    }

    public int getMes() {
        return mes;
    }

    public void setMes(int mes) {
        this.mes = mes;
    }

    public int getAño() {
        return año;
    }

    public void setAño(int año) {
        this.año = año;
    }

    //Método para comprobar si la fecha es correcta
    public boolean fechaCorrecta() {
        boolean diaCorrecto, mesCorrecto, añoCorrecto;
        añoCorrecto = this.año > 0;
        mesCorrecto = this.mes >= 1 && this.mes <= 12;
        switch (this.mes) {
            case 2 -> {
                if (esBisiesto()) {
                    diaCorrecto = this.dia >= 1 && this.dia <= 29;
                } else {
                    diaCorrecto = this.dia >= 1 && this.dia <= 28;
                }
            }
            case 4, 6, 9, 11 -> diaCorrecto = this.dia >= 1 && this.dia <= 30;
            default ->  diaCorrecto = this.dia >= 1 && this.dia <= 31;
        }
        return diaCorrecto && mesCorrecto && añoCorrecto;
    }

    //Método para comprobar si el año es bisiesto
    //Método privado invocado desde el método fechaCorrecta
    private boolean esBisiesto() {
        return (this.año % 4 == 0 && this.año % 100 != 0 || this.año % 400 == 0);                                 
    }

    //Método para comprobar si la fecha es menor que la que se recibe                                             
    public boolean esMenorQue(Fecha f) {
        if (this.año < f.año) {
            return true;
        }
        if (this.año == f.año && this.mes < f.mes) {
           return true;
        }
        return this.año == f.año && this.mes == f.mes && this.dia < f.dia;
    }

    //Método para mostrar la fecha
    //Devuelve un String con la fecha en formato dd-mm-aa
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("%02d-%02d-%02d", dia, mes, año));
        return sb.toString();
    }
} //Final de la Clase Fecha
Clase Persona
Una vez creada la clase Fecha se crea la clase Persona. Además de los constructores, métodos getters y setters y el método toString, en la clase Persona escribiremos un método boolean esMayorDeEdad() que utilizaremos para saber si la persona es mayor de edad o no lo es.
El método toString() de la clase Persona se escribirá de modo que los datos de las personas se muestren de esta forma:
Nombre: Adolfo Pérez Clavarana     
Población: Elche
Fecha Nacimiento: 12-07-1987
El atributo fechaNacimiento de la clase Persona representa un objteto de tipo Fecha, pero ese objeto no se crea cuando se escribe el atributo, es decir, no se escribe private Fecha fechaNacimiento = new Fecha();. Por norma general, los objetos que contiene una clase compuesta se crean e inicializan en el constructor. En los atributos de la clase se indica el nombre y tipo pero no se crea el objeto. En este caso, en los constructores de la clase Persona se crearán los objetos de tipo Fecha
import java.time.LocalDate;

//Clase Persona
public class Persona {
    private String nombre;
    private String poblacion;
    private Fecha fechaNacimiento;

    //Constructor por defecto
    public Persona() {
        fechaNacimiento = new Fecha();
    }

    //Constructor con todos los parámetros
    public Persona(String nombre, String poblacion, Fecha fechaNacimiento) {
        this.nombre = nombre;
        this.poblacion = poblacion;
        this.fechaNacimiento = new Fecha(fechaNacimiento);
    }
    
    //Métodos get y set
    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public String getPoblacion() {
        return poblacion;
    }

    public void setPoblacion(String poblacion) {
        this.poblacion = poblacion;
    }

    public Fecha getFechaNacimiento() {
        return fechaNacimiento;
    }

    public void setFechaNacimiento(Fecha fechaNacimiento) {
        this.fechaNacimiento = new Fecha(fechaNacimiento);
    }
    
    //Método para comprobar si la persona es mayor de edad
    public boolean esMayorDeEdad() {
        LocalDate hoy = LocalDate.now();
        int añoActual = hoy.getYear();
        int mesActual = hoy.getMonthValue();
        int diaActual = hoy.getDayOfMonth();
        if (añoActual - fechaNacimiento.getAño() > 18) {
            return true;
        } else if (añoActual - fechaNacimiento.getAño() == 18 &&
                   mesActual > fechaNacimiento.getMes()) {
            return true;
        } else if (añoActual - fechaNacimiento.getAño() == 18 &&
                   mesActual == fechaNacimiento.getMes() &&
                   diaActual >= fechaNacimiento.getDia()) {
            return true;
        }
        return false;
    }
    
    //Método para mostrar los datos de las personas según el formato pedido                                       
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Nombre: ");
        sb.append(nombre);
        sb.append("\nPoblación: ");
        sb.append(poblacion);
        sb.append("\nFecha de Nacimiento: ");
        sb.append(fechaNacimiento);
        return sb.toString();
    }
} //Final de la clase Persona
Clase Principal
Una vez creadas las clases Fecha y Persona, las utilizaremos en la clase principal donde se escriben los métodos necesarios para que el programa realice lo que pide el ejercicio.
El código de la clase principal será este:
import java.util.ArrayList;
import java.util.Scanner;

public class EjemploClaseCompuesta {

    //se crea el ArrayList de Personas
    static ArrayList<Persona> personas = new ArrayList<>();
    //creamos un Scanner para la lectura de datos por teclado
    static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {
        //se introducen las personas en el ArrayList
        leerPersonas();

        //se muestran todas las personas introducidas
        System.out.println("\nPersonas introducidas: ");
        mostrarPersonas();

        //se muestra la persona de mayor edad
        System.out.println("\nPersona de mayor edad: ");
        System.out.println(personaDeMayorEdad());

        //Se muestra el número de personas que viven en una población
        String poblacion;
        System.out.println("\nPersonas que viven en una población");
        System.out.print("Introduzca población: ");
        poblacion = sc.nextLine();
        System.out.println("\nNúmero de personas que viven en " + poblacion + ": "
                + cuantasPersonasVivenEn(poblacion));

        //se muestra el número de personas mayores de edad
        System.out.println("\nNúmero de personas mayores de edad : "
                + personasMayoresDeEdad());

    }

    //Método donde se crean los objetos Persona
    //se introducen sus datos por teclado
    //y se añaden los objetos al ArrayList
    public static void leerPersonas() {
        String nombre, poblacion;
        Fecha fecha;
        Persona p;
        int total, i, dia, mes, año;
        //se pide cuántas personas en total vamos a introducir
        do {
            System.out.print("Número de personas? ");
            total = sc.nextInt();
        } while (total < 1);
        sc.nextLine(); //limpiar el buffer de entrada

        //Bucle para crear las Personas, introducir sus datos por teclado 
        //y almacenarlas en el ArrayList
        for (i = 1; i <= total; i++) {
            System.out.println("Persona " + i);
            //se introduce por teclado el nombre de la persona
            System.out.print("Nombre: ");
            nombre = sc.nextLine();
            //se introduce por teclado la población
            System.out.print("Población: ");
            poblacion = sc.nextLine();

            //se introduce por teclado la fecha de nacimiento: dia, mes y año
            do {
                System.out.println("Fecha de Nacimiento: ");
                System.out.print("Dia: ");
                dia = sc.nextInt();
                System.out.print("Mes: ");
                mes = sc.nextInt();
                System.out.print("Año: ");
                año = sc.nextInt();
                //se crea un objeto Fecha
                fecha = new Fecha(dia, mes, año);
            } while (!fecha.fechaCorrecta());//se comprueba si la fecha es correcta

            sc.nextLine();//limpiar el buffer de entrada

            //se crea una nueva instancia de Persona mediante el constructor con parámetros
            p = new Persona(nombre, poblacion, fecha);
            //también se podría utilizar el constructor por defecto para crear el 
            //objeto Persona y después asignar los valores de los atributos
            //mediante los métodos set.
            
            //se añade el objeto al array
            personas.add(p);
        }
    }

    //Mostrar todas las personas
    public static void mostrarPersonas() {
        for (int i = 0; i < personas.size(); i++) {
            System.out.println(personas.get(i));
        }
    }

    //Método que devuelve la persona de mayor edad
    //En este método se utiliza el método esMenorQue() de la clase Fecha
    public static Persona personaDeMayorEdad() {
        Persona mayor = personas.get(0);
        for (int i = 1; i < personas.size(); i++) {
            if (personas.get(i).getFechaNacimiento().esMenorQue(mayor.getFechaNacimiento())) {                    
                mayor = personas.get(i);
            }
        }
        return mayor;
    }

    //Calcula y devuelve el número de personas que viven
    //en la ciudad que se recibe como parámetro
    public static int cuantasPersonasVivenEn(String ciudad) {
        int contador = 0;
        for (int i = 0; i < personas.size(); i++) {
            if (personas.get(i).getPoblacion().equalsIgnoreCase(ciudad)) {
                contador++;
            }
        }
        return contador;
    }

    //Calcula y devuelve el número de personas mayores de edad
    //En este método se utiliza el método esMayorDeEdad() de la clase Persona
    public static int personasMayoresDeEdad() {
        int contador = 0;
        for (int i = 0; i < personas.size(); i++) {
            if (personas.get(i).esMayorDeEdad()) {
                contador++;
            }
        }
        return contador;
    }
} //Fin de la Clase Principal


5 comentarios:

  1. Con este código, el método personaMayorEdad() me devuelve la persona más joven (fecha mayor)
    ******COPY&PASTE DE ARRIBA**********************
    //Devuelve la persona de mayor edad
    //En este método se utiliza el método esMayorQue() añadido a la clase Fecha
    public static Persona personaMayorEdad() {
    Persona aux = personas.get(0);
    for (int i = 1; i < personas.size(); i++) {
    if (personas.get(i).getFechaNacimiento().esMayorQue(aux.getFechaNacimiento())) {
    aux = personas.get(i);
    }
    }
    return aux;
    }
    ****************************
    He tenido que invertir la condición if
    aux.getFechaNacimiento().esMayorQue(personas.get(i).getFechaNacimiento())

    Para que me devuelva la persona de más edad.

    ResponderEliminar
  2. Excelente codigo,,, aunque se podria modificar para los statics.

    ResponderEliminar
  3. Un hospital desea un programa donde se genere el importe que se debe cobrar a los
    pacientes. El hospital ofrece los siguientes servicios:
    Consulta con médico general->$340 por consulta
    Consulta con médico especialista->$800 por consulta
    Curaciones->$200 por servicio
    El hospital es especial, hace sus cobros a los pacientes cada 8 días, por lo que se
    pueden acumular las consultas y curaciones.
    Las personas mayores de 60 años reciben un descuento del 10% en su importe total.
    Proyecto Hospital
    Clase clsCalculos:
    Método Subtotal(Recibe el número de consultas con médico general,
    número de consultas con médico especialista y número de curaciones) Retorna el
    subtotal
    Método Descuento(Recibe el subtotal ) retorna el importe de descuento
    NOTA: Este método es invocado solamente si el paciente es mayor de 60
    años

    ResponderEliminar
  4. Muy bien el código, el único inconveniente es con el método personaMayorEdad(). Solo me devuelve la fecha mayor, mas no la edad.

    ResponderEliminar