Menu
Coddy logo textTech

車両レンタルサービス

CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 91/91。

challenge icon

チャレンジ

簡単

車両レンタルサービスシステムを構築しましょう。このシステムでは、車両フリートを管理し、顧客のレンタル状況を追跡し、車両タイプに基づいて料金を計算します。この包括的な課題では、継承、インターフェース、カプセル化、Stateパターンを組み合わせて、現実的なレンタル事業アプリケーションを作成します。

コードを7つのファイルに整理します。

  • VehicleState.php:車両が取り得るさまざまな状態を表すVehicleStateインターフェースを定義します。getStatus(): stringcanRent(): boolの2つのメソッドを含めます。次に、このインターフェースを実装する3つのクラスを作成します。
    • AvailableState"Available"trueを返します
    • RentedState"Rented"falseを返します
    • MaintenanceState"Maintenance"falseを返します
  • Vehicle.php:VehicleStateファイルを読み込みます。licensePlate(string、public readonly)、brand(string、public readonly)、dailyRate(float、protected)のコンストラクタプロモーションを持つ抽象Vehicleクラスを作成します。現在の状態をprivateプロパティに保存し、デフォルト値をAvailableStateにします。以下を実装します。
    • getState(): VehicleState:現在の状態を返します
    • setState(VehicleState $state): void:状態を変更します
    • getStatus(): string:状態のgetStatus()に処理を委譲します
    • canRent(): bool:状態のcanRent()に処理を委譲します
    • abstract calculateCost(int $days): float:車両タイプごとに異なる方法で料金を計算します
    • getType(): stringstatic::classを使用してクラス名を返します
  • Vehicles.php:Vehicleファイルを読み込みます。具体的な車両クラスを3つ作成します。
    • Car:Vehicleを継承し、calculateCost()dailyRate * daysを返します
    • Motorcycle:Vehicleを継承し、calculateCost()dailyRate * days * 0.8を返します(20%割引)
    • Van:追加の$mileageRate(float)パラメータを持つVehicleのサブクラスです。calculateCost(int $days, int $miles = 0)(dailyRate * days) + (mileageRate * miles)を返します
  • Customer.phpid(int、public readonly)とname(string、public readonly)のコンストラクタプロモーションを持つCustomerクラスを作成します。レンタル履歴を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(int、private)、任意のmiles(int、private、デフォルト値0)にコンストラクタプロモーションを使用します。以下を実装します。
    • calculateTotal(): float:車両がVanの場合はmilesをcalculateCost()に渡し、それ以外の場合はdaysのみを渡します
    • complete(): string:合計を計算し、顧客の履歴に追加し、車両をAvailableStateに設定して、"Rental completed: $[total]"(小数点以下2桁)を返します
  • RentalService.php:RentalファイルとVehiclesファイルを読み込みます。フリートを管理するRentalServiceクラスを作成します。車両をlicense plateをキーとするprivate配列に保存します。以下を実装します。
    • addVehicle(Vehicle $vehicle): void:フリートに追加します
    • findVehicle(string $plate): ?Vehicle:車両またはnullを返します
    • getAvailableVehicles(): arraycanRent()が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"}]

    レンタルサービスを作成し、すべての車両をフリートに追加して、顧客を作成します。アクティブなレンタルをplateごとに管理します。各操作を処理します。

    • "available""Available vehicles: [count]"を出力します
    • "rent":車両をレンタルします。成功した場合はRentalを保存し、"Rented [plate] for [days] days"を出力します。Vanの場合は操作データのmilesを含めます。それ以外の場合はエラーメッセージを出力します
    • "complete":そのplateに対応する保存済みのレンタルを完了し、結果を出力します
    • "maintenance"setMaintenance()の結果を出力します
    • "status":指定されたplateについて"[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: customerDataからCustomerインスタンスを作成する

// TODO: プレートでアクティブなレンタルを追跡する配列を作成する
$activeRentals = [];

// TODO: 各操作を処理して結果を出力する:
// - "available": Print "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: Print available vehicle count
            break;
        case 'rent':
            // TODO: 車両をレンタルして結果を出力する
            break;
        case 'complete':
            // TODO: レンタルを完了して結果を出力する
            break;
        case 'maintenance':
            // TODO: メンテナンスを設定して結果を出力する
            break;
        case 'status':
            // TODO: 車両のステータスを出力する
            break;
        case 'customer_stats':
            // TODO: 顧客の統計情報を出力する
            break;
    }
}

?>

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: PHPオンラインコンパイラ