Machine Coding Problem

Car Rental System

maco60macoAllmarketplacestrategy-patternstate-patternconcurrent-collectionsatomic-reference-locksobserver-notifications
Commonly Asked By:UberLyftHertzTuro

Requirements & System Scope

Functional Scope (In-Scope)

  • Dynamic Billing strategies (Strategy Pattern): Delegate base, late, and mileage cost logic dynamically per vehicle category.
  • Safe Atomic Reservations (State Pattern): Enforce bullet-proof status transitions using synchronized lock sweeps and Atomic Reference guards.
  • Observer Notifications telemetry: Register alert triggers to listen to inventory shortages and status adjustments.
  • Damage Assessment returning flows: Audit return variables to calculate damage fees and send vehicles to maintenance states automatically.

Explicit Boundaries (Out-of-Scope)

  • No keyless vehicle ignitions IoT signals: Excludes direct Bluetooth socket streams or physical door locker toggling.
  • No dynamic gas fuel levels detection: Does not hook into real fuel tank sensors.

Class Diagram & Entity Relationships

Class organization demonstrating modular pricing strategies, state transitions, and observers:

Loading...

Design Patterns & SOLID Principles

  • Strategy Pattern (Rental Pricing): Encapsulates vehicle category pricing rules into unique pricing strategies, allowing the system to scale and host custom premium vehicles cleanly.
  • Observer Pattern (Fleet Alerts): Decouples automated low-stock warnings and dashboard visual refresh registers from core booking algorithms.

Core Execution Workflows

Booking & Return Lifecycle

  1. Client checks vehicle capacity and calls reserveVehicle(userId, SUV, plannedDays).
  2. Identify available SUV in fleet, execute atomic check-and-reserve transaction, and generate reservation receipt.
  3. Upon customer arrival, transition vehicle status from RESERVED to RENTED.
  4. Upon vehicle dropoff, calculate:
    1. Expected base cost via Strategy daily pricing constraints.
    2. Additional mileage fees and late returned hourly overage rates.
    3. Incorporate physical damage penalty assessments and issue final Invoice.

Concurrency & Thread Safety Strategy

When multiple customers try to book the same vehicle at the same time:

  • Thread-Safe Map Store: Houses active booking state and fleet registries inside thread-safe Concurrent Maps.
  • Atomic Compare-And-Swap (CAS): Uses thread-safe Atomic References inside the Vehicle state manager to prevent double booking.

Complete Clean Code Blueprint

Clean reference designs demonstrating stateful fleet rentals in Java and Python:

// โ”€โ”€โ”€ JAVA BLUEPRINT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.*;

enum VehicleType { SEDAN, SUV, TRUCK }
enum VehicleStatus { AVAILABLE, RESERVED, RENTED, UNDER_MAINTENANCE }

interface FleetObserver {
    void onVehicleStatusChanged(String vehicleId, VehicleStatus oldStatus, VehicleStatus newStatus);
    void onLowAvailabilityAlert(VehicleType type, int count);
}

interface RentalPricingStrategy {
    double calculateBase(int days);
    double calculateLateFees(int lateHours);
    double calculateMileageFees(double miles);
}

class SedanPricingStrategy implements RentalPricingStrategy {
    @Override public double calculateBase(int days) { return days * 40.0; }
    @Override public double calculateLateFees(int lateHours) { return lateHours * 15.0; }
    @Override public double calculateMileageFees(double miles) { return miles * 0.15; }
}

class SUVPricingStrategy implements RentalPricingStrategy {
    @Override public double calculateBase(int days) { return days * 75.0; }
    @Override public double calculateLateFees(int lateHours) { return lateHours * 25.0; }
    @Override public double calculateMileageFees(double miles) { return miles * 0.25; }
}

class Vehicle {
    private final String id;
    private final VehicleType type;
    private final String licensePlate;
    private final RentalPricingStrategy pricingStrategy;
    private final AtomicReference<VehicleStatus> status = new AtomicReference<>(VehicleStatus.AVAILABLE);

    public Vehicle(String id, VehicleType type, String licensePlate, RentalPricingStrategy strategy) {
        this.id = id;
        this.type = type;
        this.licensePlate = licensePlate;
        this.pricingStrategy = strategy;
    }

    public String getId() { return id; }
    public VehicleType getType() { return type; }
    public String getLicensePlate() { return licensePlate; }
    public RentalPricingStrategy getPricingStrategy() { return pricingStrategy; }
    public VehicleStatus getStatus() { return status.get(); }

    public boolean reserve() {
        return status.compareAndSet(VehicleStatus.AVAILABLE, VehicleStatus.RESERVED);
    }

    public boolean rent() {
        return status.compareAndSet(VehicleStatus.RESERVED, VehicleStatus.RENTED);
    }

    public void release(boolean needsMaintenance) {
        status.set(needsMaintenance ? VehicleStatus.UNDER_MAINTENANCE : VehicleStatus.AVAILABLE);
    }
}

class Reservation {
    private final String id;
    private final String userId;
    private final Vehicle vehicle;
    private final int plannedDays;
    private final long timestamp;

    public Reservation(String id, String userId, Vehicle vehicle, int plannedDays) {
        this.id = id;
        this.userId = userId;
        this.vehicle = vehicle;
        this.plannedDays = plannedDays;
        this.timestamp = System.currentTimeMillis();
    }

    public String getId() { return id; }
    public String getUserId() { return userId; }
    public Vehicle getVehicle() { return vehicle; }
    public int getPlannedDays() { return plannedDays; }
}

class Invoice {
    private final String reservationId;
    private final double baseCost;
    private final double lateFee;
    private final double mileageFee;
    private final double damageFee;
    private final double totalCost;

    public Invoice(String reservationId, double baseCost, double lateFee, double mileageFee, double damageFee) {
        this.reservationId = reservationId;
        this.baseCost = baseCost;
        this.lateFee = lateFee;
        this.mileageFee = mileageFee;
        this.damageFee = damageFee;
        this.totalCost = baseCost + lateFee + mileageFee + damageFee;
    }

    public double getTotalCost() { return totalCost; }

    @Override
    public String toString() {
        return String.format("Invoice[Reservation: %s, Base: $%.2f, Late: $%.2f, Mileage: $%.2f, Damage: $%.2f, Total: $%.2f]",
                reservationId, baseCost, lateFee, mileageFee, damageFee, totalCost);
    }
}

class CarRentalService {
    private final Map<String, Vehicle> fleet = new ConcurrentHashMap<>();
    private final Map<String, Reservation> reservations = new ConcurrentHashMap<>();
    private final List<FleetObserver> observers = new CopyOnWriteArrayList<>();
    private final ReentrantLock systemLock = new ReentrantLock();

    public void registerObserver(FleetObserver obs) { observers.add(obs); }
    public void addVehicle(Vehicle v) { fleet.put(v.getId(), v); }

    public Reservation reserveVehicle(String userId, VehicleType type, int plannedDays) {
        systemLock.lock();
        try {
            Vehicle target = fleet.values().stream()
                    .filter(v -> v.getType() == type && v.getStatus() == VehicleStatus.AVAILABLE)
                    .findFirst()
                    .orElse(null);

            if (target != null && target.reserve()) {
                notifyStatusChange(target.getId(), VehicleStatus.AVAILABLE, VehicleStatus.RESERVED);
                checkLowAvailability(type);
                Reservation res = new Reservation(UUID.randomUUID().toString(), userId, target, plannedDays);
                reservations.put(res.getId(), res);
                return res;
            }
            return null;
        } finally {
            systemLock.unlock();
        }
    }

    public boolean pickupVehicle(String reservationId) {
        Reservation res = reservations.get(reservationId);
        if (res != null && res.getVehicle().rent()) {
            notifyStatusChange(res.getVehicle().getId(), VehicleStatus.RESERVED, VehicleStatus.RENTED);
            return true;
        }
        return false;
    }

    public Invoice returnVehicle(String reservationId, int actualDays, int lateHours, double extraMiles, double damageFee) {
        Reservation res = reservations.get(reservationId);
        if (res == null) return null;

        Vehicle v = res.getVehicle();
        RentalPricingStrategy pricing = v.getPricingStrategy();

        double base = pricing.calculateBase(res.getPlannedDays());
        double late = pricing.calculateLateFees(lateHours);
        double mileage = pricing.calculateMileageFees(extraMiles);

        boolean needsMaint = damageFee > 1000.0;
        v.release(needsMaint);
        notifyStatusChange(v.getId(), VehicleStatus.RENTED, v.getStatus());

        Invoice inv = new Invoice(reservationId, base, late, mileage, damageFee);
        reservations.remove(reservationId);
        return inv;
    }

    private void notifyStatusChange(String id, VehicleStatus oldS, VehicleStatus newS) {
        for (FleetObserver obs : observers) obs.onVehicleStatusChanged(id, oldS, newS);
    }

    private void checkLowAvailability(VehicleType type) {
        long count = fleet.values().stream().filter(v -> v.getType() == type && v.getStatus() == VehicleStatus.AVAILABLE).count();
        if (count < 2) {
            for (FleetObserver obs : observers) obs.onLowAvailabilityAlert(type, (int) count);
        }
    }
}

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...