Design a Car Rental System
OOP design for a car rental platform covering vehicle inventory management, reservation lifecycle, pricing strategies, branch/location management, and late return penalty handling.
The Problem
Your company operates a car rental chain with 30 branches across the country. Walk-in customers queue at the counter while agents manually check availability on spreadsheets. Double-bookings happen weekly because two agents reserve the same vehicle simultaneously. Pricing is inconsistent: one branch charges daily rates, another offers weekly discounts, and nobody tracks seasonal surges. When a customer returns a car late, the penalty calculation is a guessing game.
A car rental system like Enterprise or Hertz solves this. It manages vehicle inventory across branches, handles reservations with guaranteed availability, applies consistent pricing strategies, and automates late return penalties. Customers search for vehicles by type, date range, and location; the system returns only what is genuinely available.
Design the core classes for a car rental system that supports multi-branch vehicle inventory, reservation lifecycle management, pluggable pricing strategies, vehicle state tracking, and late return penalty handling.
Requirements
Clarifying Questions
Before jumping into class design, ask questions to turn the vague prompt into a concrete specification. Cover four areas: core actions, error handling, boundaries, and future extensions.
You: "What is the core rental flow? Does the customer reserve online first, or walk in and rent immediately?"
Interviewer: "Both. A customer can reserve a vehicle type in advance for specific dates, or walk in and rent whatever is available. Reservations guarantee a vehicle type, not a specific car."
Two modes: reservation-based and walk-in. Reservations lock a vehicle type at a branch, not a specific VIN. That distinction matters for availability calculation.
You: "How should vehicle availability work? Do we track individual cars or just counts per type?"
Interviewer: "Track individual cars. Each vehicle has its own state: available, reserved, rented, under maintenance. But customers search by vehicle type, not by specific car."
Individual vehicle tracking with type-level search. The availability query filters by type and date range, then counts vehicles not already reserved or rented.
You: "What vehicle types do we support? Just cars, or also SUVs and trucks?"
Interviewer: "Cars, SUVs, and trucks for now. The design should support adding new types easily."
A vehicle type hierarchy. Open-Closed Principle: adding a new type should not require editing existing classes.
You: "How does pricing work? Is it a flat daily rate, or more complex?"
Interviewer: "Multiple pricing strategies. Daily rate is the baseline, but we also want weekly discounts, seasonal surges, and optional mileage-based charges. Different branches might use different pricing."
Strategy pattern for pricing. Each branch can plug in its own pricing strategy without changing the rental logic.
You: "What happens when a customer returns a car late? Is there a grace period?"
Interviewer: "Yes. A 1-hour grace period, then a per-hour penalty. The penalty rate depends on the vehicle type."
Late return handling with a grace period and type-dependent penalty rates.
You: "Can customers pick up at one branch and drop off at another?"
Interviewer: "Not in the initial design. Mention it as an extension. All rentals are same-branch pickup and return."
Good, that simplifies the first version. Cross-branch transfers go into extensibility.
You: "Do we need insurance and damage tracking?"
Interviewer: "Insurance is an optional add-on to the rental. Damage assessment is out of scope for now."
Insurance as a decorator or optional component on the rental agreement.
You: "Is persistence in scope, or in-memory only?"
Interviewer: "In-memory only. No database."
Perfect. You have now clarified scope and ruled out unnecessary complexity.
Final Requirements
Functional Requirements:
- Manage vehicle inventory across multiple branches (add, remove, track state)
- Search available vehicles by type, date range, and branch location
- Create reservations that guarantee a vehicle type at a branch for specific dates
- Process vehicle pickup (reserve to rented) and return (rented to returned)
- Apply pluggable pricing strategies: daily rate, weekly discount, seasonal surge
- Calculate late return penalties with a 1-hour grace period
- Support optional insurance add-ons for rental agreements
- Cancel reservations with a configurable cancellation policy
Non-Functional Requirements:
- Thread safety for concurrent reservation operations on the same branch
- Extensibility for new vehicle types and pricing strategies without modifying existing code
- Clean separation between domain logic (pricing, availability) and orchestration
Out of Scope:
- Cross-branch one-way rentals (extension)
- GPS tracking and mileage logging
- UI / persistence / database
- Damage assessment and repair workflow
- Loyalty programs (extension)
Example Inputs and Outputs
Scenario 1: Search and Reserve
- Input: Customer searches for SUVs at Downtown branch, Jan 10-13
- Expected: 3 available SUVs. Customer reserves "SUV" type, receives
R-1001 - Why: Validates availability search and reservation creation
Scenario 2: Pickup and Return (on time)
- Input: Customer arrives Jan 10 with
R-1001. Returns Jan 13 at 2pm (due: noon) - Expected: SUV-042 assigned, marked RENTED. Within grace period on return. Cost: 3 x $85 = $255
- Why: Validates full rental lifecycle and grace period
Scenario 3: Late Return with Penalty
- Input: Customer returns car 5 hours past due time
- Expected: 1-hour grace applied, 4 penalty hours charged at $15/hr = $60 added to base cost
- Why: Validates late return penalty calculation
Try It Yourself
Try it yourself
Before reading the solution, spend 15-20 minutes sketching your own class diagram. Focus on the vehicle state machine (which states can transition to which?) and how pricing strategies plug into the rental calculation. Compare your approach with the walkthrough below.
Step 1: Identify Core Entities
Start by asking: what are the main "things" in this problem? Look for nouns in your requirements: branch, vehicle, reservation, customer, payment, rental agreement, insurance. Each noun that has its own lifecycle or responsibility becomes a class.
A common mistake is lumping everything into a single CarRentalSystem god class. Good design means each class has a single, clear job. The rental system orchestrates, but individual branches own their inventory, and vehicles own their state.
| Entity | Responsibility | Key attributes |
|---|---|---|
| CarRentalSystem | Top-level orchestrator. Routes requests to branches, manages global search. | branches, customers |
| Branch | Owns a fleet of vehicles at a location. Handles availability queries for its inventory. | name, address, vehicles, pricing strategy |
| Vehicle | Individual car/SUV/truck. Tracks its own state through the lifecycle. | vehicleId, type, licensePlate, state, dailyRate |
| VehicleType | Enum of supported vehicle categories with base pricing metadata. | CAR, SUV, TRUCK |
| Reservation | A time-bound hold on a vehicle type at a branch. Core booking entity. | reservationId, customer, branch, vehicleType, dateRange, status |
| Customer | Person renting a vehicle. Holds license and contact info. | customerId, name, licenseNumber, email |
| RentalAgreement | Active rental binding a specific vehicle to a customer. Created at pickup. | reservation, vehicle, pickupTime, expectedReturn, insurance |
| Payment | Calculates and records the final charge including penalties. | amount, breakdown, paymentMethod |
| Insurance | Optional add-on with coverage type and daily premium. | type, dailyPremium |
| PricingStrategy | Pluggable pricing logic. Different branches can use different strategies. | calculateCost(days, vehicleType) |
Notice that Reservation and RentalAgreement are separate. A reservation is a promise; a rental agreement is the active contract once the customer picks up the car. Merging them would violate SRP because reservations can be cancelled (no vehicle assigned) while agreements track actual vehicle usage.
Step 2: Define Relationships and Class Design
Class Diagram
Class Interface Derivation
Branch (the orchestrator)
Branch is the central coordinator for a location. It owns vehicles, manages reservations, and delegates pricing to its strategy. I find this to be the most important class to get right because interviewers probe its methods heavily.
Deriving state and methods from requirements:
| Requirement | State / Method |
|---|---|
| "Manage vehicle inventory across branches" | vehicles: List<Vehicle> |
| "Search available vehicles by type and date" | getAvailableVehicles(type, dateRange) |
| "Apply pluggable pricing strategies" | pricingStrategy: PricingStrategy |
| "Process pickup and return" | processPickup(resId), processReturn(agId) |
Vehicle (state machine)
Vehicle tracks its own lifecycle. Each state transition has rules: you cannot rent a vehicle that is under maintenance, and you cannot send a rented vehicle to maintenance directly.
Vehicle state machine:
Each state transition validates the current state before allowing the change. Calling pickup() on a vehicle that is not RESERVED throws an illegal state exception.
DateRange (value object)
DateRange is a simple value object (a Java record) that encapsulates start and end times. The overlap check is the core algorithm for availability queries.
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with NotesFromSDE Premium.