Requirements
parking spot assignment based on vehicle size and spot availability
spot availability checking:
- checks parking spot is available based on current spot count
fee calculation based on parking duration
Support multiple floors
- each floor will have its own free spots and occupied spots
- Floor-specific features might include reserved spots for electric vehicles, handicapped spaces, and charging stations.
user roles and permissions:
- system will have admins and users
- admins can change parking lot settings, like price
- users can check availability, reserve spots, etc..
Define Core Objects
ParkingSpot
- String: spotId
- String size
- Vehicle vehicle
- boolean isOccupied
- Methods
- parkVehicle(Vehicle vehicle)
- removeVehicle()
ParkingFloor
- String floorId
- List <ParkingSpot> parkingSpots
- int totalSpots
- int availableSpots
- methods
- findAvailableSpot(Vehicle v): ParkingSpot
ParkingLot
- List<ParkingFloor> parkingFloors
- int totalSpots
- int availableSpots
- methods
- assignSpot(Veichle v)
- removeFromSpot()
Vehicle
- String licenseNumber
- String size
Ticket
- String ticketId
- Vehicle vehicle
- ParkingSpot parkingSpot
- Date startTime
- Date endTime
- float fee
- method
- CalculateFee(): double
user
- String userId
- String name
- String role
Analyze Relationships
Determine how these objects will interact with each other to fulfill the use cases...
- ParkingLot and ParkingFloor:
- The
ParkingLotcontains multipleParkingFloorobjects. It manages the overall parking facility and delegates the task of finding an available spot to the appropriate floor.
- The
- ParkingFloor and ParkingSpot:
- Each
ParkingFloorcontains multipleParkingSpotobjects. The floor finds and assigns an available spot to a vehicle based on its size.
- Each
- ParkingSpot and Vehicle:
- A
ParkingSpotis either occupied by aVehicleor is vacant. When occupied, the spot stores a reference to the vehicle.
- A
- Vehicle and Ticket:
- When a vehicle is parked, a
Ticketis generated, recording the entry time, the assigned spot, and the vehicle. The ticket is used to calculate the parking fee.
- When a vehicle is parked, a
- Ticket and Payment:
- A
Paymentis made for aTicket. The payment records the amount paid and marks the ticket as settled.
- A
- User and ParkingLot System:
Usersinteract with the system based on their roles (Admin, Attendant, Regular User), performing actions like spot reservation, vehicle check-in/out, and viewing parking history.
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...
Class Hierarchy:
- ParkingSpot:
- We'll create a base class called
ParkingSpotthat will contain common attributes likespotId,size, andisOccupied. - We can then create subclasses for different types of parking spots if needed, such as
CompactSpot,LargeSpot, andHandicappedSpot, each inheriting fromParkingSpot.
- We'll create a base class called
- Vehicle:
- A base class
Vehiclewill represent general attributes likelicensePlateandsize. - Subclasses like
Car,Bike, andTruckcan inherit fromVehicleto handle vehicle-specific details.
- A base class
- User:
- The
Userclass will serve as a base class with common attributes likeuserId,name, androle. - Subclasses such as
Admin,Attendant, andRegularUsercan inherit fromUserto implement role-specific behaviors.
- The
- Ticket and Payment:
- These classes do not require a hierarchical structure, as they are specific to their tasks. However, they could implement common interfaces if the system were to grow, supporting different types of payments or ticket systems.
Design Patterns
Consider using design patterns (e.g., Factory, Singleton, Observer, Strategy) that fit the problem...
Factory Design pattern for Vehicle - build which type of vehicle object based on the vehicle type
- simplifies building logic, can build different objects based on custom logic
public class VehicleFactory(String type, String licenseNumber){
switch(type.toLowerCase()){
case "car":
return new Car(licenseNumber, "medium");
case "bike":
return new Bike(licenseNumber, "small");
case "truck":
return new Truck(licenseNumber, "large");
default:
throw new IllegalArgumentException("unknown type");
}
}
The ParkingLot can be a singleton, ensuring that there is only one instance managing the entire parking facility.
public class ParkingLot {
private static ParkingLot instance;
private ParkingLot(){
}
public static synchronized ParkingLot getInstance(){
if (instance == null){
instance = new ParkingLot();
}
return instance;
}
}
Strategy Pattern
This can be applied for calculating parking fees. Different strategies can be implemented based on vehicle type, parking duration, or special discounts.
public interface FeeStrategy {
double calculateFee(Ticket ticket);
}
// Polymorphism
public class RegularStrategy implements FeeStrategy {
public double calculateFee (Ticket ticket) {
// basic fee calculation
}
}
public class PremiumStrategy implements FeeStrategy {
public double calculateFee (Ticket ticket) {
// premium fee calculation
}
}
public class Ticket {
private FeeStrategy feeStrategy;
public void setFeeStrategy(FeeStrategy feeStrategy) {
this. feeStrategy = feeStrategy;
}
public double calculateFee(){
return feeStrategy.calculateFee(this);
}
}
Observer Pattern
This can be used to notify users (Admin, Attendant) when certain events occur, such as a parking spot becoming available or a payment being processed.
public interface ParkingObserver {
void update (String eventType, ParkingSpot parkingSpot);
}
public class User implements ParkingObserver {
public void update (String eventType, ParkingSpot parkingSpot){
if (role== "admin") {
// Handle event based on eventType for Admin
} else {
// Handle event based on eventType for customers
}
}
}
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 class VehicleFactory {
Public static Vehicle createVehicle(String type, String licenseNumber) {
switch(type.toLowerCase()){
case "car":
return new Car(licenseNumber, "medium");
case "bike":
return new Bike(licenseNumber, "small");
case "truck":
return new Truck(licenseNumber, "large");
default:
throw new IllegalArgumentException("unknown type");
}
}
}
public abstract class Vehicle {
protected String licenseNumber;
protected String size;
public Vehicle(String licenseNumber, String size){
this.licenseNumber = licenseNumber;
this.size = size;
}
public String getLicenseNumber (){
return licensePlate;
}
public String getSize() {
return size;
}
}
public class Car extends Vehicle {
public Car (String licenseNumber){
super(licenseNumber, "medium");
}
}
public class Bike extends Vehicle {
public Bike (String licenseNumber){
super(licenseNumber, "small");
}
}
public class Truck extends Vehicle {
public Truck (String licenseNumber){
super(licenseNumber, "large");
}
}
public class ParkingSpot {
private String spotId;
private String size;
private Vehicle vehicle;
private boolean isOccupied;
public ParkingSpot (spotId, String size) {
this.spotId = spotId;
this.size = size;
this.isOccupied = false;
this.vehicle = null;
}
public void parkVehicle (Vehicle vehicle) {
this.vehicle = vehicle;
this.isOccupied = true;
}
public void removeVehicle() {
this.vehicle = null;
this.isOccupied = false;
}
public boolean isOccupied() {
return this.isOccupied;
}
public String getSize() {
return size;
}
}
public Class ParkingFloor {
private String floorId;
private List <ParkingSpot> parkingSpots;
private int totalSpots;
private int availableSpots;
public ParkingFloor (String floorId, List<ParkingSpot> spots) {
this.floorId = floorId;
this.parkingSpots= spots;
this.totalSpots = parkingSpots.size();
this.availableSpots = calculateAvailableSpots();
}
private int calculateAvailableSpots() {
int count = 0;
for (ParkingSpot parkingSpot : parkingSpots) {
if (!parkingSpot.isOccupied()) count ++;
}
return count;
}
public ParkingSpot findAvailableSpot(Vehicle v) {
for (ParkingSpot ps : parkingSpots) {
if (!ps.isOccupied() && spot.getSize() == v.getSize()){
spot.parkVehicle(v);
availableSpots --;
return ps;
}
}
return null; // No spot available
}
}
public class ParkingLot {
private static ParkingLot instance;
private List<ParkingFloor> parkingFloors;
private int totalSpots;
private int availableSpots;
private ParkingLot(){
this.parkingFloors= new ArrayList<>();
}
// Singleton Design Pattern
public static synchronized ParkingLot getInstance (){
if (instance == null) {
instance = new ParkingLot();
}
return instance;
}
public void addFloor(ParkingFloor floor) {
parkingFloors.add(floor);
totalSpots += floor.getTotalSpots();
avialableSpots += floor.getAvailableSpots();
}
public ParkingSpot findAvailableSpot(Vehicle vehicle) {
for (ParkingFloor floor : floors) {
ParkingSpot spot = floor.findAvailableSpot(vehicle);
if (spot != null) return spot;
}
return null; // No spot available
}
public void releaseSpot(ParkingSpot spot) {
spot.removeVehicle();
availableSpots++;
}
}
public class Ticket {
private String ticketId;
private Vehicle vehicle;
private ParkingSpot spot;
private Date entryTime;
private Date exitTime;
private double fee;
public Ticket(String ticketId, Vehicle vehicle, ParkingSpot spot) {
this.ticketId = ticketId;
this.vehicle = vehicle;
this.spot = spot;
this.entryTime = new Date(); // Set entry time to current time
}
public void markExit() {
this.exitTime = new Date(); // Set exit time to current time
calculateFee();
}
private void calculateFee() {
// Fee calculation logic, possibly using a strategy pattern
long duration = exitTime.getTime() - entryTime.getTime(); // Duration in milliseconds
this.fee = duration * 0.05; // Example fee calculation
}
public double getFee() {
return fee;
}
// Other ticket-related methods
}
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...