Saltar al contenido principal

Proyecto: aplicación orientada a objetos

Construiremos una biblioteca de consola. El objetivo no es acumular sintaxis, sino hacer que varias clases colaboren y protejan reglas claras.

Requisitos​

La aplicación debe:

  • registrar libros y revistas en un catálogo de capacidad fija;
  • identificar cada material mediante un código;
  • prestar y devolver solo los materiales prestables;
  • impedir un préstamo repetido y una devolución imposible;
  • listar materiales usando polimorfismo;
  • separar modelo, servicio y punto de entrada en packages.

Usaremos arrays, ya conocidos en Java 1. No usaremos colecciones, ficheros, excepciones personalizadas, Maven ni tests automatizados.

Modelo y decisiones​

  • Material es abstracta: comparte código y título, pero cada subtipo describe sus datos.
  • Libro y Revista son materiales: aquí la herencia representa «es un».
  • Prestable expresa una capacidad. Un libro se presta; una revista de consulta no tiene por qué hacerlo.
  • Biblioteca tiene un array de materiales y coordina búsquedas.
  • EstadoPrestamo limita los estados posibles.
Biblioteca → tiene → Material[]
Material ← Libro (también implementa Prestable)
Material ← Revista
Libro → usa → EstadoPrestamo

Estructura​

src/
├── app/Main.java
├── modelo/EstadoPrestamo.java
├── modelo/Material.java
├── modelo/Libro.java
├── modelo/Revista.java
├── modelo/Prestable.java
└── servicio/Biblioteca.java

Implementación​

src/modelo/EstadoPrestamo.java:

package modelo;
public enum EstadoPrestamo {
DISPONIBLE, PRESTADO
}

src/modelo/Prestable.java:

package modelo;
public interface Prestable {
boolean prestar();
boolean devolver();
}

src/modelo/Material.java:

package modelo;
public abstract class Material {
private String codigo;
private String titulo;

public Material(String codigo, String titulo) {
this.codigo = codigo;
this.titulo = titulo;
}

public String getCodigo() { return codigo; }
public String getTitulo() { return titulo; }
public abstract String descripcion();
}

src/modelo/Libro.java:

package modelo;
public class Libro extends Material implements Prestable {
private String autor;
private EstadoPrestamo estado;

public Libro(String codigo, String titulo, String autor) {
super(codigo, titulo);
this.autor = autor;
this.estado = EstadoPrestamo.DISPONIBLE;
}

@Override
public boolean prestar() {
if (estado != EstadoPrestamo.DISPONIBLE) return false;
estado = EstadoPrestamo.PRESTADO;
return true;
}

@Override
public boolean devolver() {
if (estado != EstadoPrestamo.PRESTADO) return false;
estado = EstadoPrestamo.DISPONIBLE;
return true;
}

@Override
public String descripcion() {
return "Libro " + getCodigo() + ": " + getTitulo()
+ " - " + autor + " - " + estado;
}
}

src/modelo/Revista.java:

package modelo;
public class Revista extends Material {
private int numero;

public Revista(String codigo, String titulo, int numero) {
super(codigo, titulo);
this.numero = numero;
}

@Override
public String descripcion() {
return "Revista " + getCodigo() + ": " + getTitulo()
+ " - número " + numero;
}
}

src/servicio/Biblioteca.java:

package servicio;

import modelo.Material;
import modelo.Prestable;

public class Biblioteca {
private Material[] catalogo;
private int cantidad;

public Biblioteca(int capacidad) {
catalogo = new Material[capacidad];
cantidad = 0;
}

public boolean registrar(Material material) {
if (material == null || cantidad == catalogo.length
|| buscar(material.getCodigo()) != null) {
return false;
}
catalogo[cantidad] = material;
cantidad++;
return true;
}

public Material buscar(String codigo) {
for (int i = 0; i < cantidad; i++) {
if (catalogo[i].getCodigo().equals(codigo)) return catalogo[i];
}
return null;
}

public boolean prestar(String codigo) {
Material material = buscar(codigo);
if (!(material instanceof Prestable)) return false;
return ((Prestable) material).prestar();
}

public boolean devolver(String codigo) {
Material material = buscar(codigo);
if (!(material instanceof Prestable)) return false;
return ((Prestable) material).devolver();
}

public void listar() {
for (int i = 0; i < cantidad; i++) {
System.out.println(catalogo[i].descripcion());
}
}
}

Aquí el instanceof responde a una necesidad concreta: el catálogo mezcla materiales y solo algunos cumplen Prestable. El cast se realiza después de comprobar el contrato.

src/app/Main.java:

package app;

import modelo.Libro;
import modelo.Revista;
import servicio.Biblioteca;

public class Main {
public static void main(String[] args) {
Biblioteca biblioteca = new Biblioteca(3);

System.out.println(biblioteca.registrar(
new Libro("L1", "Código limpio", "Ana Ruiz")));
System.out.println(biblioteca.registrar(
new Revista("R1", "Java Hoy", 12)));
System.out.println(biblioteca.registrar(
new Libro("L1", "Duplicado", "Otra autora")));

System.out.println(biblioteca.prestar("L1"));
System.out.println(biblioteca.prestar("L1"));
System.out.println(biblioteca.prestar("R1"));
biblioteca.listar();
System.out.println(biblioteca.devolver("L1"));
}
}

Compilar y ejecutar​

Desde la raíz del proyecto:

mkdir out
javac -d out src/modelo/EstadoPrestamo.java src/modelo/Prestable.java \
src/modelo/Material.java src/modelo/Libro.java src/modelo/Revista.java \
src/servicio/Biblioteca.java src/app/Main.java
java -cp out app.Main

Casos manuales esperados​

CasoResultado
registrar L1true
registrar R1true
repetir código L1false
prestar L1 disponibletrue
volver a prestar L1false
prestar revista R1false
devolver L1true
buscar código inexistentenull

Prueba también una biblioteca de capacidad cero y el registro de null: ambos deben devolver false sin romper el programa.

Revisión del diseño​

La encapsulación impide cambiar el estado desde fuera. El enum evita estados inventados. La clase abstracta comparte identidad entre materiales, mientras la interfaz separa la capacidad de préstamo de la jerarquía. Biblioteca compone materiales y protege capacidad y códigos únicos.

No hemos creado Libro extends Biblioteca ni Revista extends Libro: esas relaciones serían falsas. Tampoco hemos incluido entrada por teclado en el modelo; Main es el límite de la aplicación.

Mini reto​

Añade un Dvd prestable con duración en minutos.

Criterios: debe extender Material, implementar Prestable, proteger su estado y funcionar en Biblioteca sin modificar los recorridos.

Solución orientativa: sigue la estructura de Libro, sustituye autor por duración y sobrescribe descripcion(). Que el servicio no necesite cambios demuestra el valor del contrato y del polimorfismo.

Posibles ampliaciones​

Más adelante podrían añadirse almacenamiento en ficheros, colecciones dinámicas, fechas de devolución, excepciones y pruebas automatizadas. No son requisitos ocultos de este proyecto: la versión actual queda completa y verificable con Java 2.