Contexto
Una biblioteca necesita un catálogo mínimo para sus libros: título, autor, ISBN,
cuántas copias hay disponibles para prestar y cuánto cuesta reponer un
ejemplar perdido. A diferencia del ejercicio de la lección anterior, acá un
Libro no puede nacer con datos basura: un título vacío o un precio de
reposición negativo tienen que quedar reemplazados por un valor por defecto
seguro en el mismo instante en que alguien intenta crear el objeto, no
descubiertos después en algún reporte.
Consigna
Escribí una clase Java llamada Libro.java que represente un ejemplar del
catálogo, y una clase MainBiblioteca.java con el método main que la ponga
a prueba.
1. Atributos de la clase Libro
Definí los siguientes atributos, todos private (estado protegido, nunca
expuesto directamente):
titulo(String,final)autor(String,final)isbn(String,final)copiasDisponibles(int)precioReposicion(double)
Estructura sugerida para arrancar (completá los cuerpos):
public class Libro {
private final String titulo;
private final String autor;
private final String isbn;
private int copiasDisponibles;
private double precioReposicion;
public Libro(String titulo, String autor, String isbn, int copiasDisponibles, double precioReposicion) {
/* ... */
}
public Libro(String titulo, String autor, String isbn) {
/* ... */
}
public boolean prestar() { /* ... */ }
public void devolver() { /* ... */ }
public boolean setPrecioReposicion(double precio) { /* ... */ }
public void mostrarFicha() { /* ... */ }
}
2. El constructor canónico
Libro(String titulo, String autor, String isbn, int copiasDisponibles, double precioReposicion)
es el único lugar donde vive la validación completa:
titulo,autoreisbnno pueden sernullni estar en blanco.copiasDisponiblesno puede ser negativo.precioReposiciontiene que ser mayor a0.
Si alguna condición no se cumple, el constructor no corta la construcción:
reemplaza el dato inválido por un valor por defecto seguro ("Sin título",
"Autor desconocido", "ISBN pendiente", 0 copias o $15000.0) e informa
por consola qué se rechazó y por qué. Ningún Libro termina de construirse
con datos inválidos, pero tampoco deja de construirse: siempre gana un valor
por defecto seguro.
3. El constructor de conveniencia y this(...)
Libro(String titulo, String autor, String isbn) es para el caso frecuente
de un libro nuevo: arranca con 1 copia disponible y un
precioReposicion por defecto de 15000.0. No repitas ninguna validación
acá: delegá con this(...) en el constructor canónico, como primera
sentencia del constructor, para que sea el único lugar donde la regla puede
romperse o arreglarse.
Dejá comentado en main algo como // new Libro(); // no compila, con una
línea que explique por qué: al declarar estos dos constructores propios, el
constructor sin argumentos que el compilador regalaba automáticamente
dejó de existir.
4. Getters y el setter validado
Escribí un getter público para cada uno de los cinco atributos. Para el
precio de reposición, además, escribí setPrecioReposicion(double precio)
como boolean, aplicando la misma regla que el constructor (mayor a
0): devuelve true si acepta el nuevo precio y false si lo rechaza, sin
tocar el precio anterior. Que la regla viva en un solo lugar y el
constructor la reutilice, o que ambos deleguen en un validador privado
común: lo importante es que no esté escrita dos veces.
5. Operaciones de dominio en lugar de un setter de copias
No escribas setCopiasDisponibles(int). En su lugar, expresá la intención
con dos métodos:
prestar()(boolean): si hay copias disponibles, descontá una, informá el préstamo y devolvétrue. Si no hay copias, informá el error, no modifiques el atributo y devolvéfalse.devolver(): suma una copia disponible e informa la devolución.
Un setter genérico de copias dejaría que cualquier código externo ponga el
contador en cualquier valor, incluido uno negativo. prestar() y
devolver() son la única puerta, y cada una aplica su propia regla.
6. MainBiblioteca: instanciación, validaciones y recorrido
En MainBiblioteca:
- Creá al menos tres objetos
Libro, combinando el constructor canónico y el de conveniencia. - Demostrá al menos dos rechazos usando los valores devueltos por los
métodos: construí un libro con un dato inválido (por ejemplo, título
vacío) y comprobá con
getTitulo()que se usó el valor por defecto; y llamá asetPrecioReposicioncon un valor inválido, comprobando que devuelvefalsey quegetPrecioReposicion()no cambió. - Agotá las copias de algún libro con llamadas repetidas a
prestar(), comprobando elbooleanque devuelve cada llamada, y verificá que un préstamo de más informa el error, devuelvefalsey no deja el contador en negativo. - Llamá a
mostrarFicha()de cada uno de los libros que creaste, cada uno en su propia variable (libro1,libro2,libro3). Guardar varios objetos en una sola estructura recorrible —un array de objetos o una colección— es el tema de una lección futura.
El formato exacto de tus mensajes puede variar, pero debe comunicar la misma información que la salida esperada de más abajo: qué se rechazó y por qué, la ficha de cada libro, y el resultado de prestar, agotar copias y devolver.