Сервис аренды транспорта
Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 91 из 91.
Задание
ЛегкоДавайте создадим систему Vehicle Rental Service (Сервис аренды автомобилей), которая управляет парком транспортных средств, отслеживает аренду клиентами и рассчитывает стоимость в зависимости от типа транспортного средства. Эта комплексная задача объединяет наследование, интерфейсы, инкапсуляцию и паттерн State (Состояние) для создания реалистичного приложения для арендного бизнеса.
Вы организуете свой код в семи файлах:
VehicleState.php— Определите интерфейсVehicleState, который представляет различные состояния, в которых может находиться транспортное средство. Включите два метода:getStatus(): stringиcanRent(): bool. Затем создайте три класса, реализующих этот интерфейс:AvailableState— возвращает"Available"иtrueRentedState— возвращает"Rented"иfalseMaintenanceState— возвращает"Maintenance"иfalse
Vehicle.php— Подключите файл VehicleState. Создайте абстрактный классVehicleс продвижением конструктора (constructor promotion) дляlicensePlate(string, public readonly),brand(string, public readonly) иdailyRate(float, protected). Храните текущее состояние в приватном свойстве, по умолчаниюAvailableState. Реализуйте:getState(): VehicleState— возвращает текущее состояниеsetState(VehicleState $state): void— изменяет состояниеgetStatus(): string— делегирует выполнение методуgetStatus()состоянияcanRent(): bool— делегирует выполнение методуcanRent()состоянияabstract calculateCost(int $days): float— каждый тип транспортного средства рассчитывает стоимость по-разномуgetType(): string— возвращает имя класса, используяstatic::class
Vehicles.php— Подключите файл Vehicle. Создайте три конкретных класса транспортных средств:Car— наследует Vehicle;calculateCost()возвращаетdailyRate * daysMotorcycle— наследует Vehicle;calculateCost()возвращаетdailyRate * days * 0.8(скидка 20%)Van— наследует Vehicle с дополнительным параметром$mileageRate(float);calculateCost(int $days, int $miles = 0)возвращает(dailyRate * days) + (mileageRate * miles)
Customer.php— Создайте классCustomerс продвижением конструктора дляid(int, public readonly) иname(string, public readonly). Отслеживайте историю аренды в приватном массиве. Реализуйте:addRental(string $vehiclePlate, float $cost): void— добавляет запись в историюgetRentalCount(): int— возвращает количество арендgetTotalSpent(): float— возвращает сумму всех затрат на аренду
Rental.php— Подключите файлы Vehicle и Customer. Создайте классRental, который связывает клиента с транспортным средством. Используйте продвижение конструктора дляcustomer(Customer, public readonly),vehicle(Vehicle, public readonly),days(int, private) и, опционально,miles(int, private, по умолчанию 0). Реализуйте:calculateTotal(): float— если транспортное средство являетсяVan, передайте мили вcalculateCost(); в противном случае передайте только дниcomplete(): string— рассчитывает общую сумму, добавляет её в историю клиента, переводит транспортное средство вAvailableState, возвращает"Rental completed: $[total]"(с 2 знаками после запятой)
RentalService.php— Подключите файлы Rental и Vehicles. Создайте классRentalService, который управляет автопарком. Храните транспортные средства в приватном массиве, индексированном по номерному знаку. Реализуйте:addVehicle(Vehicle $vehicle): void— добавляет транспортное средство в паркfindVehicle(string $plate): ?Vehicle— возвращает транспортное средство или nullgetAvailableVehicles(): array— возвращает транспортные средства, для которыхcanRent()истинноrentVehicle(Customer $customer, string $plate, int $days, int $miles = 0): string|Rental— если транспортное средство не найдено, вернуть"Vehicle not found"; если недоступно, вернуть"Vehicle is not available"; в противном случае перевести транспортное средство вRentedStateи вернуть новый объект RentalsetMaintenance(string $plate): string— переводит транспортное средство вMaintenanceState; возвращает"[plate] set to maintenance"или"Vehicle not found"
main.php— Подключите файлы RentalService и Customer. Вы получите три входных параметра: данные о парке (JSON), данные о клиенте (JSON) и операции (JSON).Формат JSON для автопарка:
[{"type": "car", "plate": "CAR-001", "brand": "Toyota", "rate": 50}, {"type": "van", "plate": "VAN-001", "brand": "Ford", "rate": 80, "mileage_rate": 0.25}]Формат JSON для клиента:
{"id": 1, "name": "Alice"}Формат JSON для операций:
[{"op": "available"}, {"op": "rent", "plate": "CAR-001", "days": 3}, {"op": "complete", "plate": "CAR-001"}, {"op": "maintenance", "plate": "VAN-001"}, {"op": "customer_stats"}]Создайте сервис аренды, добавьте все транспортные средства в парк и создайте клиента. Отслеживайте активные аренды по номерному знаку. Обработайте каждую операцию:
"available"— Вывести"Available vehicles: [count]""rent"— Арендовать транспортное средство; в случае успеха сохранить Rental и вывести"Rented [plate] for [days] days"; если этоVan, включить мили из операции; в противном случае вывести сообщение об ошибке"complete"— Завершить сохраненную аренду для этого номера; вывести результат"maintenance"— Вывести результатsetMaintenance()"status"— Вывести"[plate]: [status]"для данного номера"customer_stats"— Вывести"[name]: [count] rentals, $[total] spent"(с 2 знаками после запятой)
Выводите каждый результат на новой строке.
Эта система демонстрирует, как паттерн State элегантно управляет доступностью транспортных средств без сложных условий, как наследование создает чистую иерархию транспортных средств с полиморфным ценообразованием, и как инкапсуляция защищает целостность данных об аренде на протяжении всего рабочего процесса.
Попробуйте сами
<?php
require_once 'RentalService.php';
require_once 'Customer.php';
// Чтение входных данных
$fleetJson = trim(fgets(STDIN));
$customerJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));
// Парсинг данных JSON
$fleetData = (array)json_decode($fleetJson, true);
$customerData = (array)json_decode($customerJson, true);
$operations = (array)json_decode($operationsJson, true);
// TODO: Создать экземпляр RentalService
// TODO: Добавить все транспортные средства в парк в зависимости от типа:
// - "car" -> new Car(plate, brand, rate)
// - "motorcycle" -> new Motorcycle(plate, brand, rate)
// - "van" -> new Van(plate, brand, rate, mileage_rate)
// TODO: Создать экземпляр Customer из customerData
// TODO: Создать массив для отслеживания активных аренд по номерному знаку
$activeRentals = [];
// TODO: Обработать каждую операцию и вывести результаты:
// - "available": Вывести "Available vehicles: [count]"
// - "rent": Арендовать транспортное средство, сохранить Rental в случае успеха, вывести результат
// - "complete": Завершить аренду, вывести результат
// - "maintenance": Установить техническое обслуживание, вывести результат
// - "status": Вывести "[plate]: [status]"
// - "customer_stats": Вывести "[name]: [count] rentals, $[total] spent"
foreach ($operations as $op) {
$operation = (array)$op;
$opType = $operation['op'];
// TODO: Обработать каждый тип операции
switch ($opType) {
case 'available':
// TODO: Вывести количество доступных транспортных средств
break;
case 'rent':
// TODO: Арендовать транспортное средство и вывести результат
break;
case 'complete':
// TODO: Завершить аренду и вывести результат
break;
case 'maintenance':
// TODO: Установить техническое обслуживание и вывести результат
break;
case 'status':
// TODO: Вывести статус транспортного средства
break;
case 'customer_stats':
// TODO: Вывести статистику клиента
break;
}
}
?>Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Итоги — Простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыИтоги — Иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateПодробный разбор модификаторов доступаГеттеры и сеттерыСокрытие данныхConstructor Promotion (8.0)Итоги — Система учета студентов10Продвинутые концепции ООП
Композиция против наследованияВнедрение зависимостейАнонимные классыПеречисления (PHP 8.1)Файберы (PHP 8.1)Глубокое погружение в клонирование объектовГенераторы и итераторы13Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикПаттерн Репозиторий2Пространства имен и автозагрузка
Введение в пространства именКлючевое слово useСтандарт автозагрузки PSR-4Автозагрузчик ComposerИтоги: структурированный проект5Интерфейсы и контракты
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовИнтерфейс против абстрактного классаType Hinting с интерфейсамиИтоги — Калькулятор фигур8Магические методы
Введение в магические методы__toString и __debugInfo__get, __set, __isset, __unset__call и __callStatic__clone и клонирование объектов__serialize и __unserializeИтоги — Кастомная коллекция11Система типов и обработка ошибок
Объявление типовNullable-типыТипы объединения и пересеченияКлассы исключенийИерархия пользовательских исключенийTry, Catch, FinallyИтоги — Валидатор форм14Проект: Управление библиотекой
Обзор проектаКлассы Book и User3Свойства классов
Свойства экземпляра и статические свойстваКонстанты в классахСтатические методы и свойстваПриватные и защищенные свойстваСвойства Readonly (PHP 8.1)Итоги — Менеджер банковского счета6Полиморфизм
Снова о переопределении методовПолиморфизм через интерфейсыType Hinting и Union TypesПозднее статическое связываниеИтоги — Payment Processor9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy15Финальные испытания
Платформа онлайн-обученияБанковская системаРазработка игрового персонажаСервис аренды транспорта