Requirements
Determine the different ways the system will be used. This includes main functions the system needs to perform and who will use it.
Main Functions:
- Assign a parking spot based on vehicle type and availability
- Manage multiple floors
- calculate fee based on duration of parking and vehicle type
- check for available parking spots
Define Core Objects
Based on the requirements and use cases, identify the main objects of the system...
- Vehicle:
- properties:
- string licensePlate
- string type
- properties:
- ParkingSpot:
- properties:
- int spotNumber
- string vehicleType
- Vehicle vehicle
- boolean isOccupied
- methods:
- boolean isAvailable()
- boolean park(Vehicle vehicle)
- void unpark()
- string getVehicleType()
- properties:
- ParkingFloor:
- properties:
- int floorNumber
- List<ParkingSpots> parkingSpots
- methods:
- ParkingSpot getAvailableParkingSpot(string type)
- int getFloorNumber()
- List<ParkingSpot> getParkingSpots()
- properties:
- Ticket:
- properties:
- string ticketId
- Vehicle vehicle
- ParkingSpot spot
- long entryTimeStamp
- long exitTimeStsmp
- methods:
- string getTicketId()
- Vehicle getVehicle()
- ParkingSpot getSpot()
- long getEntryTImeStamp()
- long getExitTimeStamp()
- void setExitTimeStamp()
- properties:
- FeeStrategy:
- properties:
- Map<String,Double> hourlyRates
- methods:
- double calculateFee(Ticket ticket)
- properties:
- ParkingLot:
- properties:
- List<ParkingFloor> floors
- Map<String,Ticket> activeTickets
- FeeStrategy feeStrategy
- methods:
- void addFloor(ParkingFloor floor)
- Ticket parkVehicle(Vehicle vehicle)
- double unParkVehicle(String tickeId)
- List<ParkingFloor> getParkingFloors()
- properties:
Analyze Relationships
Determine how these objects will interact with each other to fulfill the use cases...
Establish Hierarchy
Design inheritance trees where applicable to promote code reuse and polymorphism. This step involves identifying common attributes and behaviors that can be abstracted into parent classes...
Design Patterns
Consider using design patterns (e.g., Factory, Singleton, Observer, Strategy) that fit the problem...
ParkingLot can be singleton. For further enhancements, we can use strategy pattern for the fee calculation. We can use Factory Pattern to extend vehicle into car, bike and truck.
Define Class Members (write code)
Attributes: For each class, define the attributes (data) it will hold...
Methods: Define the methods (functions) that operate on the attributes. Ensure they align with the object's responsibilities and adhere to the principle of encapsulation.
public enum VehicleType {
CAR,
BIKE,
TRUCK
}
public class Vehicle {
protected String licensePlate;
protected VehicleType type;
}
public class Car extends Vehicle {
public Car(String license){
super(license, VehicleType.CAR);
}
}
public class Bike extends Vehicle {
public Bike(String license){
super(license, VehicleType.BIKE);
}
}
public class Truck extends Vehicle {
public Truck(String license){
super(license, VehicleType.TRUCK);
}
}
public class ParkingSpot {
private final int spotNumber;
private final VehicleType vehicleType;
private Vehicle vehicle;
private boolean isOccupied;
public boolean isAvailable(){ return !isOccupied;}
public synchronized boolean park(Vehicle vehicle){
if(isOccupied || vehicle.getType() != vehicleType) {
return false;
}
this.vehicle = vehicle;
isOccupied = true;
return true;
}
public synchronized void unpark(){
this.vehicle = null;
isOccupied = false;
}
}
public class ParkingFloor {
private final int floorNumber;
private final List<ParkingSpot> parkingSpots;
public synchronized Optional<ParkingSpot> getAvailableSpot(VehicleType type) {
return parkingSpots.stream().filter(spot => spot.isAvailable() && spot.getVehicleType() == type).findFirst();
}
}
public interface FeeStrategy {
public double calculateFee(Ticket ticket);
}
public class VehicleBasedFeeStrategy implements FeeStrategy {
private final Map<VehicleType,Double> hourlyRates = Map.of( VehicleType.CAR,20.0,VehicleType.BIKE,10.0,VehicleType.TRUCK,30.0);
@Override
public double calculateFee(Ticket ticket){
long duration = ticket.getExitTimeStamp() - ticket.getEntryTimeStamp();
long hours = (duration/(10006060))+1;
return hours*hourlyRates.get(ticket.getVehicle().getType());
}
}
public class ParkingLot {
private final List<ParkingFloor> floors;
private final Map<String,Ticket> activeTickets = new ConcurrentHashMap<>();
private FeeStrategy feeStrategy;
public synchronized void addFloor(ParkingFloor floor){
floors.add(floor);
}
public void setFeeStrategy(FeeStrategy strategy){
this.feeStrategy = strategy;
}
public synchronized Ticket parkVehicle(Vehicle vehicle) throws Exception {
for(ParkingFloor floor: floors){
Optional<ParkingSpot> spotOpt = floor.getAvailableSpot(vehicle.getType());
if(spotOpt.isPresent()){
ParkingSpot spot = spotOpt.get();
if(spot.park(vehicle)){
String ticketId = UUID.randomUUID().toString();
Ticket ticket = new Ticket(ticketId,vehicle,spot);
activeTickets.put(ticketId,ticket);
return ticket;
}
}
}
throw new Exception("No available spot for:"+ vehicle.getType());
}
public synchronized double unparkVehicle(String ticketId) throws Exception {
Ticket ticket = activeTickets.remove(tickedId);
if(ticket == null) throw new Exception("Invalid ticket");
ParkingSpot spot = ticket.getSpot();
spot.unpark();
ticket.setExitTimeStamp();
return feeStrategy.calculateFee(ticket);
}
}
Adhere to SOLID Guidelines
Check and explain whether your design adheres to solid principles (Ask interviewer what SOLID principle is if you can not recall it.)...
Consider Scalability and Flexibility
Explain how your design can handle changes in scale and whether it would be easily to extend with new functionalities...
Create/Explain your diagram(s)
Try creating a class, flow, state and/or sequence diagram using the diagramming tool. Mermaid flow diagrams can be used to represent system use cases. You can ask the interviewer bot to create a starter diagram if unfamiliar with the tool. Briefly explain your diagrams if necessary...
Future improvements
Critically examine your design for any flaws or areas for future improvement...