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.
Desafío
FácilConstruyamos un sistema de Servicio de alquiler de vehículos que gestione una flota de vehículos, registre los alquileres de los clientes y calcule los precios según el tipo de vehículo. Este desafío integral reúne herencia, interfaces, encapsulación y el patrón State para crear una aplicación realista de gestión de alquileres.
Organizarás tu código en siete archivos:
VehicleState.php: Define una interfazVehicleStateque represente los diferentes estados en los que puede encontrarse un vehículo. Incluye dos métodos:getStatus(): stringycanRent(): bool. Después, crea tres clases que implementen esta interfaz:AvailableState: devuelve"Available"ytrueRentedState: devuelve"Rented"yfalseMaintenanceState: devuelve"Maintenance"yfalse
Vehicle.php: Incluye el archivo VehicleState. Crea una clase abstractaVehiclecon promoción del constructor paralicensePlate(string, public readonly),brand(string, public readonly) ydailyRate(float, protected). Almacena el estado actual en una propiedad privada, con el valor predeterminado deAvailableState. Implementa:getState(): VehicleState: devuelve el estado actualsetState(VehicleState $state): void: cambia el estadogetStatus(): string: delega en elgetStatus()del estadocanRent(): bool: delega en elcanRent()del estadoabstract calculateCost(int $days): float: cada tipo de vehículo calcula el coste de forma diferentegetType(): string: devuelve el nombre de la clase mediantestatic::class
Vehicles.php: Incluye el archivo Vehicle. Crea tres clases concretas de vehículos:Car: extiende Vehicle;calculateCost()devuelvedailyRate * daysMotorcycle: extiende Vehicle;calculateCost()devuelvedailyRate * days * 0.8(20 % de descuento)Van: extiende Vehicle con un parámetro adicional$mileageRate(float);calculateCost(int $days, int $miles = 0)devuelve(dailyRate * days) + (mileageRate * miles)
Customer.php: Crea una claseCustomercon promoción del constructor paraid(int, public readonly) yname(string, public readonly). Registra el historial de alquileres en un array privado. Implementa:addRental(string $vehiclePlate, float $cost): void: añade un alquiler al historialgetRentalCount(): int: devuelve el número de alquileresgetTotalSpent(): float: devuelve la suma de todos los costes de alquiler
Rental.php: Incluye los archivos Vehicle y Customer. Crea una claseRentalque vincule un cliente con un vehículo. Usa la promoción del constructor paracustomer(Customer, public readonly),vehicle(Vehicle, public readonly),days(int, private) y, opcionalmente,miles(int, private, valor predeterminado 0). Implementa:calculateTotal(): float: si el vehículo es un Van, pasa miles acalculateCost(); en caso contrario, solo pasa dayscomplete(): 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 claseRentalServiceque gestione la flota. Almacena los vehículos en un array privado indexado por la matrícula. Implementa:addVehicle(Vehicle $vehicle): void: añade el vehículo a la flotafindVehicle(string $plate): ?Vehicle: devuelve el vehículo o nullgetAvailableVehicles(): array: devuelve los vehículos para los quecanRent()es truerentVehicle(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 RentalsetMaintenance(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 del 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 del JSON del cliente:
{"id": 1, "name": "Alice"}Formato del JSON de 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 la operación tiene éxito, guarda el Rental e imprime"Rented [plate] for [days] days"; si es un 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 desetMaintenance()"status": imprime"[plate]: [status]"para la matrícula indicada"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 gestiona elegantemente la disponibilidad de los vehículos sin condicionales complejos, cómo la herencia crea una jerarquía de vehículos clara con precios polimórficos y cómo la encapsulación protege la integridad de los datos de alquiler durante 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));
// Parsear 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: Añadir 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 array para rastrear alquileres activos por plate
$activeRentals = [];
// TODO: Procesar cada operación e imprimir resultados:
// - "available": Print "Available vehicles: [count]"
// - "rent": Alquilar vehículo, almacenar Rental si es exitoso, 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: Print available vehicle count
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 las estadísticas del cliente
break;
}
}
?>Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases frente a objetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Repaso: calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasRepaso - Jerarquía de empleados7Encapsulación
Público, protegido, privadoModificadores de acceso en profundidadGetters y settersOcultación de la informaciónPromoción del constructor (8.0)Repaso: sistema de registros de estudiantes10Conceptos avanzados de OOP
Composición vs. herenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibras (PHP 8.1)Análisis profundo de la clonación de objetosGeneradores e iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Namespaces y carga automática
Introducción a los NamespacesLa palabra clave useEstándar de carga automática PSR-4Autoloader de ComposerRepaso: proyecto organizado5Interfaces y contratos
Introducción a las interfacesImplementación de interfacesImplementación de múltiples interfacesInterfaces vs. clase abstractaTipado con interfacesRepaso - Calculadora de formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeRepaso: colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Repaso: gestor de cuentas bancarias6Polimorfismo
Repaso de la sobrescritura de métodosPolimorfismo mediante interfacesAnotaciones de tipo y tipos uniónEnlace estático tardíoRepaso: procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos entre TraitsMétodos abstractos en TraitsTraits frente a la herencia12Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy15Desafíos finales
Plataforma de aprendizaje electrónicoSistema bancarioDesarrollo de personajes de videojuegosServicio de alquiler de vehículosPractica por tu cuenta: Compilador de PHP online