車両レンタルサービス
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 91/91。
チャレンジ
簡単車両フリートを管理し、顧客のレンタルを追跡し、車両タイプに基づいて料金を計算する車両レンタルサービスシステムを構築しましょう。この包括的なチャレンジでは、継承、インターフェース、カプセル化、および State パターンを組み合わせて、現実的なレンタルビジネスアプリケーションを作成します。
コードは以下の7つのファイルに分けて構成します:
VehicleState.php— 車両が取り得る異なる状態を表すVehicleStateインターフェースを定義します。getStatus(): stringとcanRent(): 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(): string—static::classを使用してクラス名を返します
Vehicles.php— Vehicle ファイルをインクルードします。3つの具体的な車両クラスを作成します: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—id(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 の場合はcalculateCost()に miles を渡します。それ以外の場合は 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 ファイルをインクルードします。フリートデータ (JSON)、顧客データ (JSON)、および操作 (JSON) の3つの入力を受け取ります。フリート 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桁) を出力します
各結果を新しい行に出力してください。
このシステムは、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": "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;
}
}
?>