Service de location de véhicules
Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 91 sur 91.
Défi
FacileConstruisons un système de Service de Location de Véhicules qui gère une flotte de véhicules, suit les locations des clients et calcule les tarifs en fonction du type de véhicule. Ce défi complet réunit l'héritage, les interfaces, l'encapsulation et le patron de conception État (State pattern) pour créer une application réaliste de gestion de location.
Vous organiserez votre code sur sept fichiers :
VehicleState.php— Définissez une interfaceVehicleStatequi représente les différents états dans lesquels un véhicule peut se trouver. Incluez deux méthodes :getStatus(): stringetcanRent(): bool. Ensuite, créez trois classes implémentant cette interface :AvailableState— retourne"Available"ettrueRentedState— retourne"Rented"etfalseMaintenanceState— retourne"Maintenance"etfalse
Vehicle.php— Incluez le fichier VehicleState. Créez une classe abstraiteVehicleavec la promotion de constructeur pourlicensePlate(string, public readonly),brand(string, public readonly), etdailyRate(float, protected). Stockez l'état actuel dans une propriété privée, par défaut àAvailableState. Implémentez :getState(): VehicleState— retourne l'état actuelsetState(VehicleState $state): void— modifie l'étatgetStatus(): string— délègue augetStatus()de l'étatcanRent(): bool— délègue aucanRent()de l'étatabstract calculateCost(int $days): float— chaque type de véhicule calcule différemmentgetType(): string— retourne le nom de la classe en utilisantstatic::class
Vehicles.php— Incluez le fichier Vehicle. Créez trois classes de véhicules concrètes :Car— étend Vehicle ;calculateCost()retournedailyRate * daysMotorcycle— étend Vehicle ;calculateCost()retournedailyRate * days * 0.8(20% de réduction)Van— étend Vehicle avec un paramètre supplémentaire$mileageRate(float) ;calculateCost(int $days, int $miles = 0)retourne(dailyRate * days) + (mileageRate * miles)
Customer.php— Créez une classeCustomeravec la promotion de constructeur pourid(int, public readonly) etname(string, public readonly). Suivez l'historique des locations dans un tableau privé. Implémentez :addRental(string $vehiclePlate, float $cost): void— ajoute à l'historiquegetRentalCount(): int— retourne le nombre de locationsgetTotalSpent(): float— retourne la somme de tous les coûts de location
Rental.php— Incluez les fichiers Vehicle et Customer. Créez une classeRentalqui lie un client à un véhicule. Utilisez la promotion de constructeur pourcustomer(Customer, public readonly),vehicle(Vehicle, public readonly),days(int, private), et optionnellementmiles(int, private, par défaut 0). Implémentez :calculateTotal(): float— si le véhicule est un Van, passez miles àcalculateCost(); sinon passez simplement dayscomplete(): string— calcule le total, l'ajoute à l'historique du client, définit le véhicule sur AvailableState, retourne"Rental completed: $[total]"(2 décimales)
RentalService.php— Incluez les fichiers Rental et Vehicles. Créez une classeRentalServicequi gère la flotte. Stockez les véhicules dans un tableau privé indexé par la plaque d'immatriculation. Implémentez :addVehicle(Vehicle $vehicle): void— ajoute à la flottefindVehicle(string $plate): ?Vehicle— retourne le véhicule ou nullgetAvailableVehicles(): array— retourne les véhicules oùcanRent()est truerentVehicle(Customer $customer, string $plate, int $days, int $miles = 0): string|Rental— si le véhicule n'est pas trouvé, retourne"Vehicle not found"; s'il n'est pas disponible, retourne"Vehicle is not available"; sinon, définit le véhicule sur RentedState et retourne un nouvel objet RentalsetMaintenance(string $plate): string— définit le véhicule sur MaintenanceState ; retourne"[plate] set to maintenance"ou"Vehicle not found"
main.php— Incluez les fichiers RentalService et Customer. Vous recevrez trois entrées : les données de la flotte (JSON), les données du client (JSON) et les opérations (JSON).Format JSON de la flotte :
[{"type": "car", "plate": "CAR-001", "brand": "Toyota", "rate": 50}, {"type": "van", "plate": "VAN-001", "brand": "Ford", "rate": 80, "mileage_rate": 0.25}]Format JSON du client :
{"id": 1, "name": "Alice"}Format JSON des opérations :
[{"op": "available"}, {"op": "rent", "plate": "CAR-001", "days": 3}, {"op": "complete", "plate": "CAR-001"}, {"op": "maintenance", "plate": "VAN-001"}, {"op": "customer_stats"}]Créez le service de location, ajoutez tous les véhicules à la flotte et créez le client. Gardez une trace des locations actives par plaque. Traitez chaque opération :
"available"— Affiche"Available vehicles: [count]""rent"— Loue le véhicule ; en cas de succès, stocke le Rental et affiche"Rented [plate] for [days] days"; si c'est un Van, inclut les miles de l'opération ; sinon affiche le message d'erreur"complete"— Termine la location stockée pour cette plaque ; affiche le résultat"maintenance"— Affiche le résultat desetMaintenance()"status"— Affiche"[plate]: [status]"pour la plaque donnée"customer_stats"— Affiche"[name]: [count] rentals, $[total] spent"(2 décimales)
Affichez chaque résultat sur une nouvelle ligne.
Ce système montre comment le patron État gère élégamment la disponibilité des véhicules sans conditions complexes, comment l'héritage crée une hiérarchie de véhicules propre avec une tarification polymorphe, et comment l'encapsulation protège l'intégrité des données de location tout au long du flux de travail.
Essayez vous-même
<?php
require_once 'RentalService.php';
require_once 'Customer.php';
// Lire les entrées
$fleetJson = trim(fgets(STDIN));
$customerJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));
// Analyser les données JSON
$fleetData = (array)json_decode($fleetJson, true);
$customerData = (array)json_decode($customerJson, true);
$operations = (array)json_decode($operationsJson, true);
// TODO: Créer une instance de RentalService
// TODO: Ajouter tous les véhicules à la flotte selon le type :
// - "car" -> new Car(plate, brand, rate)
// - "motorcycle" -> new Motorcycle(plate, brand, rate)
// - "van" -> new Van(plate, brand, rate, mileage_rate)
// TODO: Créer une instance de Customer à partir de customerData
// TODO: Créer un tableau pour suivre les locations actives par plaque
$activeRentals = [];
// TODO: Traiter chaque opération et afficher les résultats :
// - "available": Afficher "Available vehicles: [count]"
// - "rent": Louer le véhicule, stocker Rental si réussi, afficher le résultat
// - "complete": Terminer la location, afficher le résultat
// - "maintenance": Mettre en maintenance, afficher le résultat
// - "status": Afficher "[plate]: [status]"
// - "customer_stats": Afficher "[name]: [count] rentals, $[total] spent"
foreach ($operations as $op) {
$operation = (array)$op;
$opType = $operation['op'];
// TODO: Gérer chaque type d'opération
switch ($opType) {
case 'available':
// TODO: Afficher le nombre de véhicules disponibles
break;
case 'rent':
// TODO: Louer le véhicule et afficher le résultat
break;
case 'complete':
// TODO: Terminer la location et afficher le résultat
break;
case 'maintenance':
// TODO: Mettre en maintenance et afficher le résultat
break;
case 'status':
// TODO: Afficher le statut du véhicule
break;
case 'customer_stats':
// TODO: Afficher les statistiques du client
break;
}
}
?>Toutes les leçons de Programmation Orientée Objet
1Les bases de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé $thisMéthodesPropriétésConstructeur (__construct)Destructeur (__destruct)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLe mot-clé parent::Redéfinition de méthodeLe mot-clé finalClasses abstraitesRécapitulatif - Hiérarchie des employés7Encapsulation
Public, Protégé, PrivéModificateurs d'accès en détailGetters et SettersMasquage d'informationPromotion de constructeur (8.0)Récapitulatif - Système de gestion d'étudiants10Concepts avancés de la POO
Composition vs HéritageInjection de dépendancesClasses anonymesÉnumérations (PHP 8.1)Fibers (PHP 8.1)Clonage d'objets : analyse approfondieGénérateurs et Itérateurs13Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository2Espaces de noms et Autoloading
Introduction aux espaces de nomsLe mot-clé useStandard d'Autoloading PSR-4L'Autoloader ComposerRécapitulatif - Projet organisé5Interfaces et Contrats
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesInterface vs Classe AbstraiteType Hinting avec les InterfacesRécapitulatif - Calculateur de Formes8Méthodes magiques
Introduction aux méthodes magiques__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Clonage d'objets__serialize & __unserializeRécapitulatif - Collection personnalisée3Propriétés de classe
Propriétés d'instance vs Propriétés statiquesConstantes dans les classesMéthodes et propriétés statiquesPropriétés privées et protégéesPropriétés readonly (PHP 8.1)Récapitulatif - Gestionnaire de compte bancaire6Polymorphisme
Retour sur la redéfinition de méthodePolymorphisme via les interfacesType Hinting et Types d'unionLiaison statique tardiveRécapitulatif - Processeur de paiement9Traits
Introduction aux TraitsUtilisation de plusieurs TraitsRésolution des conflits de TraitsMéthodes abstraites dans les TraitsTraits vs Héritage12Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie15Défis finaux
Plateforme d'E-LearningSystème bancaireDéveloppement de personnages de jeuService de location de véhicules