My Solution for Design a Parking Lot with Score: 8/10
by yield_radiant616
Requirements
Determine the different ways the system will be used. This includes main functions the system needs to perform and who will use it.
System users:
1- Parking owner
2- Client
1- Parking owner functionalities:
- Add new spot to the parking with the main information like spot size and floor Number and the fees pair hour
- Remove or update spot information
- get total fees for all transactions
2- Client
- Check spot availability and check the spot size if it can fit with the car size
- Reserve a new spot
- Release the reservation and see the total fees according to the parking time
class Vehicle{
private final int ID;
private float size;
private string model;
private string ownerName;
public Vehicle( float size, String model, String ownerName);
public getters();
public setters();
}
interface ISpot{
boolean checkSpotSize(float carSize);
public getters();
public setters();
}
public enum VehicleType {
Car,
Bike
}
class Spot implements ISpot{
protected float feesPerHour;
protected VehicleType type;
protected int ID ;
protected float size;
protected int floorNumber;
public Spot(float size, float feesPerHour, int floorNumber);
public boolean checkSpotSize(float carSize)
public getters();
public setters();
}
public class CarSpot extends Spot{
public CarSpot(int ID, float size, int floorNumber);
}
public class BikeSpot extends Spot{
public BikeSpot(int ID, float size, int floorNumber);
}
public class SpotFactory {
private static final AtomicInteger counter;
public static ISpot getInstance(VehicleType type, float size, int floorNumber);
}
public class SpotService{
private static SpotService spotService;
private final Map<Integer, ISpot> spots = new HashMap<>();
public static synchronized SpotService getInstance();
public void addNewSpot(ISpot spot);
public void removeSpot(int spotId);
public Collection<ISpot> getAllSpots();
}
class Booking{
private final int ID;
private final Vehicle car;
private final ISpot spot;
private final LocalTime startTime;
private LocalTime endTime;
private float totalFees;
public Booking(Vehicle car, ISpot spot, LocalTime endTime);
public Booking(Vehicle car, ISpot spot, LocalTime startTime, LocalTime endTime)
public getters();
public setters();
}
public class BookingService {
private static BookingService bookingService;
private final Map<Integer, Booking> bookings = new HashMap<>();
public static synchronized BookingService getInstance();
public boolean checkBookingAvailability(float size, ISpot spot, LocalTime startTime, LocalTime endTime)
public void addNewBooking(Booking booking)
public Booking getBooking(int bookingId)
public void releaseReservation(Booking bookingModel)
}
class Parking {
private static Parking parking;
private static float totalProfit = 0;
public static Parking getInstance()
public void addNewInstantlyBooking(Spot spot, Car car, LocalTime endTime);
public void addNewFutureBooking(Spot spot, Car car, LocalTime startTime, LocalTime endTime);
public void releaseBooking(int bookingId);
public static void getTotalOwnerProfit();
public Collection<Spot> getAllSpots();
}
Analyze Relationships
Determine how these objects will interact with each other to fulfill the use cases...
- Association relationship between Spot service and spot
- Association relationship between Booking spot service and BookingSpot
- Aggregation relationship between Booking-Spot and Car
- Aggregation relationship between Booking-Spot and Spot
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...
ISpot interface class that have all common spot services methods definition
Spot abstract class the implement ISpot interface and write the body for all common spots method
CarSpot class extend from Spot parent class
BikeSpot class extend from Spot parent class
The tree consist of:
ISpot -> Spot -> CarSpot
ISpot -> Spot -> BikeSpot
Design Patterns
1- Singleton design pattern in a Parking class and booking service and Spot service
2- Strategy design pattern in Spot logic using ISpot interface that include common spot methods
3- Factory design pattern to make a unique ID for each new spot object
Define Class Members
public class Vehicle{
private final int ID;
private float size;
private String model;
private String ownerName;
public Vehicle(float size, String model, String ownerName){
this.ID = new Random().nextInt(100000);
this.size = size;
this.model = model;
this.ownerName = ownerName;
}
public int getID(){
return this.ID;
}
public float getSize(){return this.size;}
public String getModel(){
return this.model;
}
public String getOwnerName() {
return ownerName;
}
public void setModel(String model) {
this.model = model;
}
public void setOwnerName(String ownerName) {
this.ownerName = ownerName;
}
public void setSize(float size) {
this.size = size;
}
}
public interface ISpot {
boolean checkSpotSize(float carSize);
public int getID();
public float getFeesPerHour();
public int getFloorNumber();
public float getSize();
public void setSize(float size);
public void setFeesPerHour(float feesPerHour);
public void setFloorNumber(int floorNumber);
public VehicleType getType();
}
public abstract class Spot implements ISpot {
protected float feesPerHour;
protected VehicleType type;
protected int ID ;
protected float size;
protected int floorNumber;
public boolean checkSpotSize(float carSize){
return (this.getSize() >= carSize);
}
public int getID() {return ID;}
public float getFeesPerHour() {return feesPerHour;}
public int getFloorNumber() {
return floorNumber;
}
public float getSize() {return size;}
public VehicleType getType(){return type;}
public void setSize(float size) {this.size = size;}
public void setFeesPerHour(float fees) {feesPerHour = fees;}
public void setFloorNumber(int floorNumber) {
this.floorNumber = floorNumber;
}
}
public class BikeSpot extends Spot{
public BikeSpot(int ID, float size, int floorNumber){
this.ID = ID;
this.floorNumber = floorNumber;
this.size = size;
this.type = VehicleType.Bike;
this.feesPerHour = 50;
}
}
public class CarSpot extends Spot{
public CarSpot(int ID, float size, int floorNumber){
this.ID = ID;
this.floorNumber = floorNumber;
this.size = size;
this.type = VehicleType.Car;
this.feesPerHour = 100;
}
}
public class SpotFactory {
private static final AtomicInteger counter = new AtomicInteger();
public static ISpot getInstance(VehicleType type, float size, int floorNumber){
ISpot spot;
int id = counter.incrementAndGet();
if(type == VehicleType.Car)
spot = new CarSpot(id, size, floorNumber);
else if (type == VehicleType.Bike) {
spot = new BikeSpot(id, size, floorNumber);
}else{
throw new IllegalArgumentException("The available spots type for now are Car and Bike");
}
return spot;
}
}
public class SpotService {
private static SpotService spotService;
private final Map<Integer, ISpot> spots = new HashMap<>();
public static synchronized SpotService getInstance(){
if(spotService == null)
spotService = new SpotService();
return spotService;
}
public void addNewSpot(ISpot spot){
spots.put(spot.getID(), spot);
}
public void removeSpot(int spotId){
spots.remove(spotId);
}
public Collection<ISpot> getAllSpots() {
return spots.values();
}
}
public enum VehicleType {
Car,
Bike
}
public class Booking {
private final int ID;
private final Vehicle car;
private final ISpot spot;
private final LocalTime startTime;
private LocalTime endTime;
private float totalFees;
public Booking(Vehicle car, ISpot spot, LocalTime endTime){
this.ID = new Random().nextInt(100000);
this.car = car;
this.spot = spot;
this.startTime = LocalTime.now();
this.endTime = endTime;
}
public Booking(Vehicle car, ISpot spot, LocalTime startTime, LocalTime endTime){
this.ID = new Random().nextInt(100000);
this.car = car;
this.spot = spot;
this.startTime = startTime;
this.endTime = endTime;
}
public float getTotalFees(){
return this.totalFees;
}
public void setTotalFees(float totalFees) {this.totalFees = totalFees;}
public Vehicle getCar() {
return car;
}
public ISpot getSpot() {
return spot;
}
public LocalTime getStartTime() {
return startTime;
}
public LocalTime getEndTime() {
return endTime;
}
public int getID() {
return ID;
}
}
public class BookingService {
private static BookingService bookingService;
private final Map<Integer, Booking> bookings = new HashMap<>();
public static synchronized BookingService getInstance() {
if(bookingService == null)
bookingService = new BookingService();
return bookingService;
}
public boolean checkBookingAvailability(float size, ISpot spot, LocalTime startTime, LocalTime endTime){
if(!spot.checkSpotSize(size)){
throw new IllegalArgumentException("The car size is bigger than spot size");
}
bookings.forEach((key, value) -> {
ISpot curSpot = value.getSpot();
if(curSpot.getID() == spot.getID() && !(endTime.isBefore(value.getStartTime()) || startTime.isAfter(value.getEndTime()))){
throw new UnsupportedOperationException("This spot is reserved during this time");
}
});
return true;
}
public void addNewBooking(Booking booking){
bookings.put(booking.getID(), booking);
}
public Booking getBooking(int bookingId){
return bookings.get(bookingId);
}
public void releaseReservation(Booking bookingModel){
Duration duration = Duration.between(bookingModel.getStartTime(), bookingModel.getEndTime());
float hours = (float) duration.toMinutes() / 60;
bookingModel.setTotalFees(hours * bookingModel.getSpot().getFeesPerHour());
}
}
public class Parking {
private static Parking parking;
private static float totalProfit = 0;
public static synchronized Parking getInstance(){
if(parking == null){
parking = new Parking();
}
return parking;
}
public void addNewInstantlyBooking(ISpot spot, Vehicle car, LocalTime endTime){
LocalTime startTime = LocalTime.now();
try{
if(BookingService.getInstance().checkBookingAvailability(car.getSize(), spot, startTime, endTime)){
Booking bookingSpot = new Booking(car, spot, endTime);
BookingService.getInstance().addNewBooking(bookingSpot);
}
}catch (UnsupportedOperationException | IllegalArgumentException error){
System.out.println(error.getMessage());
}
}
public void addNewFutureBooking(ISpot spot, Vehicle car, LocalTime startTime, LocalTime endTime){
try{
if(BookingService.getInstance().checkBookingAvailability(car.getSize(), spot, startTime, endTime)){
Booking bookingSpot = new Booking(car, spot, startTime, endTime);
BookingService.getInstance().addNewBooking(bookingSpot);
}
}catch (UnsupportedOperationException | IllegalArgumentException error){
System.out.println(error.getMessage());
}
}
public void releaseBooking(int bookingId){
Booking bookingSpot = BookingService.getInstance().getBooking(bookingId);
BookingService.getInstance().releaseReservation(bookingSpot);
totalProfit += bookingSpot.getTotalFees();
System.out.println("Total fees is: " + bookingSpot.getTotalFees());
}
public static void getTotalOwnerProfit(){
System.out.println("Total profits is: " + totalProfit);
}
public Collection<ISpot> getAllSpots() {
return SpotService.getInstance().getAllSpots();
}
}
Adhere to SOLID Guidelines
1- Each class responsible for single task like a Spot service is responsible for managing all spots and Booking service manage all bookings and calculate the booking fees and so on
2- the system is open for extension like adding new Spot type and this thanks to Strategy design pattern but close to modified
3- Valid with Liskov-substitution principle because of each spot type can behave like the spot parent class
4- Valid with Interface-segregation principle because any new spot will use the ISpot interface common method and will not be a new spot the is forced to use not required function
Consider Scalability and Flexibility
The design is ready for any kind of scale like adding a new spot type all what you need is implementing the ISpot interface then you won't need to refactor anything like Spot service
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
In future improvements, we can add a vehicle types like car and bike and extend from vehicle parent class and make this parent as an abstract class