problemhardood

OOD - Car Rental System

HardUpdated: Dec 31, 2025

Problem

Design an object-oriented system for a modern vehicle rental company that operates across multiple locations. The system must handle vehicle inventory, customer reservations, rental transactions, damage tracking, and dynamic pricing. Your design should demonstrate SOLID principles and incorporate appropriate design patterns to ensure extensibility for new vehicle types and pricing strategies.

Solution

1. Requirements Analysis

Functional Requirements:

  • Manage diverse vehicle inventory (sedans, SUVs, trucks, motorcycles, electric vehicles) across multiple rental locations
  • Enable customers to search and filter available vehicles by location, dates, type, and features
  • Handle complete reservation lifecycle: creation, modification, confirmation, and cancellation with refund logic
  • Implement flexible pricing system that can apply different strategies (base rate, seasonal adjustments, demand-based)
  • Process vehicle checkout with license verification, initial damage inspection, and mileage recording
  • Handle vehicle returns with damage comparison, late fee calculation, fuel charge assessment
  • Track all damage reports with photos and repair cost estimation
  • Support add-ons (GPS, child seats, insurance) with separate pricing
  • Maintain customer profiles with rental history and loyalty status
  • Send automated notifications for confirmations, reminders, and overdue alerts

Non-Functional Requirements:

  • Extensibility: Adding new vehicle types or pricing rules should not require modifying existing classes (Open/Closed Principle)
  • Maintainability: Each class should have a single, well-defined responsibility (Single Responsibility Principle)
  • Testability: Dependencies should be based on interfaces to allow easy mocking
  • Performance: Vehicle availability queries must handle concurrent reservation attempts

2. Use Case Diagram

Actors:

  • Customer: Searches vehicles, makes/modifies/cancels bookings
  • Agent: Processes check-outs and returns, assists customers
  • Manager: Manages fleet, configures pricing, views reports
  • System: Auto-sends notifications, enforces business rules

Primary Use Cases:

  • Search Vehicles - Find available vehicles matching criteria
  • Create Reservation - Book a vehicle for specific dates
  • Modify/Cancel Reservation - Change booking or get refund
  • Check Out Vehicle - Verify customer, inspect vehicle, hand over keys
  • Return Vehicle - Accept vehicle, assess condition, calculate charges
  • Configure Pricing - Set up pricing rules and strategies
  • Manage Vehicle Fleet - Add/update/deactivate vehicles
graph TD
    subgraph RentalSystem["Car Rental System"]
        UC1[Search Vehicles]
        UC2[Create Reservation]
        UC3[Modify Reservation]
        UC4[Cancel Reservation]
        UC5[Check Out Vehicle]
        UC6[Return Vehicle]
        UC7[Configure Pricing]
        UC8[Manage Fleet]
        UC9[Send Notifications]
    end

    Customer[๐Ÿ‘ค Customer]
    Agent[๐Ÿ‘” Agent]
    Manager[๐Ÿ‘จโ€๐Ÿ’ผ Manager]
    System[๐Ÿ–ฅ๏ธ System]

    Customer --> UC1
    Customer --> UC2
    Customer --> UC3
    Customer --> UC4

    Agent --> UC5
    Agent --> UC6

    Manager --> UC7
    Manager --> UC8

    System --> UC9

    UC2 -.includes.-> UC7
    UC5 -.includes.-> UC9

    style Customer fill:#E3F2FD,stroke:#1976D2,stroke-width:2px
    style Agent fill:#FFF3E0,stroke:#F57C00,stroke-width:2px
    style Manager fill:#F3E5F5,stroke:#7B1FA2,stroke-width:2px
    style System fill:#E8F5E9,stroke:#388E3C,stroke-width:2px

    style UC1 fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style UC2 fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style UC3 fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style UC4 fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style UC5 fill:#FFE0B2,stroke:#E65100,stroke-width:2px
    style UC6 fill:#FFE0B2,stroke:#E65100,stroke-width:2px
    style UC7 fill:#E1BEE7,stroke:#6A1B9A,stroke-width:2px
    style UC8 fill:#E1BEE7,stroke:#6A1B9A,stroke-width:2px
    style UC9 fill:#C8E6C9,stroke:#2E7D32,stroke-width:2px

3. Class Diagram

Core Classes and Their Responsibilities:

Vehicle Hierarchy (Demonstrates Liskov Substitution Principle):

  • Vehicle (abstract) - Base class with common attributes and behaviors
  • Sedan, SUV, Truck, Motorcycle - Specific vehicle implementations

Reservation Management:

  • Reservation - Manages booking lifecycle and state transitions
  • RentalTransaction - Records actual rental with pickup/return details

Customer Management:

  • Customer - Stores customer info and rental history
  • DriverLicense - Encapsulates license validation logic

Pricing (Strategy Pattern - demonstrates Open/Closed Principle):

  • PricingCalculator (interface) - Defines pricing contract
  • BaseRatePricing - Simple per-day pricing
  • SeasonalPricing - Adjusts rates by season
  • DemandBasedPricing - Dynamic pricing based on availability

Damage Tracking:

  • DamageReport - Documents vehicle condition
  • DamageItem - Individual damage with severity and cost

Infrastructure (Repository Pattern):

  • VehicleRepository - Abstracts vehicle data access
  • ReservationRepository - Handles reservation persistence

Notifications (Observer Pattern):

  • NotificationService - Sends alerts for various events
classDiagram
    %% Vehicle hierarchy
    class Vehicle {
        <<abstract>>
        #String vin
        #String make
        #String model
        #int manufactureYear
        #int currentMileage
        #VehicleStatus status
        #Location assignedLocation
        +getDetails() VehicleInfo
        +isAvailableFor(startDate, endDate) boolean
        +reserveForPeriod(startDate, endDate)
        +markAsRented()
        +markAsReturned()
        +getDailyRate() Money*
    }

    class Sedan {
        -boolean hasSunroof
        -int trunkCapacityLiters
        +getDailyRate() Money
    }

    class SUV {
        -String driveType
        -int passengerCapacity
        +getDailyRate() Money
    }

    class Truck {
        -int payloadKg
        -boolean hasTowHitch
        +getDailyRate() Money
    }

    class Motorcycle {
        -int engineCC
        -String bikeStyle
        +getDailyRate() Money
    }

    Vehicle <|-- Sedan
    Vehicle <|-- SUV
    Vehicle <|-- Truck
    Vehicle <|-- Motorcycle

    %% Reservation system
    class Reservation {
        -String reservationId
        -Customer customer
        -Vehicle vehicle
        -Date startDate
        -Date endDate
        -ReservationStatus status
        -Money totalCost
        -List~RentalAddOn~ addOns
        +confirm()
        +cancel() Money
        +calculateRefund() Money
    }

    class RentalTransaction {
        -String transactionId
        -Reservation reservation
        -DateTime checkOutTime
        -DateTime expectedReturnTime
        -int initialMileage
        -FuelLevel initialFuel
        -DamageReport initialDamage
        -DateTime actualReturnTime
        -int finalMileage
        -FuelLevel finalFuel
        -DamageReport finalDamage
        +calculateCharges() Money
        +isOverdue() boolean
    }

    %% Customer
    class Customer {
        -String customerId
        -String name
        -String email
        -DriverLicense license
        -MembershipTier tier
        -List~Reservation~ history
        +isEligibleToRent() boolean
        +getDiscountRate() decimal
    }

    class DriverLicense {
        -String number
        -String state
        -Date expiryDate
        +isValid() boolean
        +isExpired() boolean
    }

    %% Pricing Strategy Pattern
    class PricingCalculator {
        <<interface>>
        +calculatePrice(Reservation) Money
    }

    class BaseRatePricing {
        -Map~VehicleType Money~ dailyRates
        +calculatePrice(Reservation) Money
    }

    class SeasonalPricing {
        -PricingCalculator basePricing
        -Map~Season decimal~ seasonMultipliers
        +calculatePrice(Reservation) Money
    }

    class DemandBasedPricing {
        -PricingCalculator basePricing
        -VehicleRepository vehicleRepo
        +calculatePrice(Reservation) Money
        -getUtilizationRate(vehicle, dates) decimal
    }

    PricingCalculator <|.. BaseRatePricing
    PricingCalculator <|.. SeasonalPricing
    PricingCalculator <|.. DemandBasedPricing
    SeasonalPricing --> PricingCalculator : wraps
    DemandBasedPricing --> PricingCalculator : wraps

    %% Damage tracking
    class DamageReport {
        -String reportId
        -DateTime inspectionTime
        -String inspectorName
        -List~DamageItem~ items
        -List~String~ photoUrls
        +addDamageItem(DamageItem)
        +getTotalRepairCost() Money
        +compareTo(DamageReport) List~DamageItem~
    }

    class DamageItem {
        -String description
        -DamageSeverity severity
        -String location
        -Money estimatedCost
    }

    %% Location
    class Location {
        -String locationId
        -String name
        -String address
        -List~Vehicle~ inventory
        +getAvailableVehicles(startDate, endDate) List~Vehicle~
    }

    %% Repositories
    class VehicleRepository {
        <<interface>>
        +findById(vin) Vehicle
        +findAvailableByType(type, location, dates) List~Vehicle~
        +save(Vehicle)
        +getUtilization(location, vehicleType, dates) decimal
    }

    class ReservationRepository {
        <<interface>>
        +findById(reservationId) Reservation
        +findByCustomer(customerId) List~Reservation~
        +findOverlapping(vehicle, dates) List~Reservation~
        +save(Reservation)
    }

    %% Notification Service
    class NotificationService {
        +sendReservationConfirmation(Reservation)
        +sendCheckOutConfirmation(RentalTransaction)
        +sendOverdueAlert(RentalTransaction)
        +sendReturnReceipt(RentalTransaction, Money)
    }

    %% Relationships
    Reservation --> Customer
    Reservation --> Vehicle
    RentalTransaction --> Reservation
    RentalTransaction --> DamageReport
    Vehicle --> Location
    DamageReport --> DamageItem

    SeasonalPricing ..> VehicleRepository
    DemandBasedPricing ..> VehicleRepository

Design Patterns Used:

  1. Strategy Pattern - PricingCalculator interface allows swapping pricing algorithms:

    • Open/Closed: New pricing strategies can be added without modifying existing code
    • Each strategy encapsulates a different algorithm
    • Pricing can be changed at runtime
  2. Repository Pattern - VehicleRepository and ReservationRepository:

    • Separates data access logic from business logic
    • Allows switching between different data sources
    • Facilitates testing with mock repositories
  3. Template Method (implied in Vehicle) - Vehicle.getDailyRate() is abstract:

    • Base class defines structure, subclasses provide specifics
    • Ensures all vehicles implement rate calculation
  4. Observer Pattern - NotificationService:

    • System sends notifications when reservation states change
    • Decouples notification logic from core business logic

SOLID Principles Demonstrated:

  • Single Responsibility: Each class has one reason to change

    • Reservation handles booking lifecycle
    • DamageReport handles damage tracking
    • PricingCalculator only calculates prices
  • Open/Closed: Classes open for extension, closed for modification

    • New vehicle types can be added by extending Vehicle
    • New pricing strategies without changing Reservation
  • Liskov Substitution: Subtypes can replace base types

    • Any Vehicle subclass works where Vehicle is expected
    • Any PricingCalculator implementation is interchangeable
  • Interface Segregation: Clients depend only on methods they use

    • Repository interfaces are focused and minimal
  • Dependency Inversion: Depend on abstractions, not concretions

    • Services depend on PricingCalculator interface, not concrete implementations
    • Business logic depends on Repository interfaces, not databases

4. Activity Diagrams

Activity: Creating a Reservation

flowchart TD
    Start([๐Ÿš— Customer wants to rent]) --> Search[๐Ÿ” Search available vehicles]
    Search --> Select[๐Ÿ“… Select vehicle and dates]
    Select --> Check{โœ… Vehicle available?}
    Check -->|No| Alternatives[๐Ÿ’ก Show alternatives]
    Alternatives --> Select
    Check -->|Yes| CalcPrice[๐Ÿ’ฐ Calculate total price]
    CalcPrice --> SelectAddOns[๐Ÿ› ๏ธ Select add-ons]
    SelectAddOns --> Review[๐Ÿ“‹ Review booking summary]
    Review --> Confirm{๐Ÿค” Customer confirms?}
    Confirm -->|No| Cancel([โŒ End - Cancelled])
    Confirm -->|Yes| Payment[๐Ÿ’ณ Process payment]
    Payment --> PaySuccess{โœ“ Payment OK?}
    PaySuccess -->|No| PaymentFail[โš ๏ธ Show error]
    PaymentFail --> Payment
    PaySuccess -->|Yes| CreateRes[๐Ÿ“ Create reservation]
    CreateRes --> SendEmail[๐Ÿ“ง Send confirmation email]
    SendEmail --> End([โœ… Reservation confirmed])

    style Start fill:#E1F5FE,stroke:#01579B,stroke-width:3px
    style End fill:#C8E6C9,stroke:#2E7D32,stroke-width:3px
    style Cancel fill:#FFCDD2,stroke:#C62828,stroke-width:3px

    style Search fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style Select fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style CalcPrice fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style SelectAddOns fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style Review fill:#B3E5FC,stroke:#0277BD,stroke-width:2px
    style CreateRes fill:#A5D6A7,stroke:#388E3C,stroke-width:2px
    style SendEmail fill:#A5D6A7,stroke:#388E3C,stroke-width:2px

    style Payment fill:#FFE082,stroke:#F57F17,stroke-width:2px
    style PaymentFail fill:#FFAB91,stroke:#D84315,stroke-width:2px
    style Alternatives fill:#CE93D8,stroke:#6A1B9A,stroke-width:2px

    style Check fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style Confirm fill:#FFF9C4,stroke:#F9A825,stroke-width:2px
    style PaySuccess fill:#FFF9C4,stroke:#F9A825,stroke-width:2px

Activity: Vehicle Check-Out

flowchart TD
    Start([๐Ÿ‘ค Customer arrives]) --> GetRes[๐Ÿ“‹ Retrieve reservation]
    GetRes --> VerifyRes{โœ“ Valid reservation?}
    VerifyRes -->|No| Error([โŒ End - Error])
    VerifyRes -->|Yes| CheckLicense[๐Ÿชช Verify driver license]
    CheckLicense --> LicenseOK{โœ“ License valid?}
    LicenseOK -->|No| Error
    LicenseOK -->|Yes| Inspect[๐Ÿ” Inspect vehicle exterior]
    Inspect --> TakePhotos[๐Ÿ“ธ Take damage photos]
    TakePhotos --> CreateDamageReport[๐Ÿ“ Create damage report]
    CreateDamageReport --> RecordMileage[โ›ฝ Record mileage & fuel]
    RecordMileage --> SignContract[โœ๏ธ Customer signs agreement]
    SignContract --> CollectDeposit[๐Ÿ’ณ Collect security deposit]
    CollectDeposit --> DepositOK{โœ“ Deposit authorized?}
    DepositOK -->|No| Error
    DepositOK -->|Yes| ActivateRental[๐Ÿš€ Create rental transaction]
    ActivateRental --> UpdateStatus[๐Ÿ“Š Update reservation to ACTIVE]
    UpdateStatus --> HandKeys[๐Ÿ”‘ Hand over keys]
    HandKeys --> SendNotif[๐Ÿ“ง Send checkout confirmation]
    SendNotif --> End([โœ… Vehicle checked out])

    style Start fill:#FFF3E0,stroke:#E65100,stroke-width:3px
    style End fill:#C8E6C9,stroke:#2E7D32,stroke-width:3px
    style Error fill:#FFCDD2,stroke:#C62828,stroke-width:3px

    style GetRes fill:#FFE0B2,stroke:#EF6C00,stroke-width:2px
    style CheckLicense fill:#FFE0B2,stroke:#EF6C00,stroke-width:2px
    style Inspect fill:#FFCC80,stroke:#F57C00,stroke-width:2px
    style TakePhotos fill:#FFCC80,stroke:#F57C00,stroke-width:2px
    style CreateDamageReport fill:#FFCC80,stroke:#F57C00,stroke-width:2px
    style RecordMileage fill:#FFB74D,stroke:#FB8C00,stroke-width:2px
    style SignContract fill:#FFB74D,stroke:#FB8C00,stroke-width:2px

    style CollectDeposit fill:#FFE082,stroke:#F9A825,stroke-width:2px
    style ActivateRental fill:#AED581,stroke:#689F38,stroke-width:2px
    style UpdateStatus fill:#AED581,stroke:#689F38,stroke-width:2px
    style HandKeys fill:#81C784,stroke:#388E3C,stroke-width:2px
    style SendNotif fill:#81C784,stroke:#388E3C,stroke-width:2px

    style VerifyRes fill:#FFF59D,stroke:#FBC02D,stroke-width:2px
    style LicenseOK fill:#FFF59D,stroke:#FBC02D,stroke-width:2px
    style DepositOK fill:#FFF59D,stroke:#FBC02D,stroke-width:2px

Activity: Vehicle Return

flowchart TD
    Start([๐Ÿš— Customer returns]) --> GetTrans[๐Ÿ“‹ Retrieve active transaction]
    GetTrans --> InspectReturn[๐Ÿ” Inspect vehicle]
    InspectReturn --> PhotosReturn[๐Ÿ“ธ Take photos]
    PhotosReturn --> CreateReturnReport[๐Ÿ“ Create return damage report]
    CreateReturnReport --> CompareDamage{๐Ÿ”ง New damage?}
    CompareDamage -->|Yes| AssessDamage[๐Ÿ’ฐ Assess damage cost]
    CompareDamage -->|No| CheckMileage[๐Ÿ“ Record final mileage]
    AssessDamage --> CheckMileage
    CheckMileage --> CheckFuel[โ›ฝ Check fuel level]
    CheckFuel --> CalcBase[๐Ÿ’ต Calculate base rental charge]
    CalcBase --> CheckLate{โฐ Late return?}
    CheckLate -->|Yes| AddLateFee[๐Ÿ’ธ Add late fees]
    CheckLate -->|No| CheckFuelCharge{โ›ฝ Fuel low?}
    AddLateFee --> CheckFuelCharge
    CheckFuelCharge -->|Yes| AddFuelFee[๐Ÿ’ฐ Add fuel charge]
    CheckFuelCharge -->|No| AddDamageCharge{๐Ÿ”ง Damage charges?}
    AddFuelFee --> AddDamageCharge
    AddDamageCharge -->|Yes| AddDamageFee[๐Ÿ’ธ Add damage fees]
    AddDamageCharge -->|No| GenInvoice[๐Ÿงพ Generate final invoice]
    AddDamageFee --> GenInvoice
    GenInvoice --> ProcessPay[๐Ÿ’ณ Process final payment]
    ProcessPay --> PayOK{โœ“ Payment successful?}
    PayOK -->|No| PayError([โŒ Payment failed])
    PayOK -->|Yes| ReleaseDeposit[๐Ÿ’ฐ Release security deposit]
    ReleaseDeposit --> UpdateResStatus[๐Ÿ“Š Update reservation to COMPLETED]
    UpdateResStatus --> MarkVehicle[โœ… Mark vehicle AVAILABLE]
    MarkVehicle --> SendReceipt[๐Ÿ“ง Send receipt email]
    SendReceipt --> End([โœ… Return completed])

    style Start fill:#F3E5F5,stroke:#7B1FA2,stroke-width:3px
    style End fill:#C8E6C9,stroke:#2E7D32,stroke-width:3px
    style PayError fill:#FFCDD2,stroke:#C62828,stroke-width:3px

    style GetTrans fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px
    style InspectReturn fill:#E1BEE7,stroke:#8E24AA,stroke-width:2px
    style PhotosReturn fill:#CE93D8,stroke:#7B1FA2,stroke-width:2px
    style CreateReturnReport fill:#CE93D8,stroke:#7B1FA2,stroke-width:2px

    style AssessDamage fill:#FFAB91,stroke:#D84315,stroke-width:2px
    style AddDamageFee fill:#FFAB91,stroke:#D84315,stroke-width:2px
    style AddLateFee fill:#FFAB91,stroke:#D84315,stroke-width:2px
    style AddFuelFee fill:#FFAB91,stroke:#D84315,stroke-width:2px

    style CheckMileage fill:#BA68C8,stroke:#6A1B9A,stroke-width:2px
    style CheckFuel fill:#BA68C8,stroke:#6A1B9A,stroke-width:2px
    style CalcBase fill:#9C27B0,stroke:#4A148C,stroke-width:2px,color:#fff
    style GenInvoice fill:#9C27B0,stroke:#4A148C,stroke-width:2px,color:#fff

    style ProcessPay fill:#FFE082,stroke:#F9A825,stroke-width:2px
    style ReleaseDeposit fill:#AED581,stroke:#689F38,stroke-width:2px
    style UpdateResStatus fill:#81C784,stroke:#388E3C,stroke-width:2px
    style MarkVehicle fill:#66BB6A,stroke:#2E7D32,stroke-width:2px
    style SendReceipt fill:#66BB6A,stroke:#2E7D32,stroke-width:2px

    style CompareDamage fill:#FFF59D,stroke:#FBC02D,stroke-width:2px
    style CheckLate fill:#FFF59D,stroke:#FBC02D,stroke-width:2px
    style CheckFuelCharge fill:#FFF59D,stroke:#FBC02D,stroke-width:2px
    style AddDamageCharge fill:#FFF59D,stroke:#FBC02D,stroke-width:2px
    style PayOK fill:#FFF59D,stroke:#FBC02D,stroke-width:2px

5. High-Level Code Implementation

Java

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.math.BigDecimal;
import java.util.*;

// ============================================================================
// Enums
// ============================================================================

enum VehicleStatus {
    AVAILABLE, RESERVED, RENTED, IN_MAINTENANCE, RETIRED
}

enum ReservationStatus {
    PENDING_PAYMENT, CONFIRMED, ACTIVE, COMPLETED, CANCELLED
}

enum DamageSeverity {
    MINOR, MODERATE, SEVERE, TOTALED
}

enum FuelLevel {
    EMPTY, QUARTER, HALF, THREE_QUARTER, FULL
}

enum MembershipTier {
    BASIC(0.0),
    SILVER(0.05),
    GOLD(0.10),
    PLATINUM(0.15);

    private final double discountRate;

    MembershipTier(double rate) {
        this.discountRate = rate;
    }

    public double getDiscountRate() {
        return discountRate;
    }
}

// ============================================================================
// Value Objects / Helper Classes
// ============================================================================

class Money {
    private final BigDecimal amount;
    private final String currency;

    public Money(double amount) {
        this(BigDecimal.valueOf(amount), "USD");
    }

    public Money(BigDecimal amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public Money add(Money other) {
        return new Money(this.amount.add(other.amount), this.currency);
    }

    public Money multiply(double factor) {
        return new Money(this.amount.multiply(BigDecimal.valueOf(factor)), this.currency);
    }

    public boolean isZero() {
        return amount.compareTo(BigDecimal.ZERO) == 0;
    }
}

class DateRange {
    private final LocalDate startDate;
    private final LocalDate endDate;

    public DateRange(LocalDate start, LocalDate end) {
        if (start.isAfter(end)) {
            throw new IllegalArgumentException("Start date must be before end date");
        }
        this.startDate = start;
        this.endDate = end;
    }

    public long getDays() {
        return ChronoUnit.DAYS.between(startDate, endDate);
    }

    public boolean overlaps(DateRange other) {
        return !this.endDate.isBefore(other.startDate) &&
               !this.startDate.isAfter(other.endDate);
    }

    public LocalDate getStartDate() { return startDate; }
    public LocalDate getEndDate() { return endDate; }
}

// ============================================================================
// Domain Classes - Vehicle Hierarchy
// ============================================================================

abstract class Vehicle {
    protected String vin;
    protected String make;
    protected String model;
    protected int manufactureYear;
    protected int currentMileage;
    protected VehicleStatus status;
    protected Location assignedLocation;

    public Vehicle(String vin, String make, String model, int year) {
        this.vin = vin;
        this.make = make;
        this.model = model;
        this.manufactureYear = year;
        this.status = VehicleStatus.AVAILABLE;
    }

    // Template method - each subclass must implement
    public abstract Money getDailyRate();

    public boolean isAvailableFor(DateRange period) {
        return status == VehicleStatus.AVAILABLE;
    }

    public void markAsRented() {
        if (status != VehicleStatus.AVAILABLE) {
            throw new IllegalStateException("Vehicle not available for rent");
        }
        this.status = VehicleStatus.RENTED;
    }

    public void markAsReturned() {
        this.status = VehicleStatus.AVAILABLE;
    }

    public void updateMileage(int mileage) {
        if (mileage < this.currentMileage) {
            throw new IllegalArgumentException("Cannot decrease mileage");
        }
        this.currentMileage = mileage;
    }

    // Getters
    public String getVin() { return vin; }
    public VehicleStatus getStatus() { return status; }
}

class Sedan extends Vehicle {
    private boolean hasSunroof;
    private int trunkCapacityLiters;

    public Sedan(String vin, String make, String model, int year,
                 boolean hasSunroof, int trunkCapacity) {
        super(vin, make, model, year);
        this.hasSunroof = hasSunroof;
        this.trunkCapacityLiters = trunkCapacity;
    }

    @Override
    public Money getDailyRate() {
        return new Money(45.00);
    }
}

class SUV extends Vehicle {
    private String driveType; // "AWD", "FWD", "4WD"
    private int passengerCapacity;

    public SUV(String vin, String make, String model, int year,
               String driveType, int capacity) {
        super(vin, make, model, year);
        this.driveType = driveType;
        this.passengerCapacity = capacity;
    }

    @Override
    public Money getDailyRate() {
        return new Money(75.00);
    }

    public int getPassengerCapacity() { return passengerCapacity; }
}

class Truck extends Vehicle {
    private int payloadKg;
    private boolean hasTowHitch;

    public Truck(String vin, String make, String model, int year,
                 int payload, boolean towHitch) {
        super(vin, make, model, year);
        this.payloadKg = payload;
        this.hasTowHitch = towHitch;
    }

    @Override
    public Money getDailyRate() {
        return new Money(95.00);
    }
}

class Motorcycle extends Vehicle {
    private int engineCC;
    private String bikeStyle; // "Sport", "Cruiser", "Touring"

    public Motorcycle(String vin, String make, String model, int year,
                     int cc, String style) {
        super(vin, make, model, year);
        this.engineCC = cc;
        this.bikeStyle = style;
    }

    @Override
    public Money getDailyRate() {
        return new Money(35.00);
    }
}

// ============================================================================
// Customer & License
// ============================================================================

class DriverLicense {
    private String number;
    private String issuingState;
    private LocalDate expiryDate;

    public DriverLicense(String number, String state, LocalDate expiry) {
        this.number = number;
        this.issuingState = state;
        this.expiryDate = expiry;
    }

    public boolean isValid() {
        return !isExpired();
    }

    public boolean isExpired() {
        return LocalDate.now().isAfter(expiryDate);
    }
}

class Customer {
    private String customerId;
    private String name;
    private String email;
    private String phone;
    private DriverLicense license;
    private MembershipTier tier;
    private List<Reservation> rentalHistory;

    public Customer(String id, String name, String email, DriverLicense license) {
        this.customerId = id;
        this.name = name;
        this.email = email;
        this.license = license;
        this.tier = MembershipTier.BASIC;
        this.rentalHistory = new ArrayList<>();
    }

    public boolean isEligibleToRent() {
        return license != null && license.isValid() && !hasOverdueRentals();
    }

    private boolean hasOverdueRentals() {
        return rentalHistory.stream()
            .anyMatch(r -> r.getStatus() == ReservationStatus.ACTIVE && r.isOverdue());
    }

    public double getDiscountRate() {
        return tier.getDiscountRate();
    }

    // Getters
    public String getCustomerId() { return customerId; }
    public String getName() { return name; }
    public String getEmail() { return email; }
    public MembershipTier getTier() { return tier; }
}

// ============================================================================
// Reservation System
// ============================================================================

class Reservation {
    private String reservationId;
    private Customer customer;
    private Vehicle vehicle;
    private DateRange rentalPeriod;
    private ReservationStatus status;
    private Money totalCost;
    private List<RentalAddOn> addOns;
    private LocalDateTime createdAt;

    public Reservation(String id, Customer customer, Vehicle vehicle, DateRange period) {
        this.reservationId = id;
        this.customer = customer;
        this.vehicle = vehicle;
        this.rentalPeriod = period;
        this.status = ReservationStatus.PENDING_PAYMENT;
        this.addOns = new ArrayList<>();
        this.createdAt = LocalDateTime.now();
    }

    public void confirm() {
        if (status != ReservationStatus.PENDING_PAYMENT) {
            throw new IllegalStateException("Can only confirm pending reservations");
        }
        this.status = ReservationStatus.CONFIRMED;
    }

    public Money cancel() {
        if (status == ReservationStatus.COMPLETED || status == ReservationStatus.CANCELLED) {
            throw new IllegalStateException("Cannot cancel this reservation");
        }
        this.status = ReservationStatus.CANCELLED;
        return calculateRefund();
    }

    public Money calculateRefund() {
        long hoursUntilStart = ChronoUnit.HOURS.between(
            LocalDateTime.now(),
            rentalPeriod.getStartDate().atStartOfDay()
        );

        if (hoursUntilStart > 48) {
            return totalCost; // Full refund
        } else if (hoursUntilStart > 24) {
            return totalCost.multiply(0.5); // 50% refund
        } else {
            return new Money(0); // No refund
        }
    }

    public boolean isOverdue() {
        return status == ReservationStatus.ACTIVE &&
               LocalDate.now().isAfter(rentalPeriod.getEndDate());
    }

    public void setTotalCost(Money cost) { this.totalCost = cost; }
    public void setStatus(ReservationStatus status) { this.status = status; }

    // Getters
    public String getReservationId() { return reservationId; }
    public Customer getCustomer() { return customer; }
    public Vehicle getVehicle() { return vehicle; }
    public DateRange getRentalPeriod() { return rentalPeriod; }
    public ReservationStatus getStatus() { return status; }
    public Money getTotalCost() { return totalCost; }
    public List<RentalAddOn> getAddOns() { return addOns; }
}

class RentalTransaction {
    private String transactionId;
    private Reservation reservation;
    private LocalDateTime checkOutTime;
    private LocalDateTime expectedReturnTime;
    private int initialMileage;
    private FuelLevel initialFuel;
    private DamageReport initialDamage;
    private LocalDateTime actualReturnTime;
    private int finalMileage;
    private FuelLevel finalFuel;
    private DamageReport finalDamage;

    public RentalTransaction(String id, Reservation reservation,
                           int mileage, FuelLevel fuel, DamageReport damage) {
        this.transactionId = id;
        this.reservation = reservation;
        this.checkOutTime = LocalDateTime.now();
        this.expectedReturnTime = reservation.getRentalPeriod()
                                            .getEndDate().atStartOfDay();
        this.initialMileage = mileage;
        this.initialFuel = fuel;
        this.initialDamage = damage;
    }

    public Money calculateCharges() {
        Money baseCharge = reservation.getTotalCost();
        Money lateCharges = calculateLateFees();
        Money fuelCharges = calculateFuelCharges();
        Money damageCharges = finalDamage != null ?
            finalDamage.getTotalRepairCost() : new Money(0);

        return baseCharge.add(lateCharges).add(fuelCharges).add(damageCharges);
    }

    private Money calculateLateFees() {
        if (!isOverdue()) return new Money(0);

        long overdueDays = ChronoUnit.DAYS.between(
            expectedReturnTime.toLocalDate(),
            actualReturnTime.toLocalDate()
        );

        Money dailyRate = reservation.getVehicle().getDailyRate();
        return dailyRate.multiply(1.5 * overdueDays);
    }

    private Money calculateFuelCharges() {
        if (finalFuel.ordinal() >= initialFuel.ordinal()) {
            return new Money(0);
        }
        int levelDiff = initialFuel.ordinal() - finalFuel.ordinal();
        return new Money(levelDiff * 20.0);
    }

    public boolean isOverdue() {
        return actualReturnTime != null &&
               actualReturnTime.isAfter(expectedReturnTime);
    }

    public void recordReturn(int mileage, FuelLevel fuel, DamageReport damage) {
        this.actualReturnTime = LocalDateTime.now();
        this.finalMileage = mileage;
        this.finalFuel = fuel;
        this.finalDamage = damage;
    }
}

class RentalAddOn {
    private String name;
    private String description;
    private Money dailyRate;

    public RentalAddOn(String name, String desc, Money rate) {
        this.name = name;
        this.description = desc;
        this.dailyRate = rate;
    }

    public Money getDailyRate() { return dailyRate; }
}

// ============================================================================
// Damage Tracking
// ============================================================================

class DamageReport {
    private String reportId;
    private LocalDateTime inspectionTime;
    private String inspectorName;
    private List<DamageItem> items;
    private List<String> photoUrls;

    public DamageReport(String id, String inspector) {
        this.reportId = id;
        this.inspectionTime = LocalDateTime.now();
        this.inspectorName = inspector;
        this.items = new ArrayList<>();
        this.photoUrls = new ArrayList<>();
    }

    public void addDamageItem(DamageItem item) {
        items.add(item);
    }

    public Money getTotalRepairCost() {
        return items.stream()
            .map(DamageItem::getEstimatedCost)
            .reduce(new Money(0), Money::add);
    }

    public List<DamageItem> compareTo(DamageReport other) {
        // Find new damage items that weren't in the other report
        List<DamageItem> newDamage = new ArrayList<>();
        for (DamageItem item : this.items) {
            boolean foundInOther = other.items.stream()
                .anyMatch(otherItem -> otherItem.isSimilarTo(item));
            if (!foundInOther) {
                newDamage.add(item);
            }
        }
        return newDamage;
    }
}

class DamageItem {
    private String description;
    private DamageSeverity severity;
    private String location;
    private Money estimatedCost;

    public DamageItem(String desc, DamageSeverity severity,
                     String location, Money cost) {
        this.description = desc;
        this.severity = severity;
        this.location = location;
        this.estimatedCost = cost;
    }

    public boolean isSimilarTo(DamageItem other) {
        return this.location.equals(other.location) &&
               this.description.contains(other.description);
    }

    public Money getEstimatedCost() { return estimatedCost; }
}

// ============================================================================
// Location
// ============================================================================

class Location {
    private String locationId;
    private String name;
    private String address;
    private List<Vehicle> inventory;

    public Location(String id, String name, String address) {
        this.locationId = id;
        this.name = name;
        this.address = address;
        this.inventory = new ArrayList<>();
    }

    public List<Vehicle> getAvailableVehicles(DateRange period) {
        return inventory.stream()
            .filter(v -> v.isAvailableFor(period))
            .toList();
    }
}

// ============================================================================
// Pricing Strategy Pattern (Open/Closed Principle)
// ============================================================================

interface PricingCalculator {
    Money calculatePrice(Reservation reservation);
}

class BaseRatePricing implements PricingCalculator {
    @Override
    public Money calculatePrice(Reservation reservation) {
        Vehicle vehicle = reservation.getVehicle();
        long days = reservation.getRentalPeriod().getDays();

        Money basePrice = vehicle.getDailyRate().multiply(days);

        // Add add-ons
        Money addOnCost = reservation.getAddOns().stream()
            .map(addon -> addon.getDailyRate().multiply(days))
            .reduce(new Money(0), Money::add);

        Money total = basePrice.add(addOnCost);

        // Apply customer discount
        double discount = reservation.getCustomer().getDiscountRate();
        return total.multiply(1.0 - discount);
    }
}

class SeasonalPricing implements PricingCalculator {
    private PricingCalculator basePricing;
    private Map<Integer, Double> seasonalMultipliers;

    public SeasonalPricing(PricingCalculator base) {
        this.basePricing = base;
        this.seasonalMultipliers = new HashMap<>();
        // Summer (Jun-Aug): +30%
        seasonalMultipliers.put(6, 1.3);
        seasonalMultipliers.put(7, 1.3);
        seasonalMultipliers.put(8, 1.3);
        // Winter holidays (Dec-Jan): +20%
        seasonalMultipliers.put(12, 1.2);
        seasonalMultipliers.put(1, 1.2);
    }

    @Override
    public Money calculatePrice(Reservation reservation) {
        Money basePrice = basePricing.calculatePrice(reservation);

        int month = reservation.getRentalPeriod().getStartDate().getMonthValue();
        double multiplier = seasonalMultipliers.getOrDefault(month, 1.0);

        return basePrice.multiply(multiplier);
    }
}

class DemandBasedPricing implements PricingCalculator {
    private PricingCalculator basePricing;
    private VehicleRepository vehicleRepo;

    public DemandBasedPricing(PricingCalculator base, VehicleRepository repo) {
        this.basePricing = base;
        this.vehicleRepo = repo;
    }

    @Override
    public Money calculatePrice(Reservation reservation) {
        Money basePrice = basePricing.calculatePrice(reservation);

        // Get utilization rate for this vehicle type
        double utilization = vehicleRepo.getUtilization(
            reservation.getVehicle().assignedLocation,
            reservation.getVehicle().getClass().getSimpleName(),
            reservation.getRentalPeriod()
        );

        // Apply demand multiplier
        double demandMultiplier = 1.0;
        if (utilization > 0.90) {
            demandMultiplier = 1.5; // 50% surge
        } else if (utilization > 0.75) {
            demandMultiplier = 1.25; // 25% surge
        } else if (utilization > 0.60) {
            demandMultiplier = 1.1; // 10% surge
        }

        return basePrice.multiply(demandMultiplier);
    }
}

// ============================================================================
// Repository Interfaces (Dependency Inversion Principle)
// ============================================================================

interface VehicleRepository {
    Vehicle findById(String vin);
    List<Vehicle> findAvailableByType(String vehicleType, Location location,
                                      DateRange dates);
    void save(Vehicle vehicle);
    double getUtilization(Location location, String vehicleType, DateRange dates);
}

interface ReservationRepository {
    Reservation findById(String reservationId);
    List<Reservation> findByCustomer(String customerId);
    List<Reservation> findOverlapping(Vehicle vehicle, DateRange dates);
    void save(Reservation reservation);
}

// ============================================================================
// Notification Service (Observer Pattern)
// ============================================================================

class NotificationService {
    public void sendReservationConfirmation(Reservation reservation) {
        String message = String.format(
            "Reservation %s confirmed for %s from %s to %s",
            reservation.getReservationId(),
            reservation.getCustomer().getName(),
            reservation.getRentalPeriod().getStartDate(),
            reservation.getRentalPeriod().getEndDate()
        );
        sendEmail(reservation.getCustomer().getEmail(), message);
    }

    public void sendCheckOutConfirmation(RentalTransaction transaction) {
        String message = "Vehicle checked out successfully. Enjoy your trip!";
        sendEmail(transaction.reservation.getCustomer().getEmail(), message);
    }

    public void sendOverdueAlert(RentalTransaction transaction) {
        String message = "Your rental is overdue. Please return the vehicle.";
        sendEmail(transaction.reservation.getCustomer().getEmail(), message);
    }

    public void sendReturnReceipt(RentalTransaction transaction, Money finalCharge) {
        String message = String.format(
            "Vehicle returned. Total charges: %s",
            finalCharge.toString()
        );
        sendEmail(transaction.reservation.getCustomer().getEmail(), message);
    }

    private void sendEmail(String email, String message) {
        // Email sending implementation
        System.out.println("Email to " + email + ": " + message);
    }
}

Python

from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from decimal import Decimal
from enum import Enum
from typing import List, Optional, Dict
from uuid import uuid4


# ============================================================================
# Enums
# ============================================================================

class VehicleStatus(Enum):
    AVAILABLE = "available"
    RESERVED = "reserved"
    RENTED = "rented"
    IN_MAINTENANCE = "in_maintenance"
    RETIRED = "retired"


class ReservationStatus(Enum):
    PENDING_PAYMENT = "pending_payment"
    CONFIRMED = "confirmed"
    ACTIVE = "active"
    COMPLETED = "completed"
    CANCELLED = "cancelled"


class DamageSeverity(Enum):
    MINOR = "minor"
    MODERATE = "moderate"
    SEVERE = "severe"
    TOTALED = "totaled"


class FuelLevel(Enum):
    EMPTY = 0
    QUARTER = 1
    HALF = 2
    THREE_QUARTER = 3
    FULL = 4


class MembershipTier(Enum):
    BASIC = 0.0
    SILVER = 0.05
    GOLD = 0.10
    PLATINUM = 0.15

    @property
    def discount_rate(self) -> float:
        return self.value


# ============================================================================
# Value Objects
# ============================================================================

@dataclass(frozen=True)
class Money:
    """Immutable money representation"""
    amount: Decimal
    currency: str = "USD"

    def add(self, other: 'Money') -> 'Money':
        if self.currency != other.currency:
            raise ValueError("Currency mismatch")
        return Money(self.amount + other.amount, self.currency)

    def multiply(self, factor: float) -> 'Money':
        return Money(self.amount * Decimal(str(factor)), self.currency)

    def is_zero(self) -> bool:
        return self.amount == Decimal('0')


@dataclass(frozen=True)
class DateRange:
    """Immutable date range"""
    start_date: date
    end_date: date

    def __post_init__(self) -> None:
        if self.start_date > self.end_date:
            raise ValueError("Start must be before end")

    def get_days(self) -> int:
        return (self.end_date - self.start_date).days

    def overlaps(self, other: 'DateRange') -> bool:
        return not (self.end_date < other.start_date or
                   self.start_date > other.end_date)


# ============================================================================
# Vehicle Hierarchy (Liskov Substitution Principle)
# ============================================================================

class Vehicle(ABC):
    """Abstract base class for all vehicles"""

    def __init__(self, vin: str, make: str, model: str, year: int) -> None:
        self.vin = vin
        self.make = make
        self.model = model
        self.manufacture_year = year
        self.current_mileage = 0
        self.status = VehicleStatus.AVAILABLE
        self.assigned_location: Optional['Location'] = None

    @abstractmethod
    def get_daily_rate(self) -> Money:
        """Each vehicle type must define its daily rate"""
        pass

    def is_available_for(self, period: DateRange) -> bool:
        return self.status == VehicleStatus.AVAILABLE

    def mark_as_rented(self) -> None:
        if self.status != VehicleStatus.AVAILABLE:
            raise ValueError("Vehicle not available")
        self.status = VehicleStatus.RENTED

    def mark_as_returned(self) -> None:
        self.status = VehicleStatus.AVAILABLE

    def update_mileage(self, mileage: int) -> None:
        if mileage < self.current_mileage:
            raise ValueError("Cannot decrease mileage")
        self.current_mileage = mileage


class Sedan(Vehicle):
    def __init__(self, vin: str, make: str, model: str, year: int,
                 has_sunroof: bool, trunk_capacity: int) -> None:
        super().__init__(vin, make, model, year)
        self.has_sunroof = has_sunroof
        self.trunk_capacity_liters = trunk_capacity

    def get_daily_rate(self) -> Money:
        return Money(Decimal('45.00'))


class SUV(Vehicle):
    def __init__(self, vin: str, make: str, model: str, year: int,
                 drive_type: str, capacity: int) -> None:
        super().__init__(vin, make, model, year)
        self.drive_type = drive_type
        self.passenger_capacity = capacity

    def get_daily_rate(self) -> Money:
        return Money(Decimal('75.00'))


class Truck(Vehicle):
    def __init__(self, vin: str, make: str, model: str, year: int,
                 payload: int, has_tow: bool) -> None:
        super().__init__(vin, make, model, year)
        self.payload_kg = payload
        self.has_tow_hitch = has_tow

    def get_daily_rate(self) -> Money:
        return Money(Decimal('95.00'))


class Motorcycle(Vehicle):
    def __init__(self, vin: str, make: str, model: str, year: int,
                 cc: int, style: str) -> None:
        super().__init__(vin, make, model, year)
        self.engine_cc = cc
        self.bike_style = style

    def get_daily_rate(self) -> Money:
        return Money(Decimal('35.00'))


# ============================================================================
# Customer & License (Single Responsibility Principle)
# ============================================================================

@dataclass
class DriverLicense:
    number: str
    issuing_state: str
    expiry_date: date

    def is_valid(self) -> bool:
        return not self.is_expired()

    def is_expired(self) -> bool:
        return date.today() > self.expiry_date


class Customer:
    def __init__(self, customer_id: str, name: str, email: str,
                 license: DriverLicense) -> None:
        self.customer_id = customer_id
        self.name = name
        self.email = email
        self.phone: Optional[str] = None
        self.license = license
        self.tier = MembershipTier.BASIC
        self.rental_history: List['Reservation'] = []

    def is_eligible_to_rent(self) -> bool:
        return (self.license is not None and
                self.license.is_valid() and
                not self._has_overdue_rentals())

    def _has_overdue_rentals(self) -> bool:
        return any(r.status == ReservationStatus.ACTIVE and r.is_overdue()
                  for r in self.rental_history)

    def get_discount_rate(self) -> float:
        return self.tier.discount_rate


# ============================================================================
# Reservation System
# ============================================================================

class Reservation:
    def __init__(self, reservation_id: str, customer: Customer,
                 vehicle: Vehicle, period: DateRange) -> None:
        self.reservation_id = reservation_id
        self.customer = customer
        self.vehicle = vehicle
        self.rental_period = period
        self.status = ReservationStatus.PENDING_PAYMENT
        self.total_cost: Optional[Money] = None
        self.add_ons: List['RentalAddOn'] = []
        self.created_at = datetime.now()

    def confirm(self) -> None:
        if self.status != ReservationStatus.PENDING_PAYMENT:
            raise ValueError("Can only confirm pending reservations")
        self.status = ReservationStatus.CONFIRMED

    def cancel(self) -> Money:
        if self.status in [ReservationStatus.COMPLETED, ReservationStatus.CANCELLED]:
            raise ValueError("Cannot cancel this reservation")
        self.status = ReservationStatus.CANCELLED
        return self.calculate_refund()

    def calculate_refund(self) -> Money:
        hours_until_start = (
            datetime.combine(self.rental_period.start_date, datetime.min.time()) -
            datetime.now()
        ).total_seconds() / 3600

        if hours_until_start > 48:
            return self.total_cost
        elif hours_until_start > 24:
            return self.total_cost.multiply(0.5)
        else:
            return Money(Decimal('0'))

    def is_overdue(self) -> bool:
        return (self.status == ReservationStatus.ACTIVE and
                date.today() > self.rental_period.end_date)


class RentalTransaction:
    def __init__(self, transaction_id: str, reservation: Reservation,
                 mileage: int, fuel: FuelLevel, damage: 'DamageReport') -> None:
        self.transaction_id = transaction_id
        self.reservation = reservation
        self.check_out_time = datetime.now()
        self.expected_return = datetime.combine(
            reservation.rental_period.end_date,
            datetime.min.time()
        )
        self.initial_mileage = mileage
        self.initial_fuel = fuel
        self.initial_damage = damage
        self.actual_return_time: Optional[datetime] = None
        self.final_mileage: Optional[int] = None
        self.final_fuel: Optional[FuelLevel] = None
        self.final_damage: Optional['DamageReport'] = None

    def calculate_charges(self) -> Money:
        base = self.reservation.total_cost
        late = self._calculate_late_fees()
        fuel = self._calculate_fuel_charges()
        damage = (self.final_damage.get_total_repair_cost()
                 if self.final_damage else Money(Decimal('0')))

        return base.add(late).add(fuel).add(damage)

    def _calculate_late_fees(self) -> Money:
        if not self.is_overdue():
            return Money(Decimal('0'))

        days = (self.actual_return_time.date() -
               self.expected_return.date()).days
        rate = self.reservation.vehicle.get_daily_rate()
        return rate.multiply(1.5 * days)

    def _calculate_fuel_charges(self) -> Money:
        if self.final_fuel.value >= self.initial_fuel.value:
            return Money(Decimal('0'))
        diff = self.initial_fuel.value - self.final_fuel.value
        return Money(Decimal(str(diff * 20)))

    def is_overdue(self) -> bool:
        return (self.actual_return_time is not None and
                self.actual_return_time > self.expected_return)

    def record_return(self, mileage: int, fuel: FuelLevel,
                     damage: 'DamageReport') -> None:
        self.actual_return_time = datetime.now()
        self.final_mileage = mileage
        self.final_fuel = fuel
        self.final_damage = damage


@dataclass
class RentalAddOn:
    name: str
    description: str
    daily_rate: Money


# ============================================================================
# Damage Tracking
# ============================================================================

@dataclass
class DamageItem:
    description: str
    severity: DamageSeverity
    location: str
    estimated_cost: Money

    def is_similar_to(self, other: 'DamageItem') -> bool:
        return (self.location == other.location and
                other.description in self.description)


class DamageReport:
    def __init__(self, report_id: str, inspector: str) -> None:
        self.report_id = report_id
        self.inspection_time = datetime.now()
        self.inspector_name = inspector
        self.items: List[DamageItem] = []
        self.photo_urls: List[str] = []

    def add_damage_item(self, item: DamageItem) -> None:
        self.items.append(item)

    def get_total_repair_cost(self) -> Money:
        if not self.items:
            return Money(Decimal('0'))
        total = self.items[0].estimated_cost
        for item in self.items[1:]:
            total = total.add(item.estimated_cost)
        return total

    def compare_to(self, other: 'DamageReport') -> List[DamageItem]:
        """Find new damage not in the other report"""
        new_damage = []
        for item in self.items:
            if not any(other_item.is_similar_to(item) for other_item in other.items):
                new_damage.append(item)
        return new_damage


# ============================================================================
# Location
# ============================================================================

class Location:
    def __init__(self, location_id: str, name: str, address: str) -> None:
        self.location_id = location_id
        self.name = name
        self.address = address
        self.inventory: List[Vehicle] = []

    def get_available_vehicles(self, period: DateRange) -> List[Vehicle]:
        return [v for v in self.inventory if v.is_available_for(period)]


# ============================================================================
# Pricing Strategy Pattern (Open/Closed Principle)
# ============================================================================

class PricingCalculator(ABC):
    """Strategy interface for pricing"""

    @abstractmethod
    def calculate_price(self, reservation: Reservation) -> Money:
        pass


class BaseRatePricing(PricingCalculator):
    def calculate_price(self, reservation: Reservation) -> Money:
        vehicle = reservation.vehicle
        days = reservation.rental_period.get_days()

        base = vehicle.get_daily_rate().multiply(days)

        add_on_total = Money(Decimal('0'))
        for addon in reservation.add_ons:
            add_on_total = add_on_total.add(addon.daily_rate.multiply(days))

        total = base.add(add_on_total)
        discount = reservation.customer.get_discount_rate()
        return total.multiply(1.0 - discount)


class SeasonalPricing(PricingCalculator):
    def __init__(self, base_pricing: PricingCalculator) -> None:
        self.base_pricing = base_pricing
        self.seasonal_multipliers = {
            6: 1.3, 7: 1.3, 8: 1.3,  # Summer
            12: 1.2, 1: 1.2  # Winter holidays
        }

    def calculate_price(self, reservation: Reservation) -> Money:
        base_price = self.base_pricing.calculate_price(reservation)
        month = reservation.rental_period.start_date.month
        multiplier = self.seasonal_multipliers.get(month, 1.0)
        return base_price.multiply(multiplier)


class DemandBasedPricing(PricingCalculator):
    def __init__(self, base_pricing: PricingCalculator,
                 vehicle_repo: 'VehicleRepository') -> None:
        self.base_pricing = base_pricing
        self.vehicle_repo = vehicle_repo

    def calculate_price(self, reservation: Reservation) -> Money:
        base_price = self.base_pricing.calculate_price(reservation)

        utilization = self.vehicle_repo.get_utilization(
            reservation.vehicle.assigned_location,
            type(reservation.vehicle).__name__,
            reservation.rental_period
        )

        multiplier = 1.0
        if utilization > 0.90:
            multiplier = 1.5
        elif utilization > 0.75:
            multiplier = 1.25
        elif utilization > 0.60:
            multiplier = 1.1

        return base_price.multiply(multiplier)


# ============================================================================
# Repository Interfaces (Dependency Inversion)
# ============================================================================

class VehicleRepository(ABC):
    @abstractmethod
    def find_by_id(self, vin: str) -> Vehicle:
        pass

    @abstractmethod
    def find_available_by_type(self, vehicle_type: str, location: Location,
                               dates: DateRange) -> List[Vehicle]:
        pass

    @abstractmethod
    def save(self, vehicle: Vehicle) -> None:
        pass

    @abstractmethod
    def get_utilization(self, location: Location, vehicle_type: str,
                       dates: DateRange) -> float:
        pass


class ReservationRepository(ABC):
    @abstractmethod
    def find_by_id(self, reservation_id: str) -> Reservation:
        pass

    @abstractmethod
    def find_by_customer(self, customer_id: str) -> List[Reservation]:
        pass

    @abstractmethod
    def find_overlapping(self, vehicle: Vehicle, dates: DateRange) -> List[Reservation]:
        pass

    @abstractmethod
    def save(self, reservation: Reservation) -> None:
        pass


# ============================================================================
# Notification Service (Observer Pattern)
# ============================================================================

class NotificationService:
    def send_reservation_confirmation(self, reservation: Reservation) -> None:
        msg = (f"Reservation {reservation.reservation_id} confirmed for "
              f"{reservation.customer.name}")
        self._send_email(reservation.customer.email, msg)

    def send_check_out_confirmation(self, transaction: RentalTransaction) -> None:
        msg = "Vehicle checked out. Enjoy your trip!"
        self._send_email(transaction.reservation.customer.email, msg)

    def send_overdue_alert(self, transaction: RentalTransaction) -> None:
        msg = "Your rental is overdue. Please return the vehicle."
        self._send_email(transaction.reservation.customer.email, msg)

    def send_return_receipt(self, transaction: RentalTransaction,
                          final_charge: Money) -> None:
        msg = f"Vehicle returned. Total: {final_charge.amount}"
        self._send_email(transaction.reservation.customer.email, msg)

    def _send_email(self, email: str, message: str) -> None:
        print(f"Email to {email}: {message}")

This implementation showcases:

  • SOLID Principles clearly demonstrated in each component
  • Strategy Pattern for extensible pricing without modifying existing code
  • Repository Pattern for clean data access abstraction
  • Observer Pattern for decoupled notifications
  • Template Method in Vehicle hierarchy
  • Clean, readable code with proper type hints and documentation

Comments