Menu
Coddy logo textTech

Servicio de alquiler de vehículos

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

challenge icon

Desafío

Fácil

Vamos a construir un sistema de Servicio de Alquiler de Vehículos que gestione una flota de vehículos, realice el seguimiento de los alquileres de los clientes y calcule los precios en función del tipo de vehículo. Este desafío integral reúne herencia, interfaces, encapsulamiento y el patrón State para crear una aplicación de negocio de alquiler realista.

Organizarás tu código en siete archivos:

  • VehicleState.php — Define una interfaz VehicleState que represente los diferentes estados en los que puede estar un vehículo. Incluye dos métodos: getStatus(): string y canRent(): bool. Luego crea tres clases que implementen esta interfaz:
    • AvailableState — devuelve "Available" y true
    • RentedState — devuelve "Rented" y false
    • MaintenanceState — devuelve "Maintenance" y false
  • Vehicle.php — Incluye el archivo VehicleState. Crea una clase abstracta Vehicle con promoción de constructor para licensePlate (string, public readonly), brand (string, public readonly) y dailyRate (float, protected). Almacena el estado actual en una propiedad privada, por defecto en AvailableState. Implementa:
    • getState(): VehicleState — devuelve el estado actual
    • setState(VehicleState $state): void — cambia el estado
    • getStatus(): string — delega al getStatus() del estado
    • canRent(): bool — delega al canRent() del estado
    • abstract calculateCost(int $days): float — cada tipo de vehículo calcula de forma diferente
    • getType(): string — devuelve el nombre de la clase usando static::class
  • Vehicles.php — Incluye el archivo Vehicle. Crea tres clases de vehículos concretas:
    • Car — extiende de Vehicle; calculateCost() devuelve dailyRate * days
    • Motorcycle — extiende de Vehicle; calculateCost() devuelve dailyRate * days * 0.8 (20% de descuento)
    • Van — extiende de Vehicle con un parámetro adicional $mileageRate (float); calculateCost(int $days, int $miles = 0) devuelve (dailyRate * days) + (mileageRate * miles)
  • Customer.php — Crea una clase Customer con promoción de constructor para id (int, public readonly) y name (string, public readonly). Realiza el seguimiento del historial de alquileres en un array privado. Implementa:
    • addRental(string $vehiclePlate, float $cost): void — añade al historial
    • getRentalCount(): int — devuelve el número de alquileres
    • getTotalSpent(): float — devuelve la suma de todos los costes de alquiler
  • Rental.php — Incluye los archivos Vehicle y Customer. Crea una clase Rental que vincule a un cliente con un vehículo. Usa promoción de constructor para customer (Customer, public readonly), vehicle (Vehicle, public readonly), days (int, private) y, opcionalmente, miles (int, private, por defecto 0). Implementa:
    • calculateTotal(): float — si el vehículo es una Van, pasa miles a calculateCost(); de lo contrario, solo pasa days
    • complete(): string — calcula el total, lo añade al historial del cliente, establece el vehículo en AvailableState y devuelve "Rental completed: $[total]" (con 2 decimales)
  • RentalService.php — Incluye los archivos Rental y Vehicles. Crea una clase RentalService que gestione la flota. Almacena los vehículos en un array privado indexado por la matrícula (license plate). Implementa:
    • addVehicle(Vehicle $vehicle): void — añade a la flota
    • findVehicle(string $plate): ?Vehicle — devuelve el vehículo o null
    • getAvailableVehicles(): array — devuelve los vehículos donde canRent() es true
    • rentVehicle(Customer $customer, string $plate, int $days, int $miles = 0): string|Rental — si no se encuentra el vehículo, devuelve "Vehicle not found"; si no está disponible, devuelve "Vehicle is not available"; de lo contrario, establece el vehículo en RentedState y devuelve un nuevo objeto Rental
    • setMaintenance(string $plate): string — establece el vehículo en MaintenanceState; devuelve "[plate] set to maintenance" o "Vehicle not found"
  • main.php — Incluye los archivos RentalService y Customer. Recibirás tres entradas: datos de la flota (JSON), datos del cliente (JSON) y operaciones (JSON).

    Formato JSON de la flota:

    [{"type": "car", "plate": "CAR-001", "brand": "Toyota", "rate": 50}, {"type": "van", "plate": "VAN-001", "brand": "Ford", "rate": 80, "mileage_rate": 0.25}]

    Formato JSON del cliente:

    {"id": 1, "name": "Alice"}

    Formato JSON de las operaciones:

    [{"op": "available"}, {"op": "rent", "plate": "CAR-001", "days": 3}, {"op": "complete", "plate": "CAR-001"}, {"op": "maintenance", "plate": "VAN-001"}, {"op": "customer_stats"}]

    Crea el servicio de alquiler, añade todos los vehículos a la flota y crea el cliente. Mantén un registro de los alquileres activos por matrícula. Procesa cada operación:

    • "available" — Imprime "Available vehicles: [count]"
    • "rent" — Alquila el vehículo; si tiene éxito, almacena el Rental e imprime "Rented [plate] for [days] days"; si es Van, incluye miles de la operación; de lo contrario, imprime el mensaje de error
    • "complete" — Completa el alquiler almacenado para esa matrícula; imprime el resultado
    • "maintenance" — Imprime el resultado de setMaintenance()
    • "status" — Imprime "[plate]: [status]" para la matrícula dada
    • "customer_stats" — Imprime "[name]: [count] rentals, $[total] spent" (con 2 decimales)

    Imprime cada resultado en una línea nueva.

Este sistema muestra cómo el patrón State maneja elegantemente la disponibilidad de vehículos sin condicionales complejos, cómo la herencia crea una jerarquía de vehículos limpia con precios polimórficos y cómo el encapsulamiento protege la integridad de los datos de alquiler a lo largo de todo el flujo de trabajo.

Pruébalo tú mismo

<?php

require_once 'RentalService.php';
require_once 'Customer.php';

// Leer entradas
$fleetJson = trim(fgets(STDIN));
$customerJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));

// Analizar datos JSON
$fleetData = (array)json_decode($fleetJson, true);
$customerData = (array)json_decode($customerJson, true);
$operations = (array)json_decode($operationsJson, true);

// TODO: Crear instancia de RentalService

// TODO: Agregar todos los vehículos a la flota según el tipo:
// - "car" -> new Car(plate, brand, rate)
// - "motorcycle" -> new Motorcycle(plate, brand, rate)
// - "van" -> new Van(plate, brand, rate, mileage_rate)

// TODO: Crear instancia de Customer a partir de customerData

// TODO: Crear un array para rastrear alquileres activos por matrícula
$activeRentals = [];

// TODO: Procesar cada operación e imprimir resultados:
// - "available": Imprimir "Available vehicles: [count]"
// - "rent": Alquilar vehículo, almacenar Rental si tiene éxito, imprimir resultado
// - "complete": Completar el alquiler, imprimir resultado
// - "maintenance": Establecer mantenimiento, imprimir resultado
// - "status": Imprimir "[plate]: [status]"
// - "customer_stats": Imprimir "[name]: [count] rentals, $[total] spent"

foreach ($operations as $op) {
    $operation = (array)$op;
    $opType = $operation['op'];
    
    // TODO: Manejar cada tipo de operación
    switch ($opType) {
        case 'available':
            // TODO: Imprimir el recuento de vehículos disponibles
            break;
        case 'rent':
            // TODO: Alquilar vehículo e imprimir resultado
            break;
        case 'complete':
            // TODO: Completar el alquiler e imprimir resultado
            break;
        case 'maintenance':
            // TODO: Establecer mantenimiento e imprimir resultado
            break;
        case 'status':
            // TODO: Imprimir el estado del vehículo
            break;
        case 'customer_stats':
            // TODO: Imprimir estadísticas del cliente
            break;
    }
}

?>

Todas las lecciones de Programación Orientada a Objetos