Menu
CoddyTech

车辆租赁服务

本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 91 课,共 91 课。

challenge icon

挑战

简单

让我们构建一个车辆租赁服务系统,用于管理车队、跟踪客户租赁记录,并根据车辆类型计算价格。这个综合挑战会将继承、接口、封装和状态模式结合起来,创建一个真实的租车业务应用。

你需要将代码组织到 7 个文件中:

  • VehicleState.php:定义一个 VehicleState 接口,用来表示车辆可能处于的不同状态。接口包含两个方法:getStatus(): string 和 canRent(): bool。然后创建 3 个实现此接口的类:
    • AvailableState:返回 "Available" 和 true
    • RentedState:返回 "Rented" 和 false
    • MaintenanceState:返回 "Maintenance" 和 false
  • Vehicle.php:引入 VehicleState 文件。创建一个抽象的 Vehicle 类,并使用构造函数属性提升声明 licensePlate(字符串,public readonly)、brand(字符串,public readonly)和 dailyRate(浮点数,protected)。使用一个 private 属性保存当前状态,默认值为 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 文件。创建 3 个具体的车辆类:
    • Car:继承 Vehicle;calculateCost() 返回 dailyRate * days
    • Motorcycle:继承 Vehicle;calculateCost() 返回 dailyRate * days * 0.8(优惠 20%)
    • Van:继承 Vehicle,并添加一个 $mileageRate(浮点数)参数;calculateCost(int $days, int $miles = 0) 返回 (dailyRate * days) + (mileageRate * miles)
  • Customer.php:创建一个 Customer 类,并使用构造函数属性提升声明 id(整数,public readonly)和 name(字符串,public readonly)。使用 private 数组记录租赁历史。实现以下方法:
    • addRental(string $vehiclePlate, float $cost): void:将记录添加到历史中
    • getRentalCount(): int:返回租赁次数
    • getTotalSpent(): float:返回所有租赁费用的总和
  • Rental.php:引入 Vehicle 和 Customer 文件。创建一个 Rental 类,用于关联客户和车辆。使用构造函数属性提升声明 customer(Customer,public readonly)、vehicle(Vehicle,public readonly)、days(整数,private),以及可选的 miles(整数,private,默认值为 0)。实现以下方法:
    • calculateTotal(): float:如果车辆是 Van,就将 miles 传给 calculateCost();否则只传入 days
    • complete(): string:计算总费用,将记录添加到客户的历史中,将车辆状态设为 AvailableState,并返回 "Rental completed: $[total]"(保留 2 位小数)
  • RentalService.php:引入 Rental 和 Vehicles 文件。创建一个 RentalService 类来管理车队。使用 private 数组保存车辆,并以车牌号作为索引。实现以下方法:
    • addVehicle(Vehicle $vehicle): void:将车辆添加到车队
    • findVehicle(string $plate): ?Vehicle:返回车辆或 null
    • getAvailableVehicles(): array:返回 canRent() 为 true 的车辆
    • 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 文件。你会收到 3 个输入:车队数据(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,还要包含操作中的 miles;否则打印错误消息
    • "complete":完成该车牌号对应的已保存租赁;打印结果
    • "maintenance":打印 setMaintenance() 的结果
    • "status":打印指定车牌号对应的 "[plate]: [status]"
    • "customer_stats":打印 "[name]: [count] rentals, $[total] spent"(保留 2 位小数)

    每项结果都打印在新的一行。

这个系统展示了状态模式如何优雅地处理车辆可用状态,避免复杂的条件语句;继承如何通过多态定价构建清晰的车辆层级;以及封装如何在整个工作流程中保护租赁数据的完整性。

亲手试试

<?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:根据 customerData 创建 Customer 实例

// 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;
    }
}

?>

面向对象编程中的所有课

自己练习: PHP 在线编译器