Car Rental System
Problem Statement
Section titled “Problem Statement”Design a car rental system that allows customers to search for available vehicles, make reservations, pick up and return cars, and handle billing. The system should manage vehicle inventory across multiple locations, handle different vehicle types, and support various rental plans.
Requirements
Section titled “Requirements”Functional Requirements
Section titled “Functional Requirements”- Search vehicles by type, location, and dates
- Reserve vehicles for specific dates
- Handle vehicle pickup and return
- Calculate rental charges based on duration and vehicle type
- Support different rental plans (hourly, daily, weekly)
- Manage vehicle inventory across multiple branches
- Track vehicle maintenance and service schedules
- Handle insurance options
- Process payments and generate invoices
- Support membership and loyalty programs
Non-Functional Requirements
Section titled “Non-Functional Requirements”- Prevent double-booking of vehicles
- Handle high availability for search operations
- Real-time inventory updates
- Accurate billing calculations
- Scalable to multiple locations
Simplified Class Diagram
Section titled “Simplified Class Diagram”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
skinparam classBorderThickness 3skinparam ArrowThickness 1skinparam defaultFontSize 16skinparam classAttributeFontSize 18skinparam classFontSize 16
class RentalService { + searchVehicles() + createReservation() + pickupVehicle() + returnVehicle()}
class VehicleInventory { + checkAvailability() + reserveVehicle() + releaseVehicle()}
class Vehicle { + getDetails() + updateStatus()}
class Reservation { + calculateCost() + confirm() + cancel()}
class Customer { + getProfile() + verifyLicense()}
class PricingCalculator { + calculateRentalCost() + applyDiscounts()}
RentalService *-- VehicleInventoryRentalService *-- PricingCalculatorRentalService ..> ReservationReservation --> CustomerReservation --> VehicleVehicleInventory o-- Vehicle
@endumlSimplified Overview
Section titled “Simplified Overview”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startumlskinparam componentStyle rectangle
package "Rental Service" { [RentalService] as Service}
package "Fleet Management" { interface "IVehicle" as Vehicle interface "IVehicleInventory" as Inventory [VehicleType]}
package "Customer" { interface "ICustomer" as CustomerIntf interface "IDriverLicense" as License}
package "Rental & Billing" { interface "IRental" as Rental interface "IBill" as Bill}
package "Strategies" { interface "IPricingCalculator" as Pricing interface "IDiscountPolicy" as Discount interface "IAdditionalChargeCalculator" as Charges interface "IPaymentProcessor" as Payment}
[CarRentalDriver] --> Service : usesService *-- Inventory : composed ofService *-- Pricing : composed ofService *-- Discount : composed ofService *-- Charges : composed ofService *-- Payment : composed ofService o-- Rental : managesInventory o-- Vehicle : managesRental o-- CustomerIntfRental o-- VehicleBill o-- RentalBill o-- Discount : usesBill o-- Charges : uses
@endumlDetailed Class Diagram
Section titled “Detailed Class Diagram”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
enum VehicleType { ECONOMY COMPACT SEDAN SUV LUXURY VAN}
enum VehicleStatus { AVAILABLE RESERVED RENTED MAINTENANCE RETIRED}
enum ReservationStatus { PENDING CONFIRMED ACTIVE COMPLETED CANCELLED}
enum PaymentStatus { PENDING COMPLETED FAILED REFUNDED}
class Address { - street: String - city: String - state: String - zipCode: String + Address(street: String, city: String) + getCity(): String}
interface IVehicle { + getVehicleId(): String + getType(): VehicleType + getStatus(): VehicleStatus + getDailyRate(): double}
class Vehicle { - vehicleId: String - licensePlate: String - make: String - model: String - type: VehicleType - status: VehicleStatus - mileage: int - dailyRate: double + Vehicle(vehicleId: String, licensePlate: String, type: VehicleType) + getVehicleId(): String + getType(): VehicleType + getStatus(): VehicleStatus + updateStatus(status: VehicleStatus): void}
interface IDiscountPolicy { + getDiscountRate(): double + applyDiscount(amount: double): double}
class NoDiscountPolicy { + getDiscountRate(): double + applyDiscount(amount: double): double}
class LoyaltyDiscountPolicy { - discountRate: double - loyaltyPoints: int + LoyaltyDiscountPolicy(rate: double, points: int) + getDiscountRate(): double + applyDiscount(amount: double): double + addPoints(points: int): void}
interface ICustomer { + getCustomerId(): String + getName(): String + getEmail(): String + getDiscountRate(): double}
class Customer { - customerId: String - name: String - email: String - phone: String - driverLicense: String - address: Address - discountPolicy: IDiscountPolicy + Customer(customerId: String, name: String, policy: IDiscountPolicy) + getCustomerId(): String + getName(): String + getDiscountRate(): double}
interface IReservation { + getReservationId(): String + getCustomer(): ICustomer + getVehicle(): IVehicle + getStatus(): ReservationStatus}
class Reservation { - reservationId: String - customer: ICustomer - vehicle: IVehicle - pickupDate: DateTime - returnDate: DateTime - status: ReservationStatus - pricingCalculator: IPricingStrategy + Reservation(customer: ICustomer, vehicle: IVehicle, calculator: IPricingStrategy) + getStatus(): ReservationStatus + calculateCost(): double}
interface IPricingStrategy { + calculatePrice(vehicle: IVehicle, days: int): double}
class HourlyPricing { + calculatePrice(vehicle: IVehicle, hours: int): double}
class DailyPricing { + calculatePrice(vehicle: IVehicle, days: int): double}
class WeeklyPricing { - weeklyDiscount: double + calculatePrice(vehicle: IVehicle, days: int): double}
interface IAdditionalChargeCalculator { + calculateCharges(rental: IRental): double}
class LateFeeCalculator { + calculateCharges(rental: IRental): double}
class MileageChargeCalculator { + calculateCharges(rental: IRental): double}
interface IRental { + getRentalId(): String + getReservation(): IReservation + getActualReturnTime(): DateTime}
class Rental { - rentalId: String - reservation: IReservation - actualPickupTime: DateTime - actualReturnTime: DateTime - chargeCalculators: List<IAdditionalChargeCalculator> + Rental(reservation: IReservation, calculators: List<IAdditionalChargeCalculator>) + calculateFinalCost(): double}
interface IPaymentProcessor { + processPayment(amount: double, method: String): boolean + refund(transactionId: String, amount: double): boolean}
class PaymentProcessor { + processPayment(amount: double, method: String): boolean + refund(transactionId: String, amount: double): boolean}
class Payment { - paymentId: String - amount: double - status: PaymentStatus - processor: IPaymentProcessor + Payment(amount: double, processor: IPaymentProcessor) + processPayment(): boolean}
interface IVehicleInventory { + checkAvailability(vehicle: IVehicle, start: Date, end: Date): boolean + getAvailableVehicles(type: VehicleType, start: Date, end: Date): List<IVehicle>}
class VehicleInventory { - vehicles: Map<String, IVehicle> - reservations: List<IReservation> + checkAvailability(vehicle: IVehicle, start: Date, end: Date): boolean + getAvailableVehicles(type: VehicleType, start: Date, end: Date): List<IVehicle>}
class SearchCriteria { - location: String - vehicleType: VehicleType - pickupDate: DateTime - returnDate: DateTime + SearchCriteria(location: String, pickup: DateTime, return: DateTime)}
interface IRentalService { + searchVehicles(criteria: SearchCriteria): List<IVehicle> + createReservation(customer: ICustomer, vehicle: IVehicle, pickup: DateTime, return: DateTime): IReservation + pickupVehicle(reservationId: String): IRental}
class RentalService { - inventory: IVehicleInventory - reservations: Map<String, IReservation> - rentals: Map<String, IRental> - pricingStrategy: IPricingStrategy - paymentProcessor: IPaymentProcessor + RentalService(inventory: IVehicleInventory, pricing: IPricingStrategy, processor: IPaymentProcessor) + searchVehicles(criteria: SearchCriteria): List<IVehicle> + createReservation(customer: ICustomer, vehicle: IVehicle, pickup: DateTime, return: DateTime): IReservation + pickupVehicle(reservationId: String): IRental}
class CarRentalDriver { + {static} main(args: String[]): void - setupRentalSystem(): IRentalService - demonstrateRental(): void}
IVehicle <|.. VehicleVehicle *-- VehicleTypeVehicle *-- VehicleStatus
IDiscountPolicy <|.. NoDiscountPolicyIDiscountPolicy <|.. LoyaltyDiscountPolicy
ICustomer <|.. CustomerCustomer o-- AddressCustomer *-- IDiscountPolicy : composed of
IReservation <|.. ReservationReservation o-- ICustomerReservation o-- IVehicleReservation *-- ReservationStatusReservation o-- IPricingStrategy : uses
IPricingStrategy <|.. HourlyPricingIPricingStrategy <|.. DailyPricingIPricingStrategy <|.. WeeklyPricing
IAdditionalChargeCalculator <|.. LateFeeCalculatorIAdditionalChargeCalculator <|.. MileageChargeCalculator
IRental <|.. RentalRental o-- IReservationRental o-- IAdditionalChargeCalculator : uses
IPaymentProcessor <|.. PaymentProcessorPayment *-- PaymentStatusPayment *-- IPaymentProcessor : composed of
IVehicleInventory <|.. VehicleInventoryVehicleInventory o-- IVehicle : managesVehicleInventory o-- IReservation : tracks
IRentalService <|.. RentalServiceRentalService *-- IVehicleInventory : composed ofRentalService o-- IReservation : managesRentalService o-- IRental : managesRentalService *-- IPricingStrategy : composed ofRentalService *-- IPaymentProcessor : composed ofRentalService ..> SearchCriteria : uses
CarRentalDriver ..> IRentalService : usesCarRentalDriver ..> ICustomer : createsCarRentalDriver ..> IVehicle : creates
@endumlKey Design Patterns
Section titled “Key Design Patterns”- Singleton Pattern: RentalService as central management
- Strategy Pattern: Different pricing strategies
- Factory Pattern: Create customers and reservations
- Observer Pattern: Notifications for reservations
- State Pattern: Vehicle and reservation status management
Design Pattern Diagrams
Section titled “Design Pattern Diagrams”1. Strategy Pattern - Discount Policies (Composition)
Section titled “1. Strategy Pattern - Discount Policies (Composition)”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
title Strategy Pattern - Discount Policies
interface IDiscountPolicy { + calculateDiscount(basePrice, days): double + isApplicable(customer, rental): boolean}
class NoDiscount { + calculateDiscount(basePrice, days): double + isApplicable(customer, rental): boolean}
class SeasonalDiscount { - discountPercentage: double - validMonths: List<Integer> + calculateDiscount(basePrice, days): double + isApplicable(customer, rental): boolean}
class MembershipDiscount { - membershipTier: String - discountPercentage: double + calculateDiscount(basePrice, days): double + isApplicable(customer, rental): boolean}
class LongTermDiscount { - minDays: int - discountPercentage: double + calculateDiscount(basePrice, days): double}
class Customer { - customerId: String - discountPolicy: IDiscountPolicy + Customer(id, IDiscountPolicy) + setDiscountPolicy(IDiscountPolicy): void + calculateRentalPrice(rental): double}
class RentalService { - pricingCalculator: IPricingCalculator - defaultDiscountPolicy: IDiscountPolicy + calculatePrice(customer, rental): double}
IDiscountPolicy <|.. NoDiscountIDiscountPolicy <|.. SeasonalDiscountIDiscountPolicy <|.. MembershipDiscountIDiscountPolicy <|.. LongTermDiscountCustomer *-- IDiscountPolicyRentalService *-- IDiscountPolicy
note bottom of Customer **Code Example (Composition over Inheritance):**
// Instead of: RegularCustomer, PremiumCustomer, VIPCustomer classes // Use composition with discount policies
ICustomer regular = new Customer( "C001", new NoDiscount() );
ICustomer premium = new Customer( "C002", new MembershipDiscount("GOLD", 15.0) );
ICustomer vip = new Customer( "C003", new MembershipDiscount("PLATINUM", 25.0) );
// Seasonal promotion: switch policy dynamically regular.setDiscountPolicy(new SeasonalDiscount(20.0));
// Calculate price with discount double price = regular.calculateRentalPrice(rental); // basePrice: $300 // After 20% seasonal discount: $240
// VIP with long-term rental gets both discounts IDiscountPolicy combined = new CompositeDiscount( new MembershipDiscount("PLATINUM", 25.0), new LongTermDiscount(7, 10.0) ); vip.setDiscountPolicy(combined);end note
@enduml2. State Pattern - Vehicle & Reservation Status
Section titled “2. State Pattern - Vehicle & Reservation Status”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
title State Pattern - Vehicle Status Transitions
enum VehicleStatus { AVAILABLE RESERVED RENTED MAINTENANCE RETIRED}
enum ReservationStatus { PENDING CONFIRMED ACTIVE COMPLETED CANCELLED}
class Vehicle { - vehicleId: String - status: VehicleStatus + markAvailable(): void + markReserved(): void + markRented(): void + markMaintenance(): void + canRent(): boolean + canReserve(): boolean}
class Reservation { - reservationId: String - status: ReservationStatus + confirm(): void + activate(): void + complete(): void + cancel(): void - validateTransition(newStatus): void}
Vehicle *-- VehicleStatusReservation *-- ReservationStatus
note right of VehicleStatus **State Transitions:**
AVAILABLE -> RESERVED (reservation made) RESERVED -> RENTED (customer picks up) RENTED -> MAINTENANCE (damage reported) MAINTENANCE -> AVAILABLE (repair complete) AVAILABLE -> RETIRED (vehicle too old)
Any state -> MAINTENANCE (emergency)end note
note bottom of Vehicle **Code Example:**
Vehicle car = new Vehicle("V-123", VehicleType.SEDAN); // Status: AVAILABLE
// Customer makes reservation if (car.canReserve()) { car.markReserved(); // Status: AVAILABLE -> RESERVED }
// Customer picks up car if (car.status == VehicleStatus.RESERVED) { car.markRented(); // Status: RESERVED -> RENTED }
// Customer returns car car.markAvailable(); // Status: RENTED -> AVAILABLE
// Damage found during inspection car.markMaintenance(); // Status: AVAILABLE -> MAINTENANCE
// Invalid transitions throw exception car.markRented(); // IllegalStateException // "Cannot rent vehicle in MAINTENANCE status"end note
note bottom of Reservation **Code Example:**
Reservation reservation = new Reservation(customer, vehicle); // Status: PENDING
// Payment confirmed reservation.confirm(); // Status: PENDING -> CONFIRMED
// Customer picks up vehicle reservation.activate(); // Status: CONFIRMED -> ACTIVE
// Customer returns vehicle reservation.complete(); // Status: ACTIVE -> COMPLETED
// Cancellation (only if PENDING or CONFIRMED) if (reservation.canCancel()) { reservation.cancel(); // Refund processing based on cancellation policy }end note
@enduml3. Observer Pattern - Reservation Notifications
Section titled “3. Observer Pattern - Reservation Notifications”Error generating PlantUML diagram: connect ECONNREFUSED 127.0.0.1:8080
@startuml
title Observer Pattern - Rental Notifications
interface IReservationObserver { + onReservationCreated(reservation): void + onReservationConfirmed(reservation): void + onRentalStarted(reservation): void + onRentalCompleted(reservation): void + onReservationCancelled(reservation): void}
class Reservation { - observers: List<IReservationObserver> + addObserver(IReservationObserver): void + removeObserver(IReservationObserver): void + confirm(): void + cancel(): void - notifyObservers(event): void}
class EmailNotificationService { + onReservationConfirmed(reservation): void + onRentalStarted(reservation): void + onReservationCancelled(reservation): void - sendEmail(customer, template, data): void}
class SMSNotificationService { + onRentalStarted(reservation): void + onRentalCompleted(reservation): void - sendSMS(phoneNumber, message): void}
class VehicleInventoryService { + onReservationConfirmed(reservation): void + onRentalCompleted(reservation): void + onReservationCancelled(reservation): void - updateVehicleStatus(vehicle, status): void}
class BillingService { + onReservationConfirmed(reservation): void + onRentalCompleted(reservation): void + onReservationCancelled(reservation): void - processPayment(reservation): void - generateInvoice(reservation): void}
Reservation o-- "*" IReservationObserverIReservationObserver <|.. EmailNotificationServiceIReservationObserver <|.. SMSNotificationServiceIReservationObserver <|.. VehicleInventoryServiceIReservationObserver <|.. BillingService
note bottom of Reservation **Code Example:**
Reservation reservation = new Reservation(customer, vehicle, dates);
// Register observers reservation.addObserver(new EmailNotificationService()); reservation.addObserver(new SMSNotificationService()); reservation.addObserver(new VehicleInventoryService()); reservation.addObserver(new BillingService());
// Single action notifies all observers reservation.confirm();
// Results: // 1. EmailNotificationService: // Sends confirmation email with pickup details // 2. VehicleInventoryService: // Marks vehicle as RESERVED // 3. BillingService: // Processes payment, generates invoice
// Customer picks up car reservation.startRental(); // - Email: Rental agreement and keys info // - SMS: "Enjoy your ride! Return by Dec 25" // - Inventory: Vehicle status -> RENTED
// Easy to add new observers without modifying Reservation reservation.addObserver(new LoyaltyPointsService());end note
@endumlCode Snippets
Section titled “Code Snippets”Create Reservation
Section titled “Create Reservation”public class RentalService { public Reservation createReservation(Customer customer, Vehicle vehicle, DateTime pickup, DateTime return) throws RentalException { synchronized(this) { // Validate dates if (pickup.isAfter(return)) { throw new RentalException("Invalid dates"); }
// Check availability if (!inventory.checkAvailability(vehicle, pickup.toDate(), return.toDate())) { throw new RentalException("Vehicle not available for selected dates"); }
// Create reservation Reservation reservation = new Reservation(customer, vehicle); reservation.setPickupDate(pickup); reservation.setReturnDate(return); reservation.setPickupLocation(vehicle.getBranch()); reservation.setReturnLocation(vehicle.getBranch());
// Calculate estimated cost long days = calculateDays(pickup, return); double cost = pricingStrategy.calculatePrice(vehicle, (int) days);
// Apply customer discount cost = cost * (1 - customer.getDiscountRate()); reservation.setEstimatedCost(cost);
// Update vehicle status vehicle.updateStatus(VehicleStatus.RESERVED); reservation.setStatus(ReservationStatus.CONFIRMED);
// Save reservation reservations.put(reservation.getReservationId(), reservation);
// Send confirmation notificationService.sendReservationConfirmation(reservation);
return reservation; } }}Vehicle Pickup
Section titled “Vehicle Pickup”public class RentalService { public Rental pickupVehicle(String reservationId) throws RentalException { synchronized(this) { Reservation reservation = reservations.get(reservationId);
if (reservation == null) { throw new RentalException("Reservation not found"); }
if (reservation.getStatus() != ReservationStatus.CONFIRMED) { throw new RentalException("Reservation is not confirmed"); }
// Create rental Rental rental = new Rental(reservation); rental.setActualPickupTime(DateTime.now()); rental.setStartMileage(reservation.getVehicle().getMileage());
// Update statuses reservation.setStatus(ReservationStatus.ACTIVE); reservation.getVehicle().updateStatus(VehicleStatus.RENTED);
// Save rental rentals.put(rental.getRentalId(), rental);
return rental; } }}Vehicle Return and Invoice Generation
Section titled “Vehicle Return and Invoice Generation”public class RentalService { public Invoice returnVehicle(String rentalId, int endMileage) throws RentalException { synchronized(this) { Rental rental = rentals.get(rentalId);
if (rental == null) { throw new RentalException("Rental not found"); }
// Set return details rental.setActualReturnTime(DateTime.now()); rental.setEndMileage(endMileage);
// Calculate final cost double finalCost = calculateFinalCost(rental); rental.setTotalCost(finalCost);
// Create invoice Invoice invoice = new Invoice(rental);
// Calculate charges Reservation reservation = rental.getReservation(); long actualDays = calculateDays(rental.getActualPickupTime(), rental.getActualReturnTime());
// Rental charges double rentalCharges = pricingStrategy.calculatePrice( reservation.getVehicle(), (int) actualDays); invoice.setRentalCharges(rentalCharges);
// Insurance charges if (reservation.isInsuranceAdded()) { double insuranceCharges = reservation.getInsurance() .calculateCost((int) actualDays); invoice.setInsuranceCharges(insuranceCharges); }
// Additional charges (mileage, late return, etc.) double additionalCharges = calculateAdditionalCharges(rental); invoice.setAdditionalCharges(additionalCharges);
// Apply discount invoice.applyDiscount(reservation.getCustomer().getDiscountRate());
// Calculate total invoice.calculateTotal();
// Update statuses reservation.setStatus(ReservationStatus.COMPLETED); reservation.getVehicle().updateStatus(VehicleStatus.AVAILABLE); reservation.getVehicle().setMileage(endMileage);
return invoice; } }
private double calculateAdditionalCharges(Rental rental) { double charges = 0;
// Late return fee DateTime expectedReturn = rental.getReservation().getReturnDate(); if (rental.getActualReturnTime().isAfter(expectedReturn)) { long lateHours = calculateHours(expectedReturn, rental.getActualReturnTime()); charges += lateHours * 10; // $10 per hour }
// Extra mileage fee (if exceeded free limit) int mileageDriven = rental.getEndMileage() - rental.getStartMileage(); int freeMileage = 100 * (int) calculateDays( rental.getActualPickupTime(), rental.getActualReturnTime());
if (mileageDriven > freeMileage) { charges += (mileageDriven - freeMileage) * 0.5; // $0.50 per extra mile }
return charges; }}Search Vehicles
Section titled “Search Vehicles”public class RentalService { public List<Vehicle> searchVehicles(SearchCriteria criteria) { List<Vehicle> availableVehicles = new ArrayList<>();
// Find branches in location List<Branch> matchingBranches = branches.values().stream() .filter(b -> b.getAddress().getCity() .equalsIgnoreCase(criteria.getLocation())) .collect(Collectors.toList());
for (Branch branch : matchingBranches) { List<Vehicle> vehicles = inventory.getAvailableVehicles( branch, criteria.getVehicleType(), criteria.getPickupDate().toDate(), criteria.getReturnDate().toDate() );
// Filter by price range vehicles = vehicles.stream() .filter(v -> { long days = calculateDays(criteria.getPickupDate(), criteria.getReturnDate()); double price = v.calculateRentalCost((int) days); return price >= criteria.getMinPrice() && price <= criteria.getMaxPrice(); }) .collect(Collectors.toList());
availableVehicles.addAll(vehicles); }
return availableVehicles; }}Pricing Strategy
Section titled “Pricing Strategy”public class WeeklyPricing implements PricingStrategy { private static final double WEEKLY_DISCOUNT = 0.15; // 15% off
@Override public double calculatePrice(Vehicle vehicle, int days) { double dailyRate = vehicle.getDailyRate();
if (days >= 7) { int weeks = days / 7; int remainingDays = days % 7;
double weeklyRate = dailyRate * 7 * (1 - WEEKLY_DISCOUNT); double totalCost = (weeks * weeklyRate) + (remainingDays * dailyRate);
return totalCost; }
return days * dailyRate; }}Extension Points
Section titled “Extension Points”- Add vehicle features and accessories (GPS, child seats)
- Implement dynamic pricing based on demand
- Add one-way rental support (different pickup/return locations)
- Support corporate accounts and bulk bookings
- Implement damage assessment and reporting
- Add roadside assistance services
- Support electric vehicle charging plans
- Implement driver rating system
- Add fuel policy options (full-to-full, prepaid)
- Support multi-vehicle reservations for group travel