Design a Parking Lot
Let's make an object-oriented design for a multi-floor parking lot. A parking lot or car park is a dedicated cleared area that is intended for parking vehicles. In most countries where cars are a major mode of transportation, parking lots are a feature of every city and suburban area. Shopping malls, sports stadiums, megachurches, and similar venues often feature parking lots over large areas.
Requirements
We will focus on the following set of requirements while designing the parking lot:
- The parking lot should have multiple floors where customers can park their cars.
- The parking lot should have multiple entry and exit points.
- Customers can collect a parking ticket from the entry points and can pay the parking fee at the exit points on their way out.
- Customers can pay the tickets at the automated exit panel or to the parking attendant.
- Customers can pay via both cash and credit cards.
- Customers should also be able to pay the parking fee at the customer's info portal on each floor. If the customer has paid at the info portal, they don't have to pay at the exit.
- The system should not allow more vehicles than the maximum capacity of the parking lot. If the parking is full, the system should be able to show a message at the entrance panel and on the parking display board on the ground floor.
- Each parking floor will have many parking spots. The system should support multiple types of parking spots such as Compact, Large, Handicapped, Motorcycle, etc.
- The Parking lot should have some parking spots specified for electric cars. These spots should have an electric panel through which customers can pay and charge their vehicles.
- The system should support parking for different types of vehicles like car, truck, van, motorcycle, etc.
- Each parking floor should have a display board showing any free parking spot for each spot type.
- The system should support a per-hour parking fee model. For example, customers have to pay $4 for the first hour, $3.5 for the second and third hours, and $2.5 for all the remaining hours.
Problem Analysis
Before jumping into code or class diagrams, a thorough problem analysis helps in:
- Identifying key entities (Vehicle, ParkingSpot, Level, etc.)
- Defining relationships (A Level has many ParkingSpots)
- Recognizing constraints (e.g., spot types must match vehicle types)
- Understanding concurrency needs (multiple entries/exits)
Clarifying Ambiguities:
- What if all spots are full?
- Can trucks park across multiple spots?
- Should we track time parked or fees?
Use clarifying questions in interviews to refine requirements. Create assumptions when needed, but always state them clearly.
Use case diagram
Here are the main Actors in our system:
- Admin: Mainly responsible for adding and modifying parking floors, parking spots, entrance, and exit panels, adding/removing parking attendants, etc.
- Customer: All customers can get a parking ticket and pay for it.
- Parking attendant: Parking attendants can do all the activities on the customer's behalf, and can take cash for ticket payment.
- System: To display messages on different info panels, as well as assigning and removing a vehicle from a parking spot.
Here are the top use cases for Parking Lot:
- Add/Remove/Edit parking floor: To add, remove or modify a parking floor from the system. Each floor can have its own display board to show free parking spots.
- Add/Remove/Edit parking spot: To add, remove or modify a parking spot on a parking floor.
- Add/Remove a parking attendant: To add or remove a parking attendant from the system.
- Take ticket: To provide customers with a new parking ticket when entering the parking lot.
- Scan ticket: To scan a ticket to find out the total charge.
- Credit card payment: To pay the ticket fee with credit card.
- Cash payment: To pay the parking ticket through cash.
- Add/Modify parking rate: To allow admin to add or modify the hourly parking rate.
Activity diagram
TODO:
Class diagram
Here are the main classes of our Parking Lot System:
-
ParkingLot: The central part of the organization for which this software has been designed. It has attributes like
Name
to distinguish it from any other parking lots andAddress
to define its location. -
ParkingFloor: The parking lot will have many parking floors.
-
ParkingSpot: Each parking floor will have many parking spots. Our system will support different parking spots:
- Handicapped
- Compact
- Large
- Motorcycle
- Electric
-
Account: We will have two types of accounts in the system: one for an Admin, and the other for a parking attendant.
-
ParkingTicket: This class will encapsulate a parking ticket. Customers will take a ticket when they enter the parking lot.
-
Vehicle: Vehicles will be parked in the parking spots. Our system will support different types of vehicles:
- Car
- Truck
- Electric
- Van
- Motorcycle
-
EntrancePanel and ExitPanel:
EntrancePanel
will print tickets, andExitPanel
will facilitate payment of the ticket fee. -
Payment: This class will be responsible for making payments. The system will support credit card and cash transactions.
-
ParkingRate: This class will keep track of the hourly parking rates. It will specify a dollar amount for each hour. For example, for a two-hour parking ticket, this class will define the cost for the first and the second hour.
-
ParkingDisplayBoard: Each parking floor will have a display board to show available parking spots for each spot type. This class will be responsible for displaying the latest availability of free parking spots to the customers.
-
ParkingAttendantPortal: This class will encapsulate all the operations that an attendant can perform, like scanning tickets and processing payments.
-
CustomerInfoPortal: This class will encapsulate the info portal that customers use to pay for the parking ticket. Once paid, the info portal will update the ticket to keep track of the payment.
-
ElectricPanel: Customers will use the electric panels to pay and charge their electric vehicles.
Relationship between the classes
Here’s how the main classes relate to each other in the Parking Lot System:
-
ParkingLot
- has many Level (or ParkingFloor)
- has ParkingRate
- has List<EntrancePanel> and List<ExitPanel>
- has List<ParkingAttendant>
- is a Singleton (only one instance)
-
Level (ParkingFloor)
- has many ParkingSpot
- has ParkingDisplayBoard
-
ParkingSpot
- is abstract; concrete types: HandicappedSpot, CompactSpot, LargeSpot, MotorcycleSpot, ElectricSpot
- has ParkingSpotType
- can be occupied by Vehicle
-
Vehicle
- is abstract; concrete types: Car, Truck, ElectricCar, Van, Motorcycle
- has VehicleType
- has licenseNumber
-
ParkingTicket
- is issued to Vehicle
- is created by EntrancePanel
- is paid via Payment
- has entry/exit time, spot info
-
EntrancePanel
- issues ParkingTicket to Vehicle
- interacts with ParkingLot
-
ExitPanel
- processes payment for ParkingTicket
- interacts with ParkingLot
-
Payment
- is interface; concrete types: CreditCardPayment, CashPayment
- pays for ParkingTicket
-
ParkingRate
- calculates fee for ParkingTicket
-
ParkingDisplayBoard
- shows available ParkingSpot count per type
- is notified by Level (Observer pattern)
-
ParkingAttendantPortal
- used by ParkingAttendant to manage tickets and payments
-
CustomerInfoPortal
- used by customers to pay for ParkingTicket
-
ElectricPanel
- used by customers to pay and charge ElectricCar
- associated with ElectricSpot
Summary of relationships:
- Composition: Level → ParkingSpot, Level → ParkingDisplayBoard
- Aggregation: ParkingLot → Level, ParkingLot → EntrancePanel/ExitPanel
- Inheritance: Vehicle hierarchy, ParkingSpot hierarchy, Payment hierarchy
- Association: Ticket ↔ Vehicle, Ticket ↔ Spot, Payment ↔ Ticket
- Observer: Level → ParkingDisplayBoard (notifies on spot status change)
- Singleton: ParkingLot, ParkingRate
Key Design Ideas:
- Use composition for Level → ParkingSpots
- Use inheritance for Vehicle and Spot hierarchies
- Use interfaces for entry/exit logic
Design Patterns Applied
- Singleton – Ensure only one instance of
ParkingLot
exists - Factory – Create
Vehicle
orParkingSpot
based on type - Strategy – Implement spot allocation strategies
- Observer – Notify availability boards when a spot is filled or released
Code
Example Java Classes
// Enums for Spot and Vehicle Types
public enum VehicleType {
CAR, TRUCK, ELECTRIC, VAN, MOTORCYCLE
}
public enum ParkingSpotType {
HANDICAPPED, COMPACT, LARGE, MOTORCYCLE, ELECTRIC
}
// Vehicle base class and its derived classes
public abstract class Vehicle {
private String licenseNumber;
private final VehicleType type;
public Vehicle(String licenseNumber, VehicleType type) {
this.licenseNumber = licenseNumber;
this.type = type;
}
public VehicleType getType() {
return type;
}
public String getLicenseNumber() {
return licenseNumber;
}
}
public class Car extends Vehicle {
public Car(String license) {
super(license, VehicleType.CAR);
}
}
public class ElectricCar extends Vehicle {
public ElectricCar(String license) {
super(license, VehicleType.ELECTRIC);
}
}
// Spot base class and its derived classes
public abstract class ParkingSpot {
private final String spotNumber;
private final ParkingSpotType type;
private boolean isFree;
private Vehicle currentVehicle;
public ParkingSpot(String spotNumber, ParkingSpotType type) {
this.spotNumber = spotNumber;
this.type = type;
this.isFree = true;
}
public boolean isFree() {
return isFree;
}
public void assignVehicle(Vehicle vehicle) {
this.currentVehicle = vehicle;
this.isFree = false;
}
public void removeVehicle() {
this.currentVehicle = null;
this.isFree = true;
}
public ParkingSpotType getType() {
return type;
}
}
public class CompactSpot extends ParkingSpot {
public CompactSpot(String number) {
super(number, ParkingSpotType.COMPACT);
}
}
public class ElectricSpot extends ParkingSpot {
private ElectricPanel panel;
public ElectricSpot(String number, ElectricPanel panel) {
super(number, ParkingSpotType.ELECTRIC);
this.panel = panel;
}
public ElectricPanel getPanel() {
return panel;
}
}
// Ticket
public class Ticket {
private final String ticketNumber;
private final LocalDateTime entryTime;
private LocalDateTime exitTime;
private final ParkingSpot spot;
private double amount;
private boolean isPaid;
public Ticket(String ticketNumber, ParkingSpot spot) {
this.ticketNumber = ticketNumber;
this.entryTime = LocalDateTime.now();
this.spot = spot;
this.isPaid = false;
}
public void markPaid(double amount) {
this.amount = amount;
this.isPaid = true;
this.exitTime = LocalDateTime.now();
}
public ParkingSpot getSpot() {
return spot;
}
public LocalDateTime getEntryTime() {
return entryTime;
}
public LocalDateTime getExitTime() {
return exitTime;
}
public boolean isPaid() {
return isPaid;
}
}
// Level / Floor
public class Level {
private final int floorNumber;
private final List<ParkingSpot> spots;
private final ParkingDisplayBoard displayBoard;
public Level(int floorNumber, List<ParkingSpot> spots, ParkingDisplayBoard board) {
this.floorNumber = floorNumber;
this.spots = spots;
this.displayBoard = board;
}
public Ticket park(Vehicle vehicle) {
for (ParkingSpot spot : spots) {
if (spot.isFree() && isSpotCompatible(spot.getType(), vehicle.getType())) {
spot.assignVehicle(vehicle);
displayBoard.update(spot.getType(), false);
return new Ticket(UUID.randomUUID().toString(), spot);
}
}
return null;
}
private boolean isSpotCompatible(ParkingSpotType spotType, VehicleType vehicleType) {
switch (vehicleType) {
case MOTORCYCLE: return true;
case CAR: return spotType == ParkingSpotType.COMPACT || spotType == ParkingSpotType.LARGE || spotType == ParkingSpotType.HANDICAPPED;
case ELECTRIC: return spotType == ParkingSpotType.ELECTRIC;
case TRUCK: return spotType == ParkingSpotType.LARGE;
default: return false;
}
}
}
// Display Board
public class ParkingDisplayBoard {
private final Map<ParkingSpotType, Integer> availableSpots;
public ParkingDisplayBoard(Map<ParkingSpotType, Integer> initialSpots) {
this.availableSpots = initialSpots;
}
public void update(ParkingSpotType type, boolean freed) {
availableSpots.put(type, availableSpots.getOrDefault(type, 0) + (freed ? 1 : -1));
}
public void showStatus() {
availableSpots.forEach((type, count) -> System.out.println(type + ": " + count));
}
}
// Payment
public abstract class Payment {
public abstract boolean pay(Ticket ticket);
}
public class CashPayment extends Payment {
@Override
public boolean pay(Ticket ticket) {
ticket.markPaid(calculateFee(ticket));
return true;
}
private double calculateFee(Ticket ticket) {
return ParkingRate.getInstance().calculate(ticket.getEntryTime(), LocalDateTime.now());
}
}
public class CreditCardPayment extends Payment {
@Override
public boolean pay(Ticket ticket) {
ticket.markPaid(calculateFee(ticket));
return true;
}
private double calculateFee(Ticket ticket) {
return ParkingRate.getInstance().calculate(ticket.getEntryTime(), LocalDateTime.now());
}
}
// Singleton ParkingLot
public class ParkingLot {
private static final ParkingLot INSTANCE = new ParkingLot();
private final List<Level> levels = new ArrayList<>();
private ParkingLot() {}
public static ParkingLot getInstance() {
return INSTANCE;
}
public Ticket parkVehicle(Vehicle vehicle) {
for (Level level : levels) {
Ticket ticket = level.park(vehicle);
if (ticket != null) return ticket;
}
return null;
}
public void unparkVehicle(Ticket ticket) {
ticket.getSpot().removeVehicle();
}
public void addLevel(Level level) {
levels.add(level);
}
}
// Parking Rate
public class ParkingRate {
private static final ParkingRate INSTANCE = new ParkingRate();
private final List<Double> rates = List.of(4.0, 3.5, 3.5);
private ParkingRate() {}
public static ParkingRate getInstance() {
return INSTANCE;
}
public double calculate(LocalDateTime entry, LocalDateTime exit) {
long hours = Duration.between(entry, exit).toHours();
double total = 0;
for (int i = 0; i < hours; i++) {
if (i < rates.size()) total += rates.get(i);
else total += 2.5;
}
return total;
}
}
// Electric Panel
public class ElectricPanel {
public void chargeVehicle(Vehicle vehicle) {
System.out.println("Charging electric vehicle: " + vehicle.getLicenseNumber());
}
}
// Entry and Exit Panel
public class EntrancePanel {
public Ticket issueTicket(Vehicle vehicle) {
return ParkingLot.getInstance().parkVehicle(vehicle);
}
}
public class ExitPanel {
public boolean processPayment(Ticket ticket, Payment paymentMethod) {
return paymentMethod.pay(ticket);
}
}
// Customer Info Portal
public class CustomerInfoPortal {
public boolean payTicket(Ticket ticket, Payment method) {
return method.pay(ticket);
}
}
// Demo class
public class ParkingLotDemo {
public static void main(String[] args) throws InterruptedException {
ElectricPanel electricPanel = new ElectricPanel();
List<ParkingSpot> spots = new ArrayList<>();
spots.add(new CompactSpot("C1"));
spots.add(new ElectricSpot("E1", electricPanel));
Map<ParkingSpotType, Integer> spotMap = new HashMap<>();
spotMap.put(ParkingSpotType.COMPACT, 1);
spotMap.put(ParkingSpotType.ELECTRIC, 1);
ParkingDisplayBoard board = new ParkingDisplayBoard(spotMap);
Level level = new Level(1, spots, board);
ParkingLot.getInstance().addLevel(level);
Vehicle car = new Car("KA-01-1234");
EntrancePanel entrance = new EntrancePanel();
Ticket ticket = entrance.issueTicket(car);
if (ticket != null) {
System.out.println("Vehicle parked at: " + ticket.getSpot().getType());
Thread.sleep(2000); // simulate time spent
Payment payment = new CreditCardPayment();
ExitPanel exitPanel = new ExitPanel();
if (exitPanel.processPayment(ticket, payment)) {
System.out.println("Payment successful. Amount: " + ParkingRate.getInstance().calculate(ticket.getEntryTime(), ticket.getExitTime()));
ParkingLot.getInstance().unparkVehicle(ticket);
}
} else {
System.out.println("No spot available!");
}
}
}