车辆租赁服务
本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 91 课,共 91 课。
挑战
简单让我们构建一个车辆租赁服务系统,用于管理车队、跟踪客户租赁记录,并根据车辆类型计算价格。这个综合挑战会将继承、接口、封装和状态模式结合起来,创建一个真实的租车业务应用。
你需要将代码组织到 7 个文件中:
VehicleState.php:定义一个VehicleState接口,用来表示车辆可能处于的不同状态。接口包含两个方法:getStatus(): string和canRent(): bool。然后创建 3 个实现此接口的类:AvailableState:返回"Available"和trueRentedState:返回"Rented"和falseMaintenanceState:返回"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 * daysMotorcycle:继承 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();否则只传入 dayscomplete(): string:计算总费用,将记录添加到客户的历史中,将车辆状态设为 AvailableState,并返回"Rental completed: $[total]"(保留 2 位小数)
RentalService.php:引入 Rental 和 Vehicles 文件。创建一个RentalService类来管理车队。使用 private 数组保存车辆,并以车牌号作为索引。实现以下方法:addVehicle(Vehicle $vehicle): void:将车辆添加到车队findVehicle(string $plate): ?Vehicle:返回车辆或 nullgetAvailableVehicles(): 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 在线编译器