Ejercicio Clase compuesta en Java: clase Canción

Crea un proyecto Java llamado MiMusica. El programa permitirá dar de alta canciones y almacenarlas en un ArrayList. Además mostrará las canciones contenidas en el ArrayList ordenadas según distintos criterios de ordenación.
Además de la clase principal, las clases que intervienen en el proyecto son las siguientes:
  • Clase Intérprete. Los atributos de la clase son: nombre (String), solista (boolean). El atributo solista contendrá true si el intérprete es solista y false si no lo es. 
  • Clase Tiempo. Los atributos de la clase son: minutos (int) y segundos (int).
  • Clase Canción. Los atributos de la clase son: título (String), intérprete (de tipo Intérprete) y duración (de tipo Tiempo). La clase Canción es una clase compuesta ya los atributos intérprete y duración son objetos de tipo Interprete y Tiempo respectivamente.
Las clases Intérprete, Tiempo y Canción tendrán un método leer() para introducir por teclado los valores de sus atributos.
Además de las clases anteriores, el proyecto tendrá una clase Menú. La clase Menú tendrá un método mostrar() para mostrar las opciones de  menú y un método leer() que pedirá al usuario la opción de menú que quiere ejecutar.
Las opciones de menú que se mostrarán serán las siguientes:
1. Nueva cancion
2. Mostrar canciones        
0. Salir
En la clase Principal, el método main será el encargado de mostrar el menú y ejecutar el método correspondiente a la opción de menú seleccionada. El programa finaliza cuando se introduzca 0 como opción de menú.
El código del método main será este:
public static void main(String[] args) {
        Menu menu = new Menu();
        int op;
        do {
            menu.mostrar();
            op = menu.leer();
            switch (op) {
                case 1 -> nuevaCancion();
                case 2 -> mostrarCanciones();                                                                     
            }
        } while (op != 0);
}
Métodos en la clase principal correspondientes a cada opción del menú:
Opción 1: Método nuevaCancion()
En este método se crea una nueva canción, se piden todos sus datos por teclado y se añade al ArrayList canciones teniendo en cuenta que no puede haber canciones repetidas en el ArrayList. Una canción se considera igual a otra cuando tiene el mismo título, el mismo intérprete y la misma duración. Si en el ArrayList ya hay una canción igual a la que se intenta añadir se muestra un mensaje indicándolo y se vuelve al menú.
Opción 2: Método mostrarCanciones()
En este método se muestran todas las canciones ordenadas de distintas formas:
  • Todas las canciones ordenadas alfabéticamente por título
  • A continuación muestra todas las canciones ordenadas por duración de menor a mayor
  • Finalmente muestra todas las canciones ordenadas alfabéticamente por intérprete
SOLUCIÓN
Empezamos creando las clases Interprete y Tiempo. Una vez creadas escribiremos la clase Canción. La clase Canción es la clase compuesta ya que contiene un atributo de tipo Interprete y otro de tipo Tiempo.
import java.util.Objects;
import java.util.Scanner;

//Clase Intérprete
public class Interprete {

    private String nombre;
    private boolean solista;

    //constructor por defecto
    public Interprete() {
    }

    //constructor con parámetros
    public Interprete(String nombre, boolean solista) {
        this.nombre = nombre;
        this.solista = solista;
    }

    //constructor copia
    public Interprete(Interprete i) {
        this.nombre = i.nombre;
        this.solista = i.solista;
    }

    //métodos getters y setters
    public String getNombre() {
        return nombre;
    }

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

    public boolean isSolista() {
        return solista;
    }

    public void setSolista(boolean solista) {
        this.solista = solista;
    }

    //método para introducir por teclado valores a los atributos
    public void leer() {
        Scanner sc = new Scanner(System.in);
        int tipo;
        do {
            System.out.print("Nombre intérprete: ");
            nombre = sc.nextLine();
        } while (nombre.isBlank());
        do {
            System.out.print("Solista? 1-> SI 2-> NO: ");
            tipo = sc.nextInt();
        } while (tipo != 1 && tipo != 2);
        solista = tipo == 1;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 79 * hash + Objects.hashCode(this.nombre);
        hash = 79 * hash + (this.solista ? 1 : 0);
        return hash;
    }

    //método para determinar si dos objetos Intérprete son iguales
    //Dos objetos de tipo Intérprete son iguales si sus atributos solista y nombre son iguales                    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Interprete other = (Interprete) obj;
        if (this.solista != other.solista) {
            return false;
        }
        return this.nombre.equalsIgnoreCase(other.nombre);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(nombre);
        sb.append(" Solista: ");
        sb.append((solista) ? "SI" : "NO");
        return sb.toString();
    }

}//Fin clase Interprete
import java.util.Scanner;

//Clase Tiempo
public class Tiempo {

    private int minutos;
    private int segundos;

    //constructor por defecto
    public Tiempo() {
    }

    //constructor con parámetros
    public Tiempo(int minutos, int segundos) {
        this.minutos = minutos;
        this.segundos = segundos;
    }

    //constructor copia
    public Tiempo(Tiempo t) {
        this.minutos = t.minutos;
        this.segundos = t.segundos;
    }

    //métodos getters y setters
    public int getMinutos() {
        return minutos;
    }

    public void setMinutos(int minutos) {
        this.minutos = minutos;
    }

    public int getSegundos() {
        return segundos;
    }

    public void setSegundos(int segundos) {
        this.segundos = segundos;
    }

    //método para introducir por teclado valores a los atributos
    public void leer() {
        Scanner sc = new Scanner(System.in);
        do {
            System.out.print("minutos: ");
            minutos = sc.nextInt();
        } while (minutos < 0);
        do {
            System.out.print("segundos: ");
            segundos = sc.nextInt();
        } while (segundos < 0);
    }

    //método para obtener el tiempo en su equivalente en segundos
    public int pasarASegundos() {
        return minutos * 60 + segundos;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 29 * hash + this.minutos;
        hash = 29 * hash + this.segundos;
        return hash;
    }

    //método para determinar si dos objetos Tiempo son iguales
    //Dos objetos de la clase Tiempo son iguales si sus minutos y sus segundos son iguales                        
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Tiempo other = (Tiempo) obj;
        if (this.minutos != other.minutos) {
            return false;
        }
        return this.segundos == other.segundos;
    }

    //Se obtiene el tiempo con formato mm:ss
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("%02d:%02d", minutos, segundos));
        return sb.toString();
    }
}//Fin clase Tiempo
import java.util.Objects;
import java.util.Scanner;

//Clase Canción
public class Cancion implements Comparable<Cancion> {

    private String titulo;
    private Interprete interprete;
    private Tiempo duracion;

    //constructor por defecto
    public Cancion() {
        interprete = new Interprete();
        duracion = new Tiempo();
    }

    //constructor con parámetros
    public Cancion(String titulo, Interprete interprete, Tiempo duracion) {
        this.titulo = titulo;
        this.interprete = new Interprete(interprete);
        this.duracion = new Tiempo(duracion);
    }

    //métodos getters y setters
    public String getTitulo() {
        return titulo;
    }

    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    public Interprete getInterprete() {
        return interprete;
    }

    public void setInterprete(Interprete interprete) {
        this.interprete = interprete;
    }

    public Tiempo getDuracion() {
        return duracion;
    }

    public void setDuracion(Tiempo duracion) {
        this.duracion = duracion;
    }

    //método para introducir por teclado valores a los atributos
    public void leer() {
        Scanner sc = new Scanner(System.in);
        do {
            System.out.print("Título: ");
            titulo = sc.nextLine();
        } while (titulo.isEmpty());
        interprete.leer(); //se invoca al método leer de Interprete
        duracion.leer(); //se invoca al métod leer de Tiempo
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + Objects.hashCode(this.titulo);
        hash = 97 * hash + Objects.hashCode(this.interprete);
        hash = 97 * hash + Objects.hashCode(this.duracion);
        return hash;
    }

    //método para determinar si dos objetos Cancion son iguales
    //Dos objetos de la clase Cancion son iguales si tienen  
    //el mismo título, el mismo intérprete y la misma duración
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Cancion other = (Cancion) obj;
        if (!this.titulo.equalsIgnoreCase(other.titulo)) {
            return false;
        }
        if (!this.interprete.equals(other.interprete)) { //se invoca al método equals de Intérprete               
            return false;
        }
        return this.duracion.equals(other.duracion); //se invoca al método equals de Tiempo
        
        //como intérprete y duración son objetos
        //para comprobar si el intérprete o la duración de dos canciones son iguales
        //las clases Interprete y Tiempo deben contener también un método equals
        //donde se indique cuando dos intérpretes o dos duraciones son iguales 
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(titulo);
        sb.append(" ");
        sb.append(interprete);
        sb.append(" ");
        sb.append(duracion);
        return sb.toString();
    }

    //se indica que el orden natural de los objetos es por el título de la canción
    @Override
    public int compareTo(Cancion o) {
        return this.titulo.compareToIgnoreCase(o.titulo);
    }
}//Fin clase Canción
import java.util.Scanner;

//Clase Menú
public class Menu {

    private int opcion;

    public void mostrar() {
        System.out.println("1. Nueva cancion");
        System.out.println("2. Mostrar canciones");                                                               
        System.out.println("0. Salir");
    }

    public int leer() {
        Scanner sc = new Scanner(System.in);
        do {
            System.out.print("Opcion: ");
            opcion = sc.nextInt();
        } while (opcion < 0 || opcion > 2);
        return opcion;
    }
}//Fin clase Menu
Clase Principal
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

//Clase Principal
public class MiMusica {

    static ArrayList<Cancion> canciones = new ArrayList<>();

    //método main
    public static void main(String[] args) {
        Menu menu = new Menu();
        int op;
        do {
            menu.mostrar();
            op = menu.leer();
            switch (op) {
                case 1 -> nuevaCancion();
                case 2 -> mostrarCanciones();
            }
        } while (op != 0);
    }

    //método que crea una canción nueva y la añade al ArrayList
    //antes de añadirla se comprueba si la canción ya existe en el ArrayList
    public static void nuevaCancion() {
        Cancion c = new Cancion();
        c.leer();
        if (canciones.contains(c)) {
            System.out.println("Canción repetida");
        } else {
            canciones.add(c);
        }
        //el método contains comprueba si el ArrayList contiene alguna canción
        //igual a c. Para que esto funcione y el método contains detecte canciones iguales
        //en el ArrayList, la clase Canción debe contener un método equals donde
        //indiquemos cuando se considera que dos canciones son iguales
    }

    //método para mostrar las canciones según distintos criterios de ordenación
    public static void mostrarCanciones() {
        //mostrar las canciones ordenadas por título
        //el orden por título se indica en el método compareTo en la clase Cancion
        Collections.sort(canciones);
        for (Cancion c : canciones) {
            System.out.println(c);
        }
        
        //mostrar las canciones ordenadas por duración
        //el orden por duración lo indicamos en el método compare del Comparator
        Collections.sort(canciones, new Comparator<Cancion>() {
            @Override
            public int compare(Cancion o1, Cancion o2) {
                return o1.getDuracion().pasarASegundos() - o2.getDuracion().pasarASegundos();
            }

        });
        for (Cancion c : canciones) {
            System.out.println(c);
        }
        
        //mostrar las canciones ordenadas alfabéticamente por nombre del intérprete
        //el orden lo indicamos en el método compare del Comparator
        Collections.sort(canciones, new Comparator<Cancion>() {
            @Override
            public int compare(Cancion o1, Cancion o2) {
               return o1.getInterprete().getNombre().compareToIgnoreCase(o2.getInterprete().getNombre());         
            }

        });
        for (Cancion c : canciones) {
            System.out.println(c);
        }
    }
    
}//Fin Clase Principal
Por último indicar que en el método mostrarCanciones() para mostrar las canciones ordenadas por duración y por nombre del intérprete se ha utilizado la interface Comparator y en el método compare se ha indicado el criterio de ordenación. Con la incorporación de las expresiones lambda al lenguaje Java, podemos expresar el criterio de ordenación mediante lambdas haciendo el código del Comparator mucho más sencillo. Ambas opciones son válidas.
El método mostrarCanciones() utilizando expresiones lambda para indicar el criterio de ordenación sería este:
public static void mostrarCanciones() {
        //mostrar las canciones ordenadas por título
        //el orden por título se indica en el método compareTo en la clase Cancion
        Collections.sort(canciones);
        for (Cancion c : canciones) {
            System.out.println(c);
        }
        
        //mostrar las canciones ordenadas por duración
        //el orden por duración lo indicamos en la expresión lambda
        Collections.sort(canciones, 
          (Cancion o1, Cancion o2) -> o1.getDuracion().pasarASegundos() - o2.getDuracion().pasarASegundos());
        
        for (Cancion c : canciones) {
            System.out.println(c);
        }
        
        //mostrar las canciones ordenadas alfabéticamente por nombre del intérprete
        //el orden lo indicamos lo indicamos en la expresión lambda
        Collections.sort(canciones, (Cancion o1, Cancion o2) -> 
                        o1.getInterprete().getNombre().compareToIgnoreCase(o2.getInterprete().getNombre()));      
        
        for (Cancion c : canciones) {
            System.out.println(c);
        }
}


No hay comentarios:

Publicar un comentario