Proyecto: Sistema de gestión de biblioteca
Introducción
Un sistema de gestión de biblioteca es un ejemplo perfecto para aplicar los conocimientos de Java que hemos adquirido hasta ahora. Este tipo de proyecto nos permite trabajar con conceptos fundamentales como la programación orientada a objetos, colecciones, entrada/salida de datos y manipulación de archivos. Además, se trata de un caso de uso real que podemos encontrar en nuestro día a día, lo que hace que el aprendizaje sea más significativo y aplicable.
En este artículo, desarrollaremos paso a paso un sistema de gestión para una biblioteca que permitirá registrar libros, gestionar préstamos y devoluciones, y mantener un registro de usuarios. Implementaremos una aplicación de consola completa que podría servir como base para un sistema más amplio en un entorno profesional.
Planificación del proyecto
Antes de empezar a escribir código, es importante planificar la estructura de nuestra aplicación. Nuestro sistema de gestión de biblioteca constará de las siguientes funcionalidades básicas:
- Registro y gestión de libros
- Registro y gestión de usuarios
- Control de préstamos y devoluciones
- Búsqueda de libros por distintos criterios
- Persistencia de datos en archivos
Para implementar estas funcionalidades, utilizaremos el paradigma de programación orientada a objetos y organizaremos nuestro código en diferentes paquetes y clases.
Estructura de clases
Vamos a definir las siguientes clases principales:
Libro
: para representar la información de cada libroUsuario
: para almacenar los datos de los usuarios de la bibliotecaPrestamo
: para gestionar la información de cada préstamoBiblioteca
: clase principal que contendrá la lógica de la aplicaciónGestorDatos
: para manejar la persistencia de los datosMenuPrincipal
: para gestionar la interfaz de usuario por consola
Implementación
Comencemos con la implementación de nuestro sistema paso a paso. Crearemos cada una de las clases necesarias y explicaremos su funcionamiento.
Clase Libro
La clase Libro
representa cada libro de nuestra biblioteca:
package biblioteca.modelo;
import java.io.Serializable;
import java.util.Objects;
public class Libro implements Serializable {
private static final long serialVersionUID = 1L;
private String isbn;
private String titulo;
private String autor;
private int anioPublicacion;
private String categoria;
private boolean disponible;
// Constructor
public Libro(String isbn, String titulo, String autor, int anioPublicacion, String categoria) {
this.isbn = isbn;
this.titulo = titulo;
this.autor = autor;
this.anioPublicacion = anioPublicacion;
this.categoria = categoria;
this.disponible = true;
}
// Getters y setters
public String getIsbn() {
return isbn;
}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public String getAutor() {
return autor;
}
public void setAutor(String autor) {
this.autor = autor;
}
public int getAnioPublicacion() {
return anioPublicacion;
}
public void setAnioPublicacion(int anioPublicacion) {
this.anioPublicacion = anioPublicacion;
}
public String getCategoria() {
return categoria;
}
public void setCategoria(String categoria) {
this.categoria = categoria;
}
public boolean isDisponible() {
return disponible;
}
public void setDisponible(boolean disponible) {
this.disponible = disponible;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Libro libro = (Libro) o;
return Objects.equals(isbn, libro.isbn);
}
@Override
public int hashCode() {
return Objects.hash(isbn);
}
@Override
public String toString() {
return "Libro{" +
"ISBN='" + isbn + '\'' +
", Título='" + titulo + '\'' +
", Autor='" + autor + '\'' +
", Año=" + anioPublicacion +
", Categoría='" + categoria + '\'' +
", Disponible=" + (disponible ? "Sí" : "No") +
'}';
}
}
Observa que hemos implementado la interfaz Serializable
para que podamos guardar y recuperar objetos de esta clase en archivos. También hemos sobrescrito los métodos equals()
y hashCode()
para que dos libros se consideren iguales si tienen el mismo ISBN.
Clase Usuario
La clase Usuario
contiene la información de los usuarios de la biblioteca:
package biblioteca.modelo;
import java.io.Serializable;
import java.util.Objects;
public class Usuario implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String nombre;
private String apellidos;
private String email;
private String telefono;
// Constructor
public Usuario(String id, String nombre, String apellidos, String email, String telefono) {
this.id = id;
this.nombre = nombre;
this.apellidos = apellidos;
this.email = email;
this.telefono = telefono;
}
// Getters y setters
public String getId() {
return id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellidos() {
return apellidos;
}
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getTelefono() {
return telefono;
}
public void setTelefono(String telefono) {
this.telefono = telefono;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Usuario usuario = (Usuario) o;
return Objects.equals(id, usuario.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "Usuario{" +
"ID='" + id + '\'' +
", Nombre='" + nombre + '\'' +
", Apellidos='" + apellidos + '\'' +
", Email='" + email + '\'' +
", Teléfono='" + telefono + '\'' +
'}';
}
}
Similar a la clase Libro
, implementamos Serializable
y sobrescribimos equals()
y hashCode()
para comparar usuarios por su ID.
Clase Prestamo
La clase Prestamo
gestiona la información de cada préstamo:
package biblioteca.modelo;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
public class Prestamo implements Serializable {
private static final long serialVersionUID = 1L;
private static int contadorId = 1;
private int id;
private String isbnLibro;
private String idUsuario;
private LocalDate fechaPrestamo;
private LocalDate fechaDevolucionPrevista;
private LocalDate fechaDevolucionReal;
// Constructor
public Prestamo(String isbnLibro, String idUsuario) {
this.id = contadorId++;
this.isbnLibro = isbnLibro;
this.idUsuario = idUsuario;
this.fechaPrestamo = LocalDate.now();
// Por defecto, fecha de devolución prevista a 14 días
this.fechaDevolucionPrevista = fechaPrestamo.plusDays(14);
this.fechaDevolucionReal = null; // Aún no se ha devuelto
}
// Getters y setters
public int getId() {
return id;
}
public String getIsbnLibro() {
return isbnLibro;
}
public String getIdUsuario() {
return idUsuario;
}
public LocalDate getFechaPrestamo() {
return fechaPrestamo;
}
public LocalDate getFechaDevolucionPrevista() {
return fechaDevolucionPrevista;
}
public void setFechaDevolucionPrevista(LocalDate fechaDevolucionPrevista) {
this.fechaDevolucionPrevista = fechaDevolucionPrevista;
}
public LocalDate getFechaDevolucionReal() {
return fechaDevolucionReal;
}
public void setFechaDevolucionReal(LocalDate fechaDevolucionReal) {
this.fechaDevolucionReal = fechaDevolucionReal;
}
public boolean estaActivo() {
return fechaDevolucionReal == null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Prestamo prestamo = (Prestamo) o;
return id == prestamo.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
return "Prestamo{" +
"ID=" + id +
", ISBN del libro='" + isbnLibro + '\'' +
", ID del usuario='" + idUsuario + '\'' +
", Fecha de préstamo=" + fechaPrestamo.format(formatter) +
", Fecha de devolución prevista=" + fechaDevolucionPrevista.format(formatter) +
", Fecha de devolución real=" + (fechaDevolucionReal != null ? fechaDevolucionReal.format(formatter) : "Pendiente") +
'}';
}
// Método para reiniciar el contador de IDs (útil al cargar datos)
public static void reiniciarContador(int ultimoId) {
contadorId = ultimoId + 1;
}
}
En esta clase utilizamos LocalDate
para gestionar las fechas y un contador estático para asignar automáticamente un ID a cada préstamo.
Clase Biblioteca
La clase Biblioteca
es el núcleo de nuestra aplicación y contiene la lógica para gestionar libros, usuarios y préstamos:
package biblioteca.modelo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Biblioteca {
private Map<String, Libro> libros;
private Map<String, Usuario> usuarios;
private List<Prestamo> prestamos;
// Constructor
public Biblioteca() {
this.libros = new HashMap<>();
this.usuarios = new HashMap<>();
this.prestamos = new ArrayList<>();
}
// Métodos para gestión de libros
public void agregarLibro(Libro libro) {
libros.put(libro.getIsbn(), libro);
}
public boolean eliminarLibro(String isbn) {
// Verificamos que el libro no esté prestado
boolean libroPrestado = prestamos.stream()
.anyMatch(p -> p.getIsbnLibro().equals(isbn) && p.estaActivo());
if (libroPrestado) {
return false;
}
return libros.remove(isbn) != null;
}
public Libro buscarLibroPorIsbn(String isbn) {
return libros.get(isbn);
}
public List<Libro> buscarLibrosPorTitulo(String titulo) {
return libros.values().stream()
.filter(l -> l.getTitulo().toLowerCase().contains(titulo.toLowerCase()))
.collect(Collectors.toList());
}
public List<Libro> buscarLibrosPorAutor(String autor) {
return libros.values().stream()
.filter(l -> l.getAutor().toLowerCase().contains(autor.toLowerCase()))
.collect(Collectors.toList());
}
public List<Libro> obtenerTodosLosLibros() {
return new ArrayList<>(libros.values());
}
// Métodos para gestión de usuarios
public void agregarUsuario(Usuario usuario) {
usuarios.put(usuario.getId(), usuario);
}
public boolean eliminarUsuario(String id) {
// Verificamos que el usuario no tenga préstamos activos
boolean tienePrestamos = prestamos.stream()
.anyMatch(p -> p.getIdUsuario().equals(id) && p.estaActivo());
if (tienePrestamos) {
return false;
}
return usuarios.remove(id) != null;
}
public Usuario buscarUsuarioPorId(String id) {
return usuarios.get(id);
}
public List<Usuario> buscarUsuariosPorNombre(String nombre) {
return usuarios.values().stream()
.filter(u -> (u.getNombre() + " " + u.getApellidos()).toLowerCase().contains(nombre.toLowerCase()))
.collect(Collectors.toList());
}
public List<Usuario> obtenerTodosLosUsuarios() {
return new ArrayList<>(usuarios.values());
}
// Métodos para gestión de préstamos
public Prestamo realizarPrestamo(String isbnLibro, String idUsuario) {
Libro libro = libros.get(isbnLibro);
Usuario usuario = usuarios.get(idUsuario);
if (libro == null || usuario == null) {
return null;
}
if (!libro.isDisponible()) {
return null;
}
Prestamo prestamo = new Prestamo(isbnLibro, idUsuario);
prestamos.add(prestamo);
libro.setDisponible(false);
return prestamo;
}
public boolean devolverLibro(int idPrestamo) {
Prestamo prestamo = buscarPrestamoPorId(idPrestamo);
if (prestamo == null || !prestamo.estaActivo()) {
return false;
}
Libro libro = libros.get(prestamo.getIsbnLibro());
if (libro == null) {
return false;
}
prestamo.setFechaDevolucionReal(java.time.LocalDate.now());
libro.setDisponible(true);
return true;
}
public Prestamo buscarPrestamoPorId(int id) {
return prestamos.stream()
.filter(p -> p.getId() == id)
.findFirst()
.orElse(null);
}
public List<Prestamo> obtenerPrestamosActivos() {
return prestamos.stream()
.filter(Prestamo::estaActivo)
.collect(Collectors.toList());
}
public List<Prestamo> obtenerPrestamosPorUsuario(String idUsuario) {
return prestamos.stream()
.filter(p -> p.getIdUsuario().equals(idUsuario))
.collect(Collectors.toList());
}
public List<Prestamo> obtenerTodosLosPrestamos() {
return new ArrayList<>(prestamos);
}
// Métodos para cargar y guardar datos
public void setLibros(Map<String, Libro> libros) {
this.libros = libros;
}
public void setUsuarios(Map<String, Usuario> usuarios) {
this.usuarios = usuarios;
}
public void setPrestamos(List<Prestamo> prestamos) {
this.prestamos = prestamos;
}
public Map<String, Libro> getLibros() {
return libros;
}
public Map<String, Usuario> getUsuarios() {
return usuarios;
}
public List<Prestamo> getPrestamos() {
return prestamos;
}
}
Esta clase implementa la lógica principal del sistema usando colecciones como HashMap
y ArrayList
para almacenar los datos. También utiliza la API Stream de Java para realizar operaciones como filtrado y búsqueda.
Clase GestorDatos
La clase GestorDatos
se encarga de la persistencia de los datos:
package biblioteca.datos;
import biblioteca.modelo.Biblioteca;
import biblioteca.modelo.Libro;
import biblioteca.modelo.Prestamo;
import biblioteca.modelo.Usuario;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GestorDatos {
private static final String ARCHIVO_LIBROS = "libros.dat";
private static final String ARCHIVO_USUARIOS = "usuarios.dat";
private static final String ARCHIVO_PRESTAMOS = "prestamos.dat";
// Guardar datos
public static void guardarDatos(Biblioteca biblioteca) {
guardarLibros(biblioteca.getLibros());
guardarUsuarios(biblioteca.getUsuarios());
guardarPrestamos(biblioteca.getPrestamos());
}
// Cargar datos
public static Biblioteca cargarDatos() {
Biblioteca biblioteca = new Biblioteca();
Map<String, Libro> libros = cargarLibros();
Map<String, Usuario> usuarios = cargarUsuarios();
List<Prestamo> prestamos = cargarPrestamos();
biblioteca.setLibros(libros);
biblioteca.setUsuarios(usuarios);
biblioteca.setPrestamos(prestamos);
// Actualizar el contador de IDs de los préstamos
if (!prestamos.isEmpty()) {
int maxId = prestamos.stream()
.mapToInt(Prestamo::getId)
.max()
.orElse(0);
Prestamo.reiniciarContador(maxId);
}
return biblioteca;
}
// Métodos privados para guardar cada tipo de dato
private static void guardarLibros(Map<String, Libro> libros) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(ARCHIVO_LIBROS))) {
oos.writeObject(new ArrayList<>(libros.values()));
} catch (IOException e) {
System.err.println("Error al guardar los libros: " + e.getMessage());
}
}
private static void guardarUsuarios(Map<String, Usuario> usuarios) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(ARCHIVO_USUARIOS))) {
oos.writeObject(new ArrayList<>(usuarios.values()));
} catch (IOException e) {
System.err.println("Error al guardar los usuarios: " + e.getMessage());
}
}
private static void guardarPrestamos(List<Prestamo> prestamos) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(ARCHIVO_PRESTAMOS))) {
oos.writeObject(prestamos);
} catch (IOException e) {
System.err.println("Error al guardar los préstamos: " + e.getMessage());
}
}
// Métodos privados para cargar cada tipo de dato
private static Map<String, Libro> cargarLibros() {
Map<String, Libro> libros = new HashMap<>();
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(ARCHIVO_LIBROS))) {
List<Libro> listaLibros = (List<Libro>) ois.readObject();
for (Libro libro : listaLibros) {
libros.put(libro.getIsbn(), libro);
}
} catch (FileNotFoundException e) {
// El archivo no existe, es la primera ejecución
System.out.println("No se encontró el archivo de libros. Se creará uno nuevo al guardar.");
} catch (IOException | ClassNotFoundException e) {
System.err.println("Error al cargar los libros: " + e.getMessage());
}
return libros;
}
private static Map<String, Usuario> cargarUsuarios() {
Map<String, Usuario> usuarios = new HashMap<>();
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(ARCHIVO_USUARIOS))) {
List<Usuario> listaUsuarios = (List<Usuario>) ois.readObject();
for (Usuario usuario : listaUsuarios) {
usuarios.put(usuario.getId(), usuario);
}
} catch (FileNotFoundException e) {
// El archivo no existe, es la primera ejecución
System.out.println("No se encontró el archivo de usuarios. Se creará uno nuevo al guardar.");
} catch (IOException | ClassNotFoundException e) {
System.err.println("Error al cargar los usuarios: " + e.getMessage());
}
return usuarios;
}
private static List<Prestamo> cargarPrestamos() {
List<Prestamo> prestamos = new ArrayList<>();
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(ARCHIVO_PRESTAMOS))) {
prestamos = (List<Prestamo>) ois.readObject();
} catch (FileNotFoundException e) {
// El archivo no existe, es la primera ejecución
System.out.println("No se encontró el archivo de préstamos. Se creará uno nuevo al guardar.");
} catch (IOException | ClassNotFoundException e) {
System.err.println("Error al cargar los préstamos: " + e.getMessage());
}
return prestamos;
}
}
Esta clase utiliza la serialización de Java para guardar y cargar objetos desde archivos. Cada tipo de objeto se guarda en un archivo separado.
Clase MenuPrincipal
package biblioteca.ui;
import biblioteca.datos.GestorDatos;
import biblioteca.modelo.Biblioteca;
import biblioteca.modelo.Libro;
import biblioteca.modelo.Prestamo;
import biblioteca.modelo.Usuario;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Scanner;
public class MenuPrincipal {
private Biblioteca biblioteca;
private Scanner scanner;
public MenuPrincipal() {
this.biblioteca = GestorDatos.cargarDatos();
this.scanner = new Scanner(System.in);
// Si no hay datos, cargamos algunos ejemplos
if (biblioteca.obtenerTodosLosLibros().isEmpty()) {
cargarDatosEjemplo();
}
}
public void mostrarMenu() {
boolean salir = false;
while (!salir) {
System.out.println("\n===== SISTEMA DE GESTIÓN DE BIBLIOTECA =====\n");
System.out.println("1. Gestión de libros");
System.out.println("2. Gestión de usuarios");
System.out.println("3. Gestión de préstamos");
System.out.println("4. Guardar y salir");
System.out.print("\nSeleccione una opción: ");
int opcion = leerEntero();
switch (opcion) {
case 1:
menuLibros();
break;
case 2:
menuUsuarios();
break;
case 3:
menuPrestamos();
break;
case 4:
GestorDatos.guardarDatos(biblioteca);
System.out.println("Datos guardados correctamente. ¡Hasta pronto!");
salir = true;
break;
default:
System.out.println("Opción no válida. Intente de nuevo.");
}
}
}
private void menuLibros() {
boolean volver = false;
while (!volver) {
System.out.println("\n===== GESTIÓN DE LIBROS =====\n");
System.out.println("1. Agregar libro");
System.out.println("2. Eliminar libro");
System.out.println("3. Buscar libro por ISBN");
System.out.println("4. Buscar libros por título");
System.out.println("5. Buscar libros por autor");
System.out.println("6. Listar todos los libros");
System.out.println("7. Volver al menú principal");
System.out.print("\nSeleccione una opción: ");
int opcion = leerEntero();
switch (opcion) {
case 1:
agregarLibro();
break;
case 2:
eliminarLibro();
break;
case 3:
buscarLibroPorIsbn();
break;
case 4:
buscarLibrosPorTitulo();
break;
case 5:
buscarLibrosPorAutor();
break;
case 6:
listarTodosLosLibros();
break;
case 7:
volver = true;
break;
default:
System.out.println("Opción no válida. Intente de nuevo.");
}
}
}
private void menuUsuarios() {
boolean volver = false;
while (!volver) {
System.out.println("\n===== GESTIÓN DE USUARIOS =====\n");
System.out.println("1. Agregar usuario");
System.out.println("2. Eliminar usuario");
System.out.println("3. Buscar usuario por ID");
System.out.println("4. Buscar usuarios por nombre");
System.out.println("5. Listar todos los usuarios");
System.out.println("6. Volver al menú principal");
System.out.print("\nSeleccione una opción: ");
int opcion = leerEntero();
switch (opcion) {
case 1:
agregarUsuario();
break;
case 2:
eliminarUsuario();
break;
case 3:
buscarUsuarioPorId();
break;
case 4:
buscarUsuariosPorNombre();
break;
case 5:
listarTodosLosUsuarios();
break;
case 6:
volver = true;
break;
default:
System.out.println("Opción no válida. Intente de nuevo.");
}
}
}
private void menuPrestamos() {
boolean volver = false;
while (!volver) {
System.out.println("\n===== GESTIÓN DE PRÉSTAMOS =====\n");
System.out.println("1. Realizar préstamo");
System.out.println("2. Devolver libro");
System.out.println("3. Listar préstamos activos");
System.out.println("4. Listar préstamos por usuario");
System.out.println("5. Listar todos los préstamos");
System.out.println("6. Volver al menú principal");
System.out.print("\nSeleccione una opción: ");
int opcion = leerEntero();
switch (opcion) {
case 1:
realizarPrestamo();
break;
case 2:
devolverLibro();
break;
case 3:
listarPrestamosActivos();
break;
case 4:
listarPrestamosPorUsuario();
break;
case 5:
listarTodosLosPrestamos();
break;
case 6:
volver = true;
break;
default:
System.out.println("Opción no válida. Intente de nuevo.");
}
}
}
// Implementación de los métodos de gestión de libros
private void agregarLibro() {
System.out.println("\n===== AGREGAR LIBRO =====\n");
System.out.print("ISBN: ");
String isbn = scanner.nextLine();
if (biblioteca.buscarLibroPorIsbn(isbn) != null) {
System.out.println("Error: Ya existe un libro con ese ISBN.");
return;
}
System.out.print("Título: ");
String titulo = scanner.nextLine();
System.out.print("Autor: ");
String autor = scanner.nextLine();
System.out.print("Año de publicación: ");
int anioPublicacion = leerEntero();
System.out.print("Categoría: ");
String categoria = scanner.nextLine();
Libro libro = new Libro(isbn, titulo, autor, anioPublicacion, categoria);
biblioteca.agregarLibro(libro);
System.out.println("Libro agregado correctamente.");
}
private void eliminarLibro() {
System.out.println("\n===== ELIMINAR LIBRO =====\n");
System.out.print("ISBN del libro a eliminar: ");
String isbn = scanner.nextLine();
Libro libro = biblioteca.buscarLibroPorIsbn(isbn);
if (libro == null) {
System.out.println("Error: No existe un libro con ese ISBN.");
return;
}
boolean eliminado = biblioteca.eliminarLibro(isbn);
if (eliminado) {
System.out.println("Libro eliminado correctamente.");
} else {
System.out.println("Error: No se pudo eliminar el libro. Compruebe que no esté prestado.");
}
}
private void buscarLibroPorIsbn() {
System.out.println("\n===== BUSCAR LIBRO POR ISBN =====\n");
System.out.print("ISBN: ");
String isbn = scanner.nextLine();
Libro libro = biblioteca.buscarLibroPorIsbn(isbn);
if (libro != null) {
System.out.println("\nLibro encontrado:");
System.out.println(libro);
} else {
System.out.println("No se encontró ningún libro con ese ISBN.");
}
}
private void buscarLibrosPorTitulo() {
System.out.println("\n===== BUSCAR LIBROS POR TÍTULO =====\n");
System.out.print("Título (o parte del título): ");
String titulo = scanner.nextLine();
List<Libro> librosEncontrados = biblioteca.buscarLibrosPorTitulo(titulo);
if (!librosEncontrados.isEmpty()) {
System.out.println("\nLibros encontrados:");
for (Libro libro : librosEncontrados) {
System.out.println(libro);
}
} else {
System.out.println("No se encontraron libros con ese título.");
}
}
private void buscarLibrosPorAutor() {
System.out.println("\n===== BUSCAR LIBROS POR AUTOR =====\n");
System.out.print("Autor (o parte del nombre): ");
String autor = scanner.nextLine();
List<Libro> librosEncontrados = biblioteca.buscarLibrosPorAutor(autor);
if (!librosEncontrados.isEmpty()) {
System.out.println("\nLibros encontrados:");
for (Libro libro : librosEncontrados) {
System.out.println(libro);
}
} else {
System.out.println("No se encontraron libros de ese autor.");
}
}
private void listarTodosLosLibros() {
System.out.println("\n===== TODOS LOS LIBROS =====\n");
List<Libro> todosLosLibros = biblioteca.obtenerTodosLosLibros();
if (!todosLosLibros.isEmpty()) {
for (Libro libro : todosLosLibros) {
System.out.println(libro);
}
System.out.println("\nTotal: " + todosLosLibros.size() + " libros.");
} else {
System.out.println("No hay libros registrados en la biblioteca.");
}
}
// Implementación de los métodos de gestión de usuarios
private void agregarUsuario() {
System.out.println("\n===== AGREGAR USUARIO =====\n");
System.out.print("ID (DNI o NIE): ");
String id = scanner.nextLine();
if (biblioteca.buscarUsuarioPorId(id) != null) {
System.out.println("Error: Ya existe un usuario con ese ID.");
return;
}
System.out.print("Nombre: ");
String nombre = scanner.nextLine();
System.out.print("Apellidos: ");
String apellidos = scanner.nextLine();
System.out.print("Email: ");
String email = scanner.nextLine();
System.out.print("Teléfono: ");
String telefono = scanner.nextLine();
Usuario usuario = new Usuario(id, nombre, apellidos, email, telefono);
biblioteca.agregarUsuario(usuario);
System.out.println("Usuario agregado correctamente.");
}
private void eliminarUsuario() {
System.out.println("\n===== ELIMINAR USUARIO =====\n");
System.out.print("ID del usuario a eliminar: ");
String id = scanner.nextLine();
Usuario usuario = biblioteca.buscarUsuarioPorId(id);
if (usuario == null) {
System.out.println("Error: No existe un usuario con ese ID.");
return;
}
boolean eliminado = biblioteca.eliminarUsuario(id);
if (eliminado) {
System.out.println("Usuario eliminado correctamente.");
} else {
System.out.println("Error: No se pudo eliminar el usuario. Compruebe que no tenga préstamos activos.");
}
}
private void buscarUsuarioPorId() {
System.out.println("\n===== BUSCAR USUARIO POR ID =====\n");
System.out.print("ID: ");
String id = scanner.nextLine();
Usuario usuario = biblioteca.buscarUsuarioPorId(id);
if (usuario != null) {
System.out.println("\nUsuario encontrado:");
System.out.println(usuario);
} else {
System.out.println("No se encontró ningún usuario con ese ID.");
}
}
private void buscarUsuariosPorNombre() {
System.out.println("\n===== BUSCAR USUARIOS POR NOMBRE =====\n");
System.out.print("Nombre o apellidos (o parte): ");
String nombre = scanner.nextLine();
List<Usuario> usuariosEncontrados = biblioteca.buscarUsuariosPorNombre(nombre);
if (!usuariosEncontrados.isEmpty()) {
System.out.println("\nUsuarios encontrados:");
for (Usuario usuario : usuariosEncontrados) {
System.out.println(usuario);
}
} else {
System.out.println("No se encontraron usuarios con ese nombre o apellidos.");
}
}
private void listarTodosLosUsuarios() {
System.out.println("\n===== TODOS LOS USUARIOS =====\n");
List<Usuario> todosLosUsuarios = biblioteca.obtenerTodosLosUsuarios();
if (!todosLosUsuarios.isEmpty()) {
for (Usuario usuario : todosLosUsuarios) {
System.out.println(usuario);
}
System.out.println("\nTotal: " + todosLosUsuarios.size() + " usuarios.");
} else {
System.out.println("No hay usuarios registrados en la biblioteca.");
}
}
// Implementación de los métodos de gestión de préstamos
private void realizarPrestamo() {
System.out.println("\n===== REALIZAR PRÉSTAMO =====\n");
System.out.print("ISBN del libro: ");
String isbn = scanner.nextLine();
Libro libro = biblioteca.buscarLibroPorIsbn(isbn);
if (libro == null) {
System.out.println("Error: No existe un libro con ese ISBN.");
return;
}
if (!libro.isDisponible()) {
System.out.println("Error: El libro no está disponible actualmente.");
return;
}
System.out.print("ID del usuario: ");
String idUsuario = scanner.nextLine();
Usuario usuario = biblioteca.buscarUsuarioPorId(idUsuario);
if (usuario == null) {
System.out.println("Error: No existe un usuario con ese ID.");
return;
}
Prestamo prestamo = biblioteca.realizarPrestamo(isbn, idUsuario);
if (prestamo != null) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
System.out.println("Préstamo realizado correctamente:");
System.out.println("ID del préstamo: " + prestamo.getId());
System.out.println("Fecha de préstamo: " + prestamo.getFechaPrestamo().format(formatter));
System.out.println("Fecha de devolución prevista: " + prestamo.getFechaDevolucionPrevista().format(formatter));
} else {
System.out.println("Error: No se pudo realizar el préstamo.");
}
}
private void devolverLibro() {
System.out.println("\n===== DEVOLVER LIBRO =====\n");
System.out.print("ID del préstamo: ");
int idPrestamo = leerEntero();
Prestamo prestamo = biblioteca.buscarPrestamoPorId(idPrestamo);
if (prestamo == null) {
System.out.println("Error: No existe un préstamo con ese ID.");
return;
}
if (!prestamo.estaActivo()) {
System.out.println("Error: Este préstamo ya ha sido devuelto.");
return;
}
boolean devuelto = biblioteca.devolverLibro(idPrestamo);
if (devuelto) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
System.out.println("Libro devuelto correctamente.");
System.out.println("Fecha de devolución: " + LocalDate.now().format(formatter));
} else {
System.out.println("Error: No se pudo procesar la devolución.");
}
}
private void listarPrestamosActivos() {
System.out.println("\n===== PRÉSTAMOS ACTIVOS =====\n");
List<Prestamo> prestamosActivos = biblioteca.obtenerPrestamosActivos();
if (!prestamosActivos.isEmpty()) {
for (Prestamo prestamo : prestamosActivos) {
Libro libro = biblioteca.buscarLibroPorIsbn(prestamo.getIsbnLibro());
Usuario usuario = biblioteca.buscarUsuarioPorId(prestamo.getIdUsuario());
System.out.println("ID Préstamo: " + prestamo.getId());
System.out.println("Libro: " + (libro != null ? libro.getTitulo() : "Desconocido"));
System.out.println("Usuario: " + (usuario != null ? usuario.getNombre() + " " + usuario.getApellidos() : "Desconocido"));
System.out.println("Fecha préstamo: " + prestamo.getFechaPrestamo());
System.out.println("Fecha devolución prevista: " + prestamo.getFechaDevolucionPrevista());
System.out.println();
}
System.out.println("Total: " + prestamosActivos.size() + " préstamos activos.");
} else {
System.out.println("No hay préstamos activos actualmente.");
}
}
private void listarPrestamosPorUsuario() {
System.out.println("\n===== PRÉSTAMOS POR USUARIO =====\n");
System.out.print("ID del usuario: ");
String idUsuario = scanner.nextLine();
Usuario usuario = biblioteca.buscarUsuarioPorId(idUsuario);
if (usuario == null) {
System.out.println("Error: No existe un usuario con ese ID.");
return;
}
List<Prestamo> prestamosUsuario = biblioteca.obtenerPrestamosPorUsuario(idUsuario);
if (!prestamosUsuario.isEmpty()) {
System.out.println("\nPréstamos de " + usuario.getNombre() + " " + usuario.getApellidos() + ":");
for (Prestamo prestamo : prestamosUsuario) {
Libro libro = biblioteca.buscarLibroPorIsbn(prestamo.getIsbnLibro());
System.out.println("ID Préstamo: " + prestamo.getId());
System.out.println("Libro: " + (libro != null ? libro.getTitulo() : "Desconocido"));
System.out.println("Estado: " + (prestamo.estaActivo() ? "Activo" : "Devuelto"));
System.out.println("Fecha préstamo: " + prestamo.getFechaPrestamo());
System.out.println("Fecha devolución prevista: " + prestamo.getFechaDevolucionPrevista());
if (!prestamo.estaActivo()) {
System.out.println("Fecha devolución real: " + prestamo.getFechaDevolucionReal());
}
System.out.println();
}
} else {
System.out.println("Este usuario no tiene préstamos registrados.");
}
}
private void listarTodosLosPrestamos() {
System.out.println("\n===== TODOS LOS PRÉSTAMOS =====\n");
List<Prestamo> todosLosPrestamos = biblioteca.obtenerTodosLosPrestamos();
if (!todosLosPrestamos.isEmpty()) {
for (Prestamo prestamo : todosLosPrestamos) {
Libro libro = biblioteca.buscarLibroPorIsbn(prestamo.getIsbnLibro());
Usuario usuario = biblioteca.buscarUsuarioPorId(prestamo.getIdUsuario());
System.out.println("ID Préstamo: " + prestamo.getId());
System.out.println("Libro: " + (libro != null ? libro.getTitulo() : "Desconocido"));
System.out.println("Usuario: " + (usuario != null ? usuario.getNombre() + " " + usuario.getApellidos() : "Desconocido"));
System.out.println("Estado: " + (prestamo.estaActivo() ? "Activo" : "Devuelto"));
System.out.println("Fecha préstamo: " + prestamo.getFechaPrestamo());
System.out.println("Fecha devolución prevista: " + prestamo.getFechaDevolucionPrevista());
if (!prestamo.estaActivo()) {
System.out.println("Fecha devolución real: " + prestamo.getFechaDevolucionReal());
}
System.out.println();
}
System.out.println("Total: " + todosLosPrestamos.size() + " préstamos.");
} else {
System.out.println("No hay préstamos registrados en la biblioteca.");
}
}
// Métodos auxiliares
private int leerEntero() {
int numero = 0;
boolean entradaValida = false;
while (!entradaValida) {
try {
numero = Integer.parseInt(scanner.nextLine());
entradaValida = true;
} catch (NumberFormatException e) {
System.out.print("Por favor, introduce un número válido: ");
}
}
return numero;
}
private void cargarDatosEjemplo() {
// Libros de ejemplo
biblioteca.agregarLibro(new Libro("9788401352836", "El tiempo entre costuras", "María Dueñas", 2009, "Novela histórica"));
biblioteca.agregarLibro(new Libro("9788408161486", "Patria", "Fernando Aramburu", 2016, "Novela contemporánea"));
biblioteca.agregarLibro(new Libro("9788432234385", "Cien años de soledad", "Gabriel García Márquez", 1967, "Realismo mágico"));
biblioteca.agregarLibro(new Libro("9788466330978", "El principito", "Antoine de Saint-Exupéry", 1943, "Literatura infantil"));
biblioteca.agregarLibro(new Libro("9788420485058", "Java: Cómo programar", "Paul Deitel", 2021, "Informática"));
// Usuarios de ejemplo
biblioteca.agregarUsuario(new Usuario("12345678A", "Juan", "García López", "juan@ejemplo.com", "600123456"));
biblioteca.agregarUsuario(new Usuario("87654321B", "María", "Rodríguez Sánchez", "maria@ejemplo.com", "600654321"));
biblioteca.agregarUsuario(new Usuario("11223344C", "Carlos", "Martínez Pérez", "carlos@ejemplo.com", "600112233"));
System.out.println("Se han cargado datos de ejemplo en la biblioteca.");
}
}
Clase Principal
Finalmente, creamos la clase principal que iniciará nuestra aplicación:
package biblioteca;
import biblioteca.ui.MenuPrincipal;
public class SistemaBiblioteca {
public static void main(String[] args) {
MenuPrincipal menu = new MenuPrincipal();
menu.mostrarMenu();
}
}
Estructura del proyecto
La estructura completa del proyecto quedaría así:
biblioteca/
├── SistemaBiblioteca.java
├── modelo/
│ ├── Libro.java
│ ├── Usuario.java
│ ├── Prestamo.java
│ └── Biblioteca.java
├── datos/
│ └── GestorDatos.java
└── ui/
└── MenuPrincipal.java
Esta estructura sigue el patrón Modelo-Vista-Controlador (MVC), donde:
- El Modelo está formado por las clases
Libro
,Usuario
,Prestamo
yBiblioteca
- La Vista es la clase
MenuPrincipal
- El Controlador está distribuido entre
Biblioteca
yGestorDatos
Cómo ejecutar el programa
Para ejecutar el programa, sigue estos pasos:
- Crea un proyecto Java en tu IDE preferido
- Crea los paquetes
biblioteca
,biblioteca.modelo
,biblioteca.datos
ybiblioteca.ui
- Crea las clases en sus paquetes correspondientes, copiando el código mostrado en este artículo
- Ejecuta la clase
SistemaBiblioteca
También puedes compilar y ejecutar el programa desde la línea de comandos:
# Compilar
javac -d . biblioteca/SistemaBiblioteca.java biblioteca/modelo/*.java biblioteca/datos/*.java biblioteca/ui/*.java
# Ejecutar
java biblioteca.SistemaBiblioteca
Posibles mejoras
Este sistema de gestión de biblioteca es completamente funcional, pero existen muchas mejoras que podrías implementar:
- Interfaz gráfica: Reemplazar la interfaz de consola por una interfaz gráfica usando JavaFX o Swing.
- Base de datos: Utilizar una base de datos SQL en lugar de archivos para la persistencia de datos.
- Validación de datos: Añadir validaciones más rigurosas para los datos introducidos.
- Gestión de multas: Implementar un sistema de multas por retrasos en las devoluciones.
- Búsqueda avanzada: Permitir búsquedas combinadas por diferentes criterios.
- Estadísticas: Añadir informes y estadísticas sobre el uso de la biblioteca.
- Sistema de reservas: Permitir a los usuarios reservar libros que están prestados.
- Autenticación: Implementar un sistema de autenticación para acceder a la aplicación.
Resumen
En este artículo, hemos desarrollado un sistema completo de gestión de biblioteca en Java, que permite administrar libros, usuarios y préstamos. Hemos aplicado conceptos como:
- Programación orientada a objetos
- Colecciones de Java (Map, List)
- Streams y expresiones lambda
- Persistencia de datos mediante serialización
- Manejo de fechas con java.time
- Estructuras de control
- Entrada/salida de datos
Este proyecto integra muchos de los conocimientos adquiridos a lo largo del tutorial y proporciona una base sólida que puede ser extendida con nuevas funcionalidades. La implementación modular facilita los cambios y mejoras, permitiendo adaptar el sistema a diferentes necesidades.