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.
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);
}
}
}