Menu
CoddyTech

Dịch vụ cho thuê phương tiện

Thuộc phần Lập trình hướng đối tượng trong hành trình PHP của Coddy. Bài học 91 trên 91.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống dịch vụ cho thuê xe để quản lý đội xe, theo dõi các lượt thuê của khách hàng và tính giá dựa trên loại xe. Thử thách toàn diện này kết hợp tính kế thừa, giao diện, tính đóng gói và mẫu State để tạo một ứng dụng kinh doanh cho thuê xe thực tế.

Bạn sẽ sắp xếp code trong bảy tệp:

  • VehicleState.php: Định nghĩa giao diện VehicleState đại diện cho các trạng thái khác nhau của một xe. Thêm hai phương thức: getStatus(): string và canRent(): bool. Sau đó, tạo ba lớp triển khai giao diện này:
    • AvailableState: trả về "Available" và true
    • RentedState: trả về "Rented" và false
    • MaintenanceState: trả về "Maintenance" và false
  • Vehicle.php: Thêm tệp VehicleState. Tạo một lớp trừu tượng Vehicle, dùng tính năng khởi tạo thuộc tính từ tham số cho licensePlate (chuỗi, public readonly), brand (chuỗi, public readonly) và dailyRate (số thực, protected). Lưu trạng thái hiện tại trong một thuộc tính private, mặc định là AvailableState. Triển khai:
    • getState(): VehicleState: trả về trạng thái hiện tại
    • setState(VehicleState $state): void: thay đổi trạng thái
    • getStatus(): string: gọi phương thức getStatus() của trạng thái
    • canRent(): bool: gọi phương thức canRent() của trạng thái
    • abstract calculateCost(int $days): float: mỗi loại xe tính chi phí theo cách khác nhau
    • getType(): string: trả về tên lớp bằng static::class
  • Vehicles.php: Thêm tệp Vehicle. Tạo ba lớp xe cụ thể:
    • Car: kế thừa Vehicle; calculateCost() trả về dailyRate * days
    • Motorcycle: kế thừa Vehicle; calculateCost() trả về dailyRate * days * 0.8 (giảm giá 20%)
    • Van: kế thừa Vehicle và có thêm tham số $mileageRate (số thực); calculateCost(int $days, int $miles = 0) trả về (dailyRate * days) + (mileageRate * miles)
  • Customer.php: Tạo lớp Customer, dùng tính năng khởi tạo thuộc tính từ tham số cho id (số nguyên, public readonly) và name (chuỗi, public readonly). Theo dõi lịch sử thuê trong một mảng private. Triển khai:
    • addRental(string $vehiclePlate, float $cost): void: thêm vào lịch sử
    • getRentalCount(): int: trả về số lượt thuê
    • getTotalSpent(): float: trả về tổng chi phí của tất cả lượt thuê
  • Rental.php: Thêm các tệp Vehicle và Customer. Tạo lớp Rental liên kết khách hàng với một chiếc xe. Dùng tính năng khởi tạo thuộc tính từ tham số cho customer (Customer, public readonly), vehicle (Vehicle, public readonly), days (số nguyên, private) và tùy chọn miles (số nguyên, private, mặc định là 0). Triển khai:
    • calculateTotal(): float: nếu xe là Van, truyền miles vào calculateCost(); nếu không, chỉ truyền days
    • complete(): string: tính tổng chi phí, thêm vào lịch sử của khách hàng, đặt xe về AvailableState và trả về "Rental completed: $[total]" (2 chữ số thập phân)
  • RentalService.php: Thêm các tệp Rental và Vehicles. Tạo lớp RentalService để quản lý đội xe. Lưu xe trong một mảng private, dùng biển số làm khóa. Triển khai:
    • addVehicle(Vehicle $vehicle): void: thêm xe vào đội xe
    • findVehicle(string $plate): ?Vehicle: trả về xe hoặc null
    • getAvailableVehicles(): array: trả về các xe có canRent() là true
    • rentVehicle(Customer $customer, string $plate, int $days, int $miles = 0): string|Rental: nếu không tìm thấy xe, trả về "Vehicle not found"; nếu xe không khả dụng, trả về "Vehicle is not available"; nếu không, đặt xe thành RentedState và trả về một đối tượng Rental mới
    • setMaintenance(string $plate): string: đặt xe thành MaintenanceState; trả về "[plate] set to maintenance" hoặc "Vehicle not found"
  • main.php: Thêm các tệp RentalService và Customer. Bạn sẽ nhận ba đầu vào: dữ liệu đội xe (JSON), dữ liệu khách hàng (JSON) và các thao tác (JSON).

    Định dạng JSON của đội xe:

    [{"type": "car", "plate": "CAR-001", "brand": "Toyota", "rate": 50}, {"type": "van", "plate": "VAN-001", "brand": "Ford", "rate": 80, "mileage_rate": 0.25}]

    Định dạng JSON của khách hàng:

    {"id": 1, "name": "Alice"}

    Định dạng JSON của các thao tác:

    [{"op": "available"}, {"op": "rent", "plate": "CAR-001", "days": 3}, {"op": "complete", "plate": "CAR-001"}, {"op": "maintenance", "plate": "VAN-001"}, {"op": "customer_stats"}]

    Tạo dịch vụ cho thuê, thêm tất cả xe vào đội xe và tạo khách hàng. Theo dõi các lượt thuê đang hoạt động theo biển số. Xử lý từng thao tác:

    • "available": In ra "Available vehicles: [count]"
    • "rent": Cho thuê xe; nếu thành công, lưu Rental và in ra "Rented [plate] for [days] days"; nếu là Van, lấy miles từ thao tác; nếu không, in ra thông báo lỗi
    • "complete": Hoàn tất lượt thuê đã lưu cho biển số đó; in ra kết quả
    • "maintenance": In ra kết quả của setMaintenance()
    • "status": In ra "[plate]: [status]" cho biển số đã cho
    • "customer_stats": In ra "[name]: [count] rentals, $[total] spent" (2 chữ số thập phân)

    In từng kết quả trên một dòng mới.

Hệ thống này cho thấy mẫu State xử lý trạng thái khả dụng của xe một cách gọn gàng mà không cần các câu lệnh điều kiện phức tạp, tính kế thừa tạo nên hệ thống phân cấp xe rõ ràng với cách tính giá đa hình, còn tính đóng gói bảo vệ tính toàn vẹn của dữ liệu thuê trong suốt quy trình.

Tự làm thử

<?php

require_once 'RentalService.php';
require_once 'Customer.php';

// Đọc dữ liệu đầu vào
$fleetJson = trim(fgets(STDIN));
$customerJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));

// Phân tích dữ liệu JSON
$fleetData = (array)json_decode($fleetJson, true);
$customerData = (array)json_decode($customerJson, true);
$operations = (array)json_decode($operationsJson, true);

// TODO: Tạo instance của RentalService

// TODO: Thêm tất cả xe vào đội xe theo loại:
// - "car" -> new Car(plate, brand, rate)
// - "motorcycle" -> new Motorcycle(plate, brand, rate)
// - "van" -> new Van(plate, brand, rate, mileage_rate)

// TODO: Tạo instance của Customer từ customerData

// TODO: Tạo mảng để theo dõi các lượt thuê đang diễn ra theo biển số xe
$activeRentals = [];

// TODO: Xử lý từng thao tác và in ra kết quả:
// - "available": In ra "Available vehicles: [count]"
// - "rent": Cho thuê xe, lưu Rental nếu thành công và in ra kết quả
// - "complete": Hoàn tất lượt thuê, in ra kết quả
// - "maintenance": Đặt trạng thái bảo dưỡng, in ra kết quả
// - "status": In ra "[plate]: [status]"
// - "customer_stats": In ra "[name]: [count] rentals, $[total] spent"

foreach ($operations as $op) {
    $operation = (array)$op;
    $opType = $operation['op'];
    
    // TODO: Xử lý từng loại thao tác
    switch ($opType) {
        case 'available':
            // TODO: In ra số xe hiện có
            break;
        case 'rent':
            // TODO: Cho thuê xe và in ra kết quả
            break;
        case 'complete':
            // TODO: Hoàn tất lượt thuê và in ra kết quả
            break;
        case 'maintenance':
            // TODO: Đặt trạng thái bảo dưỡng và in ra kết quả
            break;
        case 'status':
            // TODO: In ra trạng thái xe
            break;
        case 'customer_stats':
            // TODO: In ra thống kê khách hàng
            break;
    }
}

?>

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch PHP online