Ejercicio Resuelto POO Java: Clase Biblioteca

Ejercicio POO Java. Clases compuestas. Clase Biblioteca

Crea un proyecto Java llamado ProyectoBiblioteca. El programa simulará el funcionamiento básico de una biblioteca: dar de alta libros, mostrar libros, realizar préstamos y devoluciones de libros, dar libros de baja, mostrar los libros que están disponibles en la biblioteca para prestar, etc.
El proyecto contendrá las siguientes clases:
Clase Libro
Los atributos de la clase son: 
  • referencia (String)
  • título (String)
  • autor (String)
  • ejemplares (int) Este atributo contiene el número total de ejemplares del libro que hay en la biblioteca
  • prestados(int) Este atributo contiene el número de ejemplares del libro que se encuentran prestados. 
Además de los constructores, métodos getters-setters y el método toString, la clase Libro contendrá los siguientes métodos:
  • Método préstamo para realizar el préstamo de un libro. El método incrementa el atributo prestados cada vez que se realice un préstamo del libro. Se debe comprobar que haya ejemplares disponibles del libro para prestar. No se podrán prestar libros de los que no haya ejemplares disponibles. El método devuelve true si se ha podido realizar el préstamo y false en caso contrario.
  • Método devolución para realizar la devolución de un libro que se encuentre prestado. El método decrementa el atributo prestados cuando se produzca la devolución de un libro. No se podrán devolver libros que no se hayan prestado. Devuelve true si se ha podido realizar la operación y false en caso contrario. 
  • Método estáDisponible que compruebe si hay ejemplares disponibles del libro para prestar. Devuelve true si hay ejemplares del libro para prestar. Devuelve false en caso contrario.
  • Método ejemplaresDisponibles que devuelva el número de ejemplares del libro que hay disponibles para prestar.
Añade a la clase Libro los métodos que consideres necesarios.
Clase Biblioteca
Los atributos de la clase son:
  • Un array llamado libros. Se trata de un array de objetos de tipo Libro. 
  • Un atributo de tipo int llamado índice. Este atributo contendrá la siguiente posición libre dentro del array libros. 
La clase biblioteca contendrá los siguientes métodos: 
  • Constructor por defecto que crea el array de libros de 100 elementos.
  • Método para añadir un nuevo libro en la biblioteca. El método recibe el libro que queremos añadir en la biblioteca. El libro se añade a continuación del último libro añadido en el array siempre que haya espacio. El método devuelve true si el libro se ha añadido a la biblioteca. Devuelve false si no se ha podido añadir por falta de espacio. 
  • Método para eliminar un libro por referencia. El método recibe la referencia del libro que queremos eliminar de la biblioteca y si existe lo elimina. Eliminar un libro del array significa desplazar un lugar a la izquierda el resto de libros que se encuentran a continuación éste. El método devuelve true si se ha eliminado el libro y false en caso contrario.
  • Método para eliminar un libro por posición. El método recibe la posición dentro del array del libro que queremos eliminar de la biblioteca y lo elimina de la misma forma que en el método anterior. Se debe comprobar que la posición recibida sea válida. El método devuelve true si se ha eliminado el libro y false en caso contrario.
  • Método que devuelva la cantidad de libros disponibles en la biblioteca.
  • Método para saber si un libro se encuentra en la biblioteca. El método recibe la referencia del libro que buscamos. Devuelve true si el libro se encuentra en la biblioteca y false en caso contrario.
  • Método que recibe una referencia y devuelve el libro con esa referencia. Devuelve null si el libro no existe en la biblioteca.
  • Método toString para mostrar todos los libros de la biblioteca.
Añade a la clase Biblioteca los métodos que consideres necesarios.
Clase Menú
Además de las clases anteriores, el proyecto contendrá 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. Nuevo libro
2. Mostrar todos los libros
3. Buscar libros
4. Bajas de libros por referencia          
5. Bajas de libros por posición
6. Préstamo
7. Devolución
8. Mostrar libros disponibles
0. FIN
Clase Principal

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ú.
Métodos en la clase principal correspondientes a cada opción del menú:
Opción 1: Método libroNuevo()
En este método se crea nuevo libro, se introducen todos sus datos por teclado y se añade a la biblioteca. Debemos tener en cuenta que no se puede haber libros con la misma referencia. Si la biblioteca ya contiene un libro con esa referencia se muestra un mensaje indicándolo y se vuelve al menú.
Opción 2: Método mostrarLibros()
En este método se muestran todos los libros ordenados alfabéticamente por título.
Opción 3: Método buscarLibros()
Se introduce por teclado la referencia de un libro y se muestra por pantalla el libro correspondiente. Si la referencia no existe en la biblioteca se muestra un mensaje indicándolo
Opción 4: Método darDeBajaLibroPorReferencia()
Se introduce por teclado la referencia de un libro y se elimina de la biblioteca. Si la referencia no existe en la biblioteca se muestra un mensaje indicándolo
Opción 5: Método darDeBajaLibroPorPosicion()
Se introduce por teclado la posición del libro dentro del array de libros y se elimina. Si la posición no es válida se muestra un mensaje indicándolo
Opción 6: Método realizarPrestamo()
Se introduce por teclado la referencia del libro y se realiza el préstamo. Si la referencia no existe o no hay ejemplares disponibles para prestar se muestra un mensaje indicándolo.
Opción 7: Método realizarDevolucion()
Se introduce por teclado la referencia del libro y se realiza la devolución. Si la referencia no existe o no hay ejemplares prestados de ese libro se muestra un mensaje indicándolo.
Opción 8: Método mostrarLibrosDisponibles()
Se muestran por pantalla todos los libros disponibles para prestar. El listado de libros se muestra ordenado alfabéticamente por título
SOLUCIÓN
Clase Libro
import java.util.Objects;

//Clase Libro Java
public class Libro implements Comparable<Libro>{

    private String referencia;
    private String titulo;
    private String autor;
    private int ejemplares;
    private int prestados;
   
    //constructor por defecto
    public Libro() {
    }

    //constructor con parámetros
    public Libro(String referencia, String titulo, String autor, int ejemplares, int prestados) {
        this.referencia = referencia;
        this.titulo = titulo;
        this.autor = autor;
        this.ejemplares = ejemplares;
        this.prestados = prestados;
    }

    //constructor copia
    public Libro(Libro l) {
        this.referencia = l.referencia;
        this.titulo = l.titulo;
        this.autor = l.autor;
        this.ejemplares = l.ejemplares;
        this.prestados = l.prestados;
    }

    //métodos getters-setters
    public String getAutor() {
        return autor;
    }

    public void setAutor(String autor) {
        this.autor = autor;
    }

    public int getEjemplares() {
        return ejemplares;
    }

    public void setEjemplares(int ejemplares) {
        this.ejemplares = ejemplares;
    }

    public int getPrestados() {
        return prestados;
    }

    public void setPrestados(int prestados) {
        this.prestados = prestados;
    }

    public String getTitulo() {
        return titulo;
    }

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

    public String getReferencia() {
        return referencia;
    }

    public void setReferencia(String referencia) {
        this.referencia = referencia;
    }

    //método para realizar el préstamo del libro
    //realizar el préstamo de un libro significa incrementar su atributo prestados 
    //antes de realizar el préstamo se comprueba si hay ejemplares disponibles
    //devuelve true si se ha realizado el préstamo. 
    //devuelve false si no se ha realizado
    public boolean prestamo() {
        if (estaDisponible()) {
            prestados++;
            return true;
        }
        return false;
    }

    //método para realizar la devolución del libro
    //realizar la devolución de un libro significa decrementar su atributo prestados
    //antes de realizar la devolución se comprueba si hay ejemplares prestados del libro
    //devuelve true si se ha realizado la devolución
    //devuelve false si no se ha podido realizar
    public boolean devolucion() {
        if (prestados == 0) {
            return false;
        }
        prestados--;
        return true;
    }

    //método que comprueba si hay ejemplares del libro disponbles para prestar
    public boolean estaDisponible() {
        return prestados < ejemplares;
    }

    //método que devuelve cuántos ejemplares del libro hay disponibles para prestar
    public int ejemplaresDisponibles() {
        return ejemplares - prestados;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Referencia: ");
        sb.append(referencia);
        sb.append("\nTitulo: ");
        sb.append(titulo);
        sb.append("\nAutor: ");
        sb.append(autor);
        sb.append("\nEjemplares: ");
        sb.append(ejemplares);
        sb.append("\nPrestados: ");
        sb.append(prestados);
        return sb.toString();
    }

    //método para indicar el orden alfabético por título del libro
    @Override
    public int compareTo(Libro o) {
        return this.titulo.compareToIgnoreCase(o.titulo);
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 23 * hash + Objects.hashCode(this.referencia);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Libro other = (Libro) obj;
        return this.referencia.equalsIgnoreCase(other.referencia);                                                
    }
    
}//Fin clase Libro
Clase Biblioteca
import java.util.Arrays;

//Clase Biblioteca Java
public class Biblioteca {

    private Libro[] libros;
    private int indice;

    //constructor por defecto
    //crea el array libros con un tamaño de 100 elementos
    public Biblioteca() {
        libros = new Libro[100];
    }

    //métodos setters-getters
    public int getIndice() {
        return indice;
    }

    public void setIndice(int indice) {
        this.indice = indice;
    }

    public Libro[] getLibros() {
        return libros;
    }

    //método para comprobar si hay espacio en el array para almacenar más libros
    public boolean hayEspacio() {
        return indice < libros.length;
    }

    //método que comprueba si hay libros en el array
    public boolean hayLibros() {
        return indice > 0;
    }

    //método para añadir un nuevo libro al array
    //el libro se añade a continuación del último libro añadido
    //la posición del array donde se almacena el libro la indica la variable indice
    //se debe comprobar que hay espacio en el array para poder almacenar el libro
    //el método devuelve true si se ha añadido el libro al array
    //devuelve false si no había más espacio en el array 
    public boolean nuevoLibro(Libro l) {
        if (hayEspacio()) {
            libros[indice] = new Libro(l);
            indice++;
            return true;
        }
        return false;
    }

    //método para eliminar el libro utilizando su referencia
    //para eliminar el libro del array se desplazan un lugar a la izquierda
    //el resto de libros que se encuentran a continuación de éste
    //devuelve true si se ha podido eliminar el libro
    //devuelve false si no se ha podido eliminar por que la referencia no se ha encontrado
    public boolean eliminar(String ref) {
        int pos = posicionDe(ref);
        if (pos == -1) {
            return false;
        } else {
            for (int i = pos; i < indice; i++) {
                libros[i] = libros[i + 1];
            }
            indice--;
            return true;
        }
    }

    //método para eliminar el libro utilizando su posición dentro del array
    //para eliminar el libro del array se procede como en el método anterior
    //devuelve true si se ha podido eliminar el libro
    //devuelve false si no se ha podido eliminar por que la posición no es válida
    public boolean eliminar(int pos) {
        int i;
        if (pos >= 0 && pos < indice) {
            for (i = pos; i < indice; i++) {
                libros[i] = libros[i + 1];
            }
            indice--;
            return true;
        } else {
            return false;
        }
    }

    //método que devuelve el número de libros disponibles en la biblioteca para prestar
    public int disponibles() {
        int i, cont = 0;
        for (i = 0; i < indice; i++) {
            if (libros[i].estaDisponible()) {
                cont++;
            }
        }
        return cont;
    }

    //Devuelve la posición dentro del array del libro con esa referencia. 
    //Devuelve -1 si no lo encuentra
    public int posicionDe(String referencia) {
        Libro libro = new Libro();
        libro.setReferencia(referencia);
        for (int i = 0; i < indice; i++) {
            if (libros[i].equals(libro)) {
                return i;
            }
        }
        return -1;
    }

    //Devuelve true si el array contiene un libro con esa referencia.
    public boolean contiene(String ref) {
        return posicionDe(ref) != -1;
    }

    //Devuelve el libro que tiene esa referencia. Devuelve null si el libro no existe                             
    public Libro get(String ref) {
        int i = posicionDe(ref);
        if (i != -1) {
            return libros[i];
        }
        return null;
    }

    //método para ordenar el array libros
    //el criterio de ordenación se indica en el método compareTo de la clase Libro
    //en este caso en el método compareTo se indica que la ordenación es alfabética por título del libro
    //la ordenación no se debe realizar para todo el array ya que puede que no esté completo
    //y haya elementos nulos al final
    //se debe indicar que ordene desde el primer elemento hasta el último introducido 
    public void ordenar() {
        Arrays.sort(libros, 0, indice);
    }

    //método para mostrar por pantalla todos los libros que están disponibles para prestar
    public void mostrarDisponibles() {
        for (int i = 0; i < indice; i++) {
            if (libros[i].estaDisponible()) {
                System.out.println(libros[i]);
                System.out.println();
            }
        }
    }
    
    //método que devuelve un String con todos libros de la biblioteca
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < indice; i++) {
            sb.append(libros[i]);
            sb.append("\n\n");
        }
        return sb.toString();
    }
}//Fin clase Biblioteca
Clase Menu
java.util.Scanner;

//Clase Menu Java
public class Menu {

    private int opcion;

    public void mostrar() {
        System.out.println("1. Nuevo libro");
        System.out.println("2. Mostrar todos los libros");
        System.out.println("3. Buscar libros");
        System.out.println("4. Bajas de libros por referencia");                                                  
        System.out.println("5. Bajas de libros por posición");
        System.out.println("6. Préstamo");
        System.out.println("7. Devolución");
        System.out.println("8. Mostrar libros disponibles");
        System.out.println("0. FIN");
    }

    public int leer() {
        Scanner sc = new Scanner(System.in);
        do {
            System.out.print("Introduzca opción: ");
            opcion = sc.nextInt();
        } while (opcion < 0 || opcion > 8);
        return opcion;
    }
}//Fin clase Menu
Clase Principal
import java.util.Scanner;

//Clase Principal. ProyectoBiblioteca
public class ProyectoBiblioteca {

    static Biblioteca biblioteca = new Biblioteca();
    static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {

        Menu menu = new Menu();
        int opcion;
        do {
            menu.mostrar();
            opcion = menu.leer();
            switch (opcion) {
                case 1 -> libroNuevo();
                case 2 -> mostrarLibros();
                case 3 -> buscarLibros();
                case 4 -> darDeBajaLibroPorReferencia();
                case 5 -> darDeBajaLibroPorPosicion();
                case 6 -> realizarPrestamo();
                case 7 -> realizarDevolucion();
                case 8 -> mostrarLibrosDisponibles();
            }
        } while (opcion != 0);
    }

    //método para añadir un nuevo libro a la biblioteca
    //se debe comprobar que haya espacio en la biblioteca para más libros
    //se debe comprobar también que la referencia del nuevo libro no esté repetida
    public static void libroNuevo() {
        if (biblioteca.hayEspacio()) {
            System.out.println("\nIntroduzca los datos del libro");
            String referencia, titulo, autor;
            int ejemplares;
            System.out.print("Referencia: ");
            referencia = sc.nextLine();
            if (!biblioteca.contiene(referencia)) {
                System.out.print("Título: ");
                titulo = sc.nextLine();
                System.out.print("Autor: ");
                autor = sc.nextLine();
                System.out.print("Número de ejemplares: ");
                ejemplares = sc.nextInt();
                sc.nextLine();
                Libro libro = new Libro(referencia, titulo, autor, ejemplares, 0);
                biblioteca.nuevoLibro(libro);
            } else {
                System.out.println("Ya existe un libro con esa referencia");
            }
        } else {
            System.out.println("\nNo hay espacio en la biblioteca para más libros\n");
        }
    }

    //método que muestra un listado alfabético de todos los libros de la biblioteca
    public static void mostrarLibros() {
        if (biblioteca.hayLibros()) {
            System.out.println("\nListado alfabético de libros\n");
            biblioteca.ordenar();
            System.out.println(biblioteca);
        } else {
            System.out.println("\nBiblioteca sin libros\n");
        }

    }

    //método que busca un libro en la biblioteca
    //se introduce la referencia del libro a buscar
    //se muestra el libro correspondiente a esa referencia
    //se muestra un mensaje si la referencia no existe
    public static void buscarLibros() {
        if (biblioteca.hayLibros()) {
            System.out.println("\nBuscar libros por referencia");
            String referencia;
            System.out.print("Referencia: ");
            referencia = sc.nextLine();
            Libro libro = biblioteca.get(referencia);
            if (libro != null) {
                System.out.println(libro);
            } else {
                System.out.println("\nEl libro no se encuentra en la biblioteca\n");
            }
        } else {
            System.out.println("\nBiblioteca sin libros\n");
        }
    }

    //método para dar de baja un libro de la biblioteca
    //se introduce la referencia del libro y se elimina de la biblioteca
    //Si la referencia no existe se muestra un mensaje indicándolo
    public static void darDeBajaLibroPorReferencia() {
        if (biblioteca.hayLibros()) {
            System.out.println("\nBajas de libros por referencia");
            String referencia;
            char respuesta;
            System.out.print("Referencia: ");
            referencia = sc.nextLine();
            Libro libro = biblioteca.get(referencia);
            if (libro != null) {
                System.out.println(libro);
                do {
                    System.out.print("Dar de baja? (S/N)");
                    respuesta = sc.nextLine().charAt(0);
                } while (Character.toUpperCase(respuesta) != 'S' && Character.toUpperCase(respuesta) != 'N');    
                if (Character.toUpperCase(respuesta) == 'S') {
                    biblioteca.eliminar(referencia);
                }
            } else {
                System.out.println("\nEl libro no se encuentra en la biblioteca\n");
            }
        } else {
            System.out.println("\nBiblioteca sin libros\n");
        }
    }

    //método para dar de baja un libro de la biblioteca
    //se introduce la posición del libro dentro del array y se elimina de la biblioteca
    //si la posición no es válida se muestra un mensaje indicándolo
    public static void darDeBajaLibroPorPosicion() {
        if (biblioteca.hayLibros()) {
            System.out.println("\nBajas de libros por posición");
            System.out.println("\nPosiciones válidas desde 0 hasta " + (biblioteca.getIndice() - 1));
            int posicion;
            char respuesta;
            do {
                System.out.print("Posición a dar de baja: ");
                posicion = sc.nextInt();
            } while (posicion < 0 || posicion >= biblioteca.getIndice());
            sc.nextLine();
            Libro libro = biblioteca.getLibros()[posicion];
            System.out.println(libro);
            do {
                System.out.print("Dar de baja? (S/N)");
                respuesta = sc.nextLine().charAt(0);
            } while (Character.toUpperCase(respuesta) != 'S' && Character.toUpperCase(respuesta) != 'N');
            if (Character.toUpperCase(respuesta) == 'S') {
                biblioteca.eliminar(posicion);
            }
        } else {
            System.out.println("\nBiblioteca sin libros\n");
        }
    }

    //método para realizar el préstamo de un libro
    //se introduce la referencia del libro a prestar
    //si la referencia no existe o no hay ejemplares disponibles se muestra un mensaje indicándolo
    public static void realizarPrestamo() {
        if (biblioteca.hayLibros()) {
            System.out.println("\nGestión de préstamos");
            String referencia;
            System.out.print("Referencia del libro a prestar: ");
            referencia = sc.nextLine();
            Libro libro = biblioteca.get(referencia);
            if (libro != null) {
                System.out.println(libro);
                if (!libro.prestamo()) {
                    System.out.println("\nNo hay ejemplares disponibles de este libro para prestar\n");
                } else {
                    System.out.println("\nPréstamo realizado\n");
                    System.out.println("\nQuedan " + libro.ejemplaresDisponibles() 
                                        + " ejemplares de este libro para prestar\n");
                }
            } else {
                System.out.println("\nLibro no encontrado");
            }
        } else {
            System.out.println("\nBiblioteca sin libros\n");
        }
    }

    //método para realizar la devolución de un libro
    //se introduce la referencia del libro a devolver
    //si la referencia no existe o no hay ejemplares prestados del libro se muestra un mensaje indicándolo
    public static void realizarDevolucion() {
        if (biblioteca.hayLibros()) {
            System.out.println("\nGestión de devoluciones");
            String referencia;
            System.out.print("Referencia del libro a devolver: ");
            referencia = sc.nextLine();
            Libro libro = biblioteca.get(referencia);
            if (libro != null) {
                System.out.println(libro);
                if (!libro.devolucion()) {
                    System.out.println("\nNo hay ejemplares prestados de este libro\n");
                } else {
                    System.out.println("\nPréstamo devuelto\n");
                    System.out.println("\nQuedan " + libro.ejemplaresDisponibles() 
                                       + " ejemplares de este libro para prestar\n");
                }
            } else {
                System.out.println("\nLibro no encontrado");
            }
        } else {
            System.out.println("\nBiblioteca sin libros\n");
        }
    }

    //método para mostrar todos los libros disponibles para prestar que hay en la biblioteca
    public static void mostrarLibrosDisponibles() {
        if (biblioteca.hayLibros()) {
            System.out.println("\nListado alfabético de libros disponibles\n");
            if (biblioteca.disponibles() == 0) {
                System.out.println("No hay libros disponibles\n");
            } else {
                biblioteca.ordenar();
                biblioteca.mostrarDisponibles();
            }
        } else {
            System.out.println("\nBiblioteca sin libros\n");
        }
    }
}//Fin clase Principal