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!
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 valoresAVAILABLE,RENTEDyMAINTENANCE. Añade un campo privadodescription(String), un constructor y un getter.AVAILABLEdebe tener la descripción "ready to rent",RENTEDdebe tener "currently in use" yMAINTENANCEdebe tener "under service".VehicleNotAvailableException.java: Crea una excepción personalizada que extienda deException. El constructor debe aceptar una matrícula (license plate) y crear un mensaje con el formatoVehicle not available: [licensePlate].Rentable.java: Crea una interfaz que defina las operaciones de alquiler. Declara los métodosrent()que devuelve un boolean,returnVehicle()que devuelve un boolean ycalculateCost(int days)que devuelve un double.Vehicle.java: Crea una clase base abstracta para todos los vehículos. Incluye campos privados paralicensePlate(String),brand(String),dailyRate(double) ystatus(RentalStatus, inicializado enAVAILABLE). El constructor recibelicensePlate,brandydailyRate. Incluye los getters apropiados y un setter parastatus. Declara un método abstractogetVehicleType()que devuelva un String. Implementa un métodogetDetails()que devuelva"[licensePlate] [brand] ([vehicleType]) - $[dailyRate]/day - [status description]"condailyRateformateado a 2 decimales.Car.java: Crea una clase que extienda deVehiclee implementeRentable. Añade un campo privadoseats(int). El constructor recibelicensePlate,brand,dailyRateyseats. ImplementagetVehicleType()para que devuelva"Car". Pararent(), si el estado esAVAILABLE, cámbialo aRENTEDy devuelve true; de lo contrario, devuelve false. ParareturnVehicle(), si el estado esRENTED, cámbialo aAVAILABLEy devuelve true; de lo contrario, devuelve false. ParacalculateCost(int days), devuelvedailyRatemultiplicado pordays.Motorcycle.java: Crea una clase que extienda deVehiclee implementeRentable. Añade un campo privadoengineCC(int). El constructor recibelicensePlate,brand,dailyRateyengineCC. ImplementagetVehicleType()para que devuelva"Motorcycle". Implementa los métodos deRentablede la misma manera que enCar, perocalculateCost()debe aplicar un descuento del 10% (multiplicar por 0.9).RentalAgency.java: Crea una clase que gestione la flota de vehículos usando unArrayList. Incluye los métodos:addVehicle(Vehicle vehicle)- añade un vehículo a la flotafindVehicle(String licensePlate)- devuelve elVehicleo nullrentVehicle(String licensePlate, int days)- busca el vehículo, intenta alquilarlo (haciendo casting aRentable) y devuelve el costo del alquiler. LanzaVehicleNotAvailableExceptionsi el vehículo no se encuentra o sirent()devuelve falsereturnVehicle(String licensePlate)- busca y devuelve el vehículo, retornando true si tiene éxitogetAvailableVehicles()- devuelve unArrayListde vehículos con estadoAVAILABLE
Main.java: ¡Construye tu servicio de alquiler! Recibirás una cadena de comandos separados por comas:ADD_CAR:licensePlate:brand:dailyRate:seatsADD_MOTORCYCLE:licensePlate:brand:dailyRate:engineCCRENT:licensePlate:daysRETURN:licensePlateAVAILABLE
Procesa cada comando e imprime:
- ADD_CAR/ADD_MOTORCYCLE:
Added: [brand] ([vehicleType]) - RENT:
Rented [licensePlate] for [days] days - Total: $[cost](costo con 2 decimales), oError: [exception message] - RETURN:
Returned: [licensePlate]oReturn 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
}
}
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
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy14Desafíos finales
Plataforma de E-LearningSistema bancarioDesarrollo de personajes de videojuegosServicio de alquiler de vehículos3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator