System Design — Parking Lot Design Interview Question Using Java

JavaInUse
Geek Culture
Published in
9 min readJul 6, 2022

System Design Questions are now part of the interview evaluation process of many organizations. These questions are usually open-ended and the interviewer tests how the candidate approaches the problem statement. If he can break down the problem statement and think in terms of object-oriented principles. If the candidate follows good coding practices like SOLID principles and the various design principles.
In the previous tutorial, we designed and implemented an Elevator System. In this tutorial, we will design a parking lot and implement the solution.

Video

In this tutorial, we will be making use of the following design patterns -

  • Singleton Design Pattern
  • Strategy Design Pattern
  • SOLID Design Principles

This implementation we will be dividing in 3 parts-

  • Discuss how we will be simulating the elevator design
  • Parking Design Code — Iteration-1 — Park Vehicle
  • Parking Design Code — Iteration-2 — Unpark Vehicle

Simulate the Parking Lot design

We will be simulating the scenario — A vehicle to be parked can be either a two-wheeler or a four-wheeler.

When the vehicle is to be parked, the owner will be provided a ticket. If there are no parking slots available, then a parking full exception will be thrown. When the vehicle is to be unparked, the owner must provide the ticket.
The parking charges will depend on -

  • If it is a weekend or a weekday
  • If the vehicle is a two-wheeler or a four-wheeler

Parking Design Code — Iteration-1 — Park Vehicle

The Parking Lot implementation will have the following domain classes -

  • Enum VehicleSize — This enum will have two values TWO WHEELER and FOUR WHEELER.
  • Create the enum VehicleSize
package com.javastructures.model;

public enum VehicleSize{
TWOWHEELER, FOURWHEELER;
}

Class Vehicle — The vehicle to be parked. It has the vehicle number and the type of vehicle i.e. two-wheeler or four-wheeler.

Create the domain Vehicle class

package com.javastructures.model;

public class Vehicle {

private String vehicleNumber;
private VehicleSize vehicleSize;

public Vehicle(String vehicleNumber, VehicleSize vehicleSize) {
this.vehicleNumber = vehicleNumber;
this.vehicleSize = vehicleSize;
}

public VehicleSize getVehicleSize() {
return vehicleSize;
}

public void setVehicleSize(VehicleSize vehicleSize) {
this.vehicleSize = vehicleSize;
}

public String getVehicleNumber() {
return vehicleNumber;
}

public void setVehicleNumber(String vehicleNumber) {
this.vehicleNumber = vehicleNumber;
}
}

Class Slot — This class represents the space in the parking lot which will be used to park the vehicle. A Parking lot will have a finite number of parking slots. This number will be initialized. The parking lot will have two types of slots -

  • Two Wheeler Parking Slots
  • Four Wheeler Parking Slots

Each slot will be having a unique slot number.

Create the domain Slot class

package com.javastructures.model;

public class Slot {

private int slotNumber;
private boolean isEmpty;
private Vehicle parkVehicle;

public Slot(int slotNumber) {
this.slotNumber = slotNumber;
isEmpty = true;
}

public int getSlotNumber() {
return slotNumber;
}

public void setSlotNumber(int slotNumber) {
this.slotNumber = slotNumber;
}

public boolean isEmpty() {
return isEmpty;
}

public void setEmpty(boolean isEmpty) {
this.isEmpty = isEmpty;
}

public Vehicle getParkVehicle() {
return parkVehicle;
}

public void setParkVehicle(Vehicle parkVehicle) {
this.parkVehicle = parkVehicle;
}

public void vacateSlot() {
parkVehicle = null;
this.isEmpty = true;
}

public void occupySlot(Vehicle parkVehicle) {
this.parkVehicle = parkVehicle;
this.isEmpty = false;
}
}

Class Ticket — Once the vehicle has been parked, the owner will be provided with the Ticket. It will have the slotNumber, vehicle number, time at which the vehicle has been parked and the vehicle size.

Create the domain Ticket class

package com.javastructures.model;

import java.util.Date;

public class Ticket {

private int slotNumber;
private String vehicleNumber;
private Date date;
private VehicleSize vehicleSize;

public Ticket(int slotNumber, String vehicleNumber, VehicleSize vehicleSize, Date date) {
super();
this.slotNumber = slotNumber;
this.vehicleNumber = vehicleNumber;
this.date = date;
this.setVehicleSize(vehicleSize);
}

public Date getDate() {
return date;
}

public void setDate(Date date) {
this.date = date;
}

public int getSlotNumber() {
return slotNumber;
}

public void setSlotNumber(int slotNumber) {
this.slotNumber = slotNumber;
}

public String getVehicleNumber() {
return vehicleNumber;
}

public void setVehicleNumber(String vehicleNumber) {
this.vehicleNumber = vehicleNumber;
}

public VehicleSize getVehicleSize() {
return vehicleSize;
}

public void setVehicleSize(VehicleSize vehicleSize) {
this.vehicleSize = vehicleSize;
}

@Override
public String toString() {
return "Ticket [slotNumber=" + slotNumber + ", vehicleNumber=" + vehicleNumber + ", date=" + date
+ ", vehicleSize=" + vehicleSize + "]";
}

}

We will be creating a custom exception named ParkingFullException -

package com.javastructures.exception;

public class ParkingFullException extends Exception {
public ParkingFullException(String message) {
super(message);
}
}

Create an interface named Parking which will have park method.

package com.javastructures.service;

import com.javastructures.exception.InvalidVehicleNumberException;
import com.javastructures.exception.ParkingFullException;
import com.javastructures.model.Ticket;
import com.javastructures.model.Vehicle;
import com.javastructures.strategy.ParkingChargeStrategy;

public interface Parking {

public Ticket park(Vehicle vehicle) throws ParkingFullException;

public int unPark(Ticket ticket, ParkingChargeStrategy parkingCostStrategy) throws InvalidVehicleNumberException;

}

Finally we will be implementing the above interface to write the logic for parking the vehicle and returning the ticket. This class will be a singleton class.

package com.javastructures.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import exception.InvalidVehicleNumberException;
import exception.ParkingFullException;
import model.Slot;
import model.Ticket;
import model.Vehicle;
import model.VehicleSize;
import strategy.ParkingChargeStrategy;

public class ParkingLot implements Parking {

private static ParkingLot parkingLot;
private final List<Slot> twoWheelerSlots;
private final List<Slot> fourWheelerSlots;

private ParkingLot() {
this.twoWheelerSlots = new ArrayList<>();
this.fourWheelerSlots = new ArrayList<>();
}

public static ParkingLot getParkingLot() {
if (parkingLot == null)
parkingLot = new ParkingLot();
return parkingLot;
}

public boolean initializeParkingSlots(int numberOfTwoWheelerParkingSlots, int numberOfFourWheelerParkingSlots) {

for (int i = 1; i <= numberOfTwoWheelerParkingSlots; i++) {
twoWheelerSlots.add(new Slot(i));
}

System.out.printf("Created a two wheeler parking lot with %s slots %n", numberOfTwoWheelerParkingSlots);

for (int i = 1; i <= numberOfFourWheelerParkingSlots; i++) {
fourWheelerSlots.add(new Slot(i));
}

System.out.printf("Created a four wheeler parking lot with %s slots %n", numberOfFourWheelerParkingSlots);
return true;
}

public Ticket park(Vehicle vehicle) throws ParkingFullException {
Slot nextAvailableSlot;
if (vehicle.getVehicleSize().equals(VehicleSize.FOURWHEELER)) {
nextAvailableSlot = getNextAvailableFourWheelerSlot();
} else {
nextAvailableSlot = getNextAvailableTwoWheelerSlot();
}
nextAvailableSlot.occupySlot(vehicle);
System.out.printf("Allocated slot number: %d \n", nextAvailableSlot.getSlotNumber());
Ticket ticket = new Ticket(nextAvailableSlot.getSlotNumber(), vehicle.getVehicleNumber(),
vehicle.getVehicleSize(), new Date());
return ticket;
}

private Slot getNextAvailableFourWheelerSlot() throws ParkingFullException {
for (Slot slot : fourWheelerSlots) {
if (slot.isEmpty()) {
return slot;
}
}
throw new ParkingFullException("No Empty Slot available");
}

private Slot getNextAvailableTwoWheelerSlot() throws ParkingFullException {
for (Slot slot : twoWheelerSlots) {
if (slot.isEmpty()) {
return slot;
}
}
throw new ParkingFullException("No Empty Slot available");
}
}

Next we will be creating a class to test the above parking logic.

package com.javastructures;

import com.javastructures.exception.ParkingFullException;
import com.javastructures.model.Ticket;
import com.javastructures.model.Vehicle;
import com.javastructures.model.VehicleSize;
import com.javastructures.service.ParkingLot;

public class TestParking {

public static void main(String[] args) throws ParkingFullException {
ParkingLot parkingLot = ParkingLot.getParkingLot();

parkingLot.initializeParkingSlots(10, 10);

Vehicle vehicle = new Vehicle("Mh12", VehicleSize.TWOWHEELER);

Ticket ticket = parkingLot.park(vehicle);
System.out.println(ticket);

Vehicle vehicle2 = new Vehicle("Mh13", VehicleSize.TWOWHEELER);

Ticket ticket2 = parkingLot.park(vehicle2);
System.out.println(ticket2);

}

}

ParkingLot Design Code — Iteration-2

In the first iteration we implemented the code to park the vehicle. In the second iteration we will be implementing the code to unpark the vehicle and calculate the charges.
Create an exception class named InvalidVehicleNumberException. This exception will be thrown if due to some reason the vehicle to be unparked cannot be found in the parking lot.

package com.javastructures.exception;

public class InvalidVehicleNumberException extends Exception {
public InvalidVehicleNumberException(String s) {
super(s);
}
}

Next we will be implementing classes to calculate the parking charges for the parked vehicles.

Create an interface named ParkingChargeStrategy.

package com.javastructures.strategy;

public interface ParkingChargeStrategy {
int getCharge(int parkHours);
}

For this interface we will provide four implementations as follows -
If the parked vehicle is a four wheeler and it is parked on a weekday.

package com.javastructures.strategy;

public class FourWheelerWeekDayChargeStrategy implements ParkingChargeStrategy {

@Override
public int getCharge(int parkHours) {
if (parkHours < 1) {
return 20;
}
return parkHours * 20;
}
}

If the parked vehicle is a four wheeler and it is parked on a weekend.

package com.javastructures.strategy;

public class FourWheelerWeekendChargeStrategy implements ParkingChargeStrategy {
@Override
public int getCharge(int parkHours) {
if (parkHours < 1) {
return 30;
}
return parkHours * 30;
}
}

If the parked vehicle is a two wheeler and it is parked on a weekday.

package com.javastructures.strategy;

public class TwoWheelerWeekDayChargeStrategy implements ParkingChargeStrategy {

@Override
public int getCharge(int parkHours) {
if (parkHours < 1) {
return 10;
}
return parkHours * 10;
}
}

If the parked vehicle is a two wheeler and it is parked on a weekend.

package com.javastructures.strategy;

public class TwoWheelerWeekendChargeStrategy implements ParkingChargeStrategy {
@Override
public int getCharge(int parkHours) {
if (parkHours < 1) {
return 15;
}
return parkHours * 15;
}
}

Also for the Parking interface we had created, add a new method named unpark as follows -

package com.javastructures.service;

import com.javastructures.exception.InvalidVehicleNumberException;
import com.javastructures.exception.ParkingFullException;
import com.javastructures.model.Ticket;
import com.javastructures.model.Vehicle;
import com.javastructures.strategy.ParkingChargeStrategy;

public interface Parking {

public Ticket park(Vehicle vehicle) throws ParkingFullException;

public int unPark(Ticket ticket, ParkingChargeStrategy parkingCostStrategy) throws InvalidVehicleNumberException;

}

Finally implement the logic to unpark the vehicle.

package com.javastructures.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.javastructures.exception.InvalidVehicleNumberException;
import com.javastructures.exception.ParkingFullException;
import com.javastructures.model.Slot;
import com.javastructures.model.Ticket;
import com.javastructures.model.Vehicle;
import com.javastructures.model.VehicleSize;
import com.javastructures.strategy.ParkingChargeStrategy;

public class ParkingLot implements Parking {

private static ParkingLot parkingLot;

private final List<Slot> twoWheelerSlots;
private final List<Slot> fourWheelerSlots;

private ParkingLot() {
this.twoWheelerSlots = new ArrayList<>();
this.fourWheelerSlots = new ArrayList<>();
}

public static ParkingLot getParkingLot() {
if (parkingLot == null)
parkingLot = new ParkingLot();
return parkingLot;
}

public boolean initializeParkingSlots(int numberOfTwoWheelerParkingSlots, int numberOfFourWheelerParkingSlots) {

for (int i = 1; i <= numberOfTwoWheelerParkingSlots; i++) {
twoWheelerSlots.add(new Slot(i));
}

System.out.printf("Created a two wheeler parking lot with %s slots %n", numberOfTwoWheelerParkingSlots);

for (int i = 1; i <= numberOfFourWheelerParkingSlots; i++) {
fourWheelerSlots.add(new Slot(i));
}

System.out.printf("Created a four wheeler parking lot with %s slots %n", numberOfFourWheelerParkingSlots);
return true;
}

public Ticket park(Vehicle vehicle) throws ParkingFullException {
Slot nextAvailableSlot;
if (vehicle.getVehicleSize().equals(VehicleSize.FOURWHEELER)) {
nextAvailableSlot = getNextAvailableFourWheelerSlot();
} else {
nextAvailableSlot = getNextAvailableTwoWheelerSlot();
}
nextAvailableSlot.occupySlot(vehicle);
System.out.printf("Allocated slot number: %d \n", nextAvailableSlot.getSlotNumber());
Ticket ticket = new Ticket(nextAvailableSlot.getSlotNumber(), vehicle.getVehicleNumber(),
vehicle.getVehicleSize(), new Date());
return ticket;
}

private Slot getNextAvailableFourWheelerSlot() throws ParkingFullException {
for (Slot slot : fourWheelerSlots) {
if (slot.isEmpty()) {
return slot;
}
}
throw new ParkingFullException("No Empty Slot available");
}

private Slot getNextAvailableTwoWheelerSlot() throws ParkingFullException {
for (Slot slot : twoWheelerSlots) {
if (slot.isEmpty()) {
return slot;
}
}
throw new ParkingFullException("No Empty Slot available");
}

public int unPark(Ticket ticket, ParkingChargeStrategy parkingCostStrategy) throws InvalidVehicleNumberException {
int costByHours = 0;
Slot slot;
try {
if (ticket.getVehicleSize().equals(VehicleSize.FOURWHEELER)) {
slot = getFourWheelerSlotByVehicleNumber(ticket.getVehicleNumber());
} else {
slot = getTwoWheelerSlotByVehicleNumber(ticket.getVehicleNumber());
}
slot.vacateSlot();
int hours = getHoursParked(ticket.getDate(), new Date());
costByHours = parkingCostStrategy.getCharge(hours);
System.out.println(
"Vehicle with registration " + ticket.getVehicleNumber() + " at slot number " + slot.getSlotNumber()
+ " was parked for " + hours + " hours and the total charge is " + costByHours);
} catch (InvalidVehicleNumberException invalidVehicleNumber) {
System.out.println(invalidVehicleNumber.getMessage());
throw invalidVehicleNumber;
}
return costByHours;
}

private int getHoursParked(Date startDate, Date endDate) {
long secs = (endDate.getTime() - startDate.getTime()) / 1000;
int hours = (int) (secs / 3600);
return hours;

}

private Slot getFourWheelerSlotByVehicleNumber(String vehicleNumber) throws InvalidVehicleNumberException {
for (Slot slot : fourWheelerSlots) {
Vehicle vehicle = slot.getParkVehicle();
if (vehicle != null && vehicle.getVehicleNumber().equals(vehicleNumber)) {
return slot;
}
}
throw new InvalidVehicleNumberException("Two wheeler with registration number " + vehicleNumber + " not found");
}

private Slot getTwoWheelerSlotByVehicleNumber(String vehicleNumber) throws InvalidVehicleNumberException {
for (Slot slot : twoWheelerSlots) {
Vehicle vehicle = slot.getParkVehicle();
if (vehicle != null && vehicle.getVehicleNumber().equals(vehicleNumber)) {
return slot;
}
}
throw new InvalidVehicleNumberException("Two wheeler with registration number " + vehicleNumber + " not found");
}


}

Finally we will test the unpark logic

package com.javastructures;

import com.javastructures.exception.ParkingFullException;
import com.javastructures.model.Ticket;
import com.javastructures.model.Vehicle;
import com.javastructures.model.VehicleSize;
import com.javastructures.service.ParkingLot;

public class TestParking {

public static void main(String[] args) throws ParkingFullException {
ParkingLot parkingLot = ParkingLot.getParkingLot();

parkingLot.initializeParkingSlots(10, 10);

Vehicle vehicle = new Vehicle("Mh12", VehicleSize.TWOWHEELER);

Ticket ticket = parkingLot.park(vehicle);
System.out.println(ticket);

Vehicle vehicle2 = new Vehicle("Mh13", VehicleSize.FOURWHEELER);

Ticket ticket2 = parkingLot.park(vehicle2);
System.out.println(ticket2);

int cost1 = parkingLot.unPark(ticket2, new TwoWheelerWeekDayChargeStrategy());
System.out.println(cost1);

int cost2 = parkingLot.unPark(ticket2, new FourWheelerWeekDayChargeStrategy());
System.out.println(cost2);


}

}

--

--