Menu
Coddy logo textTech

Servicio de alquiler de vehículos

Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 87 de 87.

Para este desafío final, construirás un sistema de Servicio de Alquiler de Vehículos que reúne todos los conceptos de POO que has dominado a lo largo de este curso.

Su sistema debe modelar una flota de diferentes tipos de vehículos. Considere una clase base Vehicle con propiedades comunes como la matrícula, la marca y la tarifa de alquiler diaria. Las subclases como Car, Motorcycle y Truck pueden extender esto con atributos específicos como el número de asientos, el tamaño del motor o la capacidad de carga.

Las interfaces pueden definir comportamientos de alquiler: una interfaz Rentable podría declarar métodos como rent(), returnVehicle() y calculateCost(int days). Diferentes tipos de vehículos podrían implementar estrategias de precios de manera distinta, lo que convierte a esto en un caso de uso perfecto para el polimorfismo.

Piense en cómo la clase RentalAgency gestiona la flota mediante la composición.

Las excepciones personalizadas como VehicleNotAvailableException pueden manejar casos extremos.

Podrías aplicar el patrón Factory para crear vehículos o utilizar enums para las categorías de vehículos y el estado del alquiler.

Este desafío pone a prueba tu capacidad para diseñar un sistema de OOP completo y bien estructurado. ¡Buena suerte!

challenge icon

Desafío

Fácil

¡Vamos a construir un Servicio de Alquiler de Vehículos completo que reúna todo lo que has aprendido sobre OOP! Crearás un sistema que gestiona una flota de diferentes tipos de vehículos, maneja alquileres con la validación adecuada y utiliza un patrón de fábrica para crear vehículos.

Organizarás tu código en siete archivos:

  • RentalStatus.java: Crea un enum que represente el estado de alquiler de un vehículo. Incluye los valores AVAILABLE, RENTED y MAINTENANCE. Añade un campo privado description (String), un constructor y un getter. AVAILABLE debe tener la descripción "ready to rent", RENTED debe tener "currently in use" y MAINTENANCE debe tener "under service".
  • VehicleNotAvailableException.java: Crea una excepción personalizada que extienda de Exception. El constructor debe aceptar una matrícula (license plate) y crear un mensaje con el formato Vehicle not available: [licensePlate].
  • Rentable.java: Crea una interfaz que defina las operaciones de alquiler. Declara los métodos rent() que devuelve un boolean, returnVehicle() que devuelve un boolean y calculateCost(int days) que devuelve un double.
  • Vehicle.java: Crea una clase base abstracta para todos los vehículos. Incluye campos privados para licensePlate (String), brand (String), dailyRate (double) y status (RentalStatus, inicializado en AVAILABLE). El constructor recibe licensePlate, brand y dailyRate. Incluye los getters apropiados y un setter para status. Declara un método abstracto getVehicleType() que devuelva un String. Implementa un método getDetails() que devuelva "[licensePlate] [brand] ([vehicleType]) - $[dailyRate]/day - [status description]" con dailyRate formateado a 2 decimales.
  • Car.java: Crea una clase que extienda de Vehicle e implemente Rentable. Añade un campo privado seats (int). El constructor recibe licensePlate, brand, dailyRate y seats. Implementa getVehicleType() para que devuelva "Car". Para rent(), si el estado es AVAILABLE, cámbialo a RENTED y devuelve true; de lo contrario, devuelve false. Para returnVehicle(), si el estado es RENTED, cámbialo a AVAILABLE y devuelve true; de lo contrario, devuelve false. Para calculateCost(int days), devuelve dailyRate multiplicado por days.
  • Motorcycle.java: Crea una clase que extienda de Vehicle e implemente Rentable. Añade un campo privado engineCC (int). El constructor recibe licensePlate, brand, dailyRate y engineCC. Implementa getVehicleType() para que devuelva "Motorcycle". Implementa los métodos de Rentable de la misma manera que en Car, pero calculateCost() debe aplicar un descuento del 10% (multiplicar por 0.9).
  • RentalAgency.java: Crea una clase que gestione la flota de vehículos usando un ArrayList. Incluye los métodos:
    • addVehicle(Vehicle vehicle) - añade un vehículo a la flota
    • findVehicle(String licensePlate) - devuelve el Vehicle o null
    • rentVehicle(String licensePlate, int days) - busca el vehículo, intenta alquilarlo (haciendo casting a Rentable) y devuelve el costo del alquiler. Lanza VehicleNotAvailableException si el vehículo no se encuentra o si rent() devuelve false
    • returnVehicle(String licensePlate) - busca y devuelve el vehículo, retornando true si tiene éxito
    • getAvailableVehicles() - devuelve un ArrayList de vehículos con estado AVAILABLE
  • Main.java: ¡Construye tu servicio de alquiler! Recibirás una cadena de comandos separados por comas:
    • ADD_CAR:licensePlate:brand:dailyRate:seats
    • ADD_MOTORCYCLE:licensePlate:brand:dailyRate:engineCC
    • RENT:licensePlate:days
    • RETURN:licensePlate
    • AVAILABLE

    Procesa cada comando e imprime:

    • ADD_CAR/ADD_MOTORCYCLE: Added: [brand] ([vehicleType])
    • RENT: Rented [licensePlate] for [days] days - Total: $[cost] (costo con 2 decimales), o Error: [exception message]
    • RETURN: Returned: [licensePlate] o Return failed: [licensePlate]
    • AVAILABLE: Imprime Available vehicles: seguido de los detalles de cada vehículo disponible en líneas separadas

    Después de todos los comandos, imprime --- Fleet Summary --- seguido de los detalles de cada vehículo.

Recibirás una entrada: la cadena de comandos separados por comas.

Por ejemplo, con la entrada ADD_CAR:ABC-123:Toyota:50.00:5,ADD_MOTORCYCLE:XYZ-789:Honda:40.00:600,RENT:ABC-123:3,RENT:ABC-123:2,AVAILABLE,RETURN:ABC-123, tu salida sería:

Added: Toyota (Car)
Added: Honda (Motorcycle)
Rented ABC-123 for 3 days - Total: $150.00
Error: Vehicle not available: ABC-123
Available vehicles:
XYZ-789 Honda (Motorcycle) - $40.00/day - ready to rent
Returned: ABC-123
--- Fleet Summary ---
ABC-123 Toyota (Car) - $50.00/day - ready to rent
XYZ-789 Honda (Motorcycle) - $40.00/day - ready to rent

¡Este desafío combina herencia (jerarquía de Vehicle), interfaces (Rentable), enums (RentalStatus), excepciones personalizadas, polimorfismo (tratar diferentes tipos de vehículos de manera uniforme) y composición (RentalAgency gestionando vehículos) en un sistema completo de servicio de alquiler!

Hoja de referencia

Este desafío final combina múltiples conceptos de POO en un sistema completo. Aquí están los patrones y técnicas clave:

Enums con campos

Los enums pueden tener campos, constructores y métodos:

public enum RentalStatus {
    AVAILABLE("ready to rent"),
    RENTED("currently in use");
    
    private String description;
    
    private RentalStatus(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

Excepciones personalizadas

Crea excepciones específicas del dominio extendiendo Exception:

public class VehicleNotAvailableException extends Exception {
    public VehicleNotAvailableException(String licensePlate) {
        super("Vehicle not available: " + licensePlate);
    }
}

Clases base abstractas con interfaces

Combina clases abstractas para implementaciones compartidas con interfaces para contratos de comportamiento:

public abstract class Vehicle {
    private String licensePlate;
    private RentalStatus status;
    
    public abstract String getVehicleType();
    
    public String getDetails() {
        return licensePlate + " " + getVehicleType() + " - " + status.getDescription();
    }
}

public interface Rentable {
    boolean rent();
    boolean returnVehicle();
    double calculateCost(int days);
}

public class Car extends Vehicle implements Rentable {
    public boolean rent() {
        if (getStatus() == RentalStatus.AVAILABLE) {
            setStatus(RentalStatus.RENTED);
            return true;
        }
        return false;
    }
}

Composición para la gestión

Usa la composición para gestionar colecciones de objetos:

public class RentalAgency {
    private ArrayList<Vehicle> fleet = new ArrayList<>();
    
    public void addVehicle(Vehicle vehicle) {
        fleet.add(vehicle);
    }
    
    public double rentVehicle(String licensePlate, int days) throws VehicleNotAvailableException {
        Vehicle vehicle = findVehicle(licensePlate);
        if (vehicle == null) {
            throw new VehicleNotAvailableException(licensePlate);
        }
        Rentable rentable = (Rentable) vehicle;
        if (!rentable.rent()) {
            throw new VehicleNotAvailableException(licensePlate);
        }
        return rentable.calculateCost(days);
    }
}

Polimorfismo en acción

Diferentes subclases pueden implementar los métodos de la interfaz de manera distinta:

// Car: precio estándar
public double calculateCost(int days) {
    return dailyRate * days;
}

// Motorcycle: precio con descuento
public double calculateCost(int days) {
    return dailyRate * days * 0.9;
}

Pruébalo tú mismo

import java.util.Scanner;
import java.util.ArrayList;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Crear una instancia de RentalAgency
        
        // TODO: Dividir la entrada por comas para obtener los comandos individuales
        
        // TODO: Procesar cada comando:
        // ADD_CAR:licensePlate:brand:dailyRate:seats -> imprimir "Added: [brand] ([vehicleType])"
        // ADD_MOTORCYCLE:licensePlate:brand:dailyRate:engineCC -> imprimir "Added: [brand] ([vehicleType])"
        // RENT:licensePlate:days -> imprimir "Rented [licensePlate] for [days] days - Total: $[cost]" (2 decimales)
        //                           o "Error: [exception message]" si se lanza una excepción
        // RETURN:licensePlate -> imprimir "Returned: [licensePlate]" o "Return failed: [licensePlate]"
        // AVAILABLE -> imprimir "Available vehicles:" seguido de los detalles de cada vehículo disponible
        
        // TODO: Después de todos los comandos, imprimir "--- Fleet Summary ---"
        // seguido de los detalles de cada vehículo
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos