Menu
Coddy logo textTech

Сервис аренды транспорта

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 91 из 91.

challenge icon

Задание

Легко

Давайте создадим систему Vehicle Rental Service (Сервис аренды автомобилей), которая управляет парком транспортных средств, отслеживает аренду клиентами и рассчитывает стоимость в зависимости от типа транспортного средства. Эта комплексная задача объединяет наследование, интерфейсы, инкапсуляцию и паттерн State (Состояние) для создания реалистичного приложения для арендного бизнеса.

Вы организуете свой код в семи файлах:

  • VehicleState.php — Определите интерфейс VehicleState, который представляет различные состояния, в которых может находиться транспортное средство. Включите два метода: getStatus(): string и canRent(): bool. Затем создайте три класса, реализующих этот интерфейс:
    • AvailableState — возвращает "Available" и true
    • RentedState — возвращает "Rented" и false
    • MaintenanceState — возвращает "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 * days
    • Motorcycle — наследует 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 — возвращает транспортное средство или null
    • getAvailableVehicles(): array — возвращает транспортные средства, для которых canRent() истинно
    • rentVehicle(Customer $customer, string $plate, int $days, int $miles = 0): string|Rental — если транспортное средство не найдено, вернуть "Vehicle not found"; если недоступно, вернуть "Vehicle is not available"; в противном случае перевести транспортное средство в RentedState и вернуть новый объект Rental
    • setMaintenance(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;
    }
}

?>

Все уроки раздела Объектно-ориентированное программирование