Requirements
Functional:
1. Assign nearest spot across the floors from the entrance
2. Calculate and collect fee at the checkout based on hours.
Non-functional:
1. New vehicle sizes could be added without affecting the rest of the system
2. New floors could be added without affecting the rest of the system
3. New spots could be added without affecting the rest of the system
4. New payment methods could be added without affecting the rest of the system
5. Fee calculation strategy could be changed without affecting the rest of the system
Define Core Objects
VehicleSize (enum)
Spot (abstract class)
CompactSpot
LargeSpot
Lot
AssignmentManager
PQManager
Booking
AssignmentStrategy (interface)
PQAssignmentStrategy
Floor
FareCalculatorStrategy (interface)
CompactFareCalculatorStrategy
LargeFareCalculatorStrategy
FareCalculatorManager
PaymentType (enum)
PaymentProcessor (interface)
CardPaymentProcessor
PaypalPaymentProcessor
PaymentProcessorFactory
Analyze Relationships
Spot has Floor
CompactSpot is-a Spot
LargeSpot is-a Spot
PQAssignmentStrategy is-a AssignmentStrategy
AssignmentManager has AssignmentStrategy
Booking has Vehicle
Booking has Spot
CompactFareCalculatorStrategy is-a FareCalculatorStrategy
LargeFareCalculatorStrategy is-a FareCalculatorStrategy
CardPaymentProcessor is-a PaymentProcessor
PaypalPaymentProcessor is-a PaymentProcessor
Establish Hierarchy
Spots of multiple sizes are inherited from Spot
PQAssignmentStrategy implements AssignmentStrategy
fareCalculatorStrategies of multiple sizes implement fareCalculatorStrategy
PaymentProcessors of different payment type implement PaymentProcessor
Design Patterns
PQAssignmentStrategy implements AssignmentStrategy
AssignmentManager holds a singleton of AssignmentStrategy
PQManager holds singletons of PriorityQueues of different sizes in a map
fareCalculatorStrategies of multiple sizes implement fareCalculatorStrategy
FareCalculatorManager holds singletons of fareCalculatorStrategies of different size in a map
PaymentProcessors of different payment type implement PaymentProcessor
PaymentProcessorFactory creates paymentProcessor of a size
Define Class Members (write code)
public enum VehicleSize {
Compact, Large
}
public abstract class Spot {
private String id = new String(""); // a random string
private int timeFromGround;
private Floor floor;
Spot(Floor floor, int timeFromElevator) {
this.floor = floor;
this.timeFromGround = floor.getTimeFromGround() + timeFromElevator;
}
public String getId() {
return id;
}
public int getTimeFromGround() {
return timeFromGround;
}
public Floor getFloor() {
return floor;
}
public abstract VehicleSize getVehicleSize();
}
public class CompactSpot extends Spot {
private final VehicleSize vehicleSize = VehicleSize.Compact;
CompactSpot(Floor floor, int timeFromElevator) {
super(floor, timeFromElevator);
}
@Override
public VehicleSize getVehicleSize() {
return vehicleSize;
}
}
public class CompactSpot extends Spot {
private final VehicleSize vehicleSize = VehicleSize.Compact;
CompactSpot(Floor floor, int timeFromElevator) {
super(floor, timeFromElevator);
}
@Override
public VehicleSize getVehicleSize() {
return vehicleSize;
}
}
public class Lot {
public Booking assignSpot(String vehicleId, VehicleSize vehicleSize) {
AssignmentStrategy assignmentStrategy = AssignmentManager.getAssignmentStrategy();
return assignmentStrategy.assignSpot(vehicleId, vehicleSize);
}
public boolean checkout(Booking booking, PaymentType paymentType) {
FareCalculatorStrategy fareCalculatorStrategy = FareCalculatorManager
.getFareCalculatorStrategy(booking.getSpot().getVehicleSize());
long fare = fareCalculatorStrategy.calculateFare(System.currentTimeMillis() - booking.getAllottedAt());
PaymentProcessor paymentProcessor = PaymentProcessorFactory.getPaymentProcessor(paymentType);
return paymentProcessor.makePayment(fare);
}
}
public class AssignmentManager {
private static final AssignmentStrategy assignmentStrategy = new PQAssignmentStrategy();
public static AssignmentStrategy getAssignmentStrategy() {
return assignmentStrategy;
}
}
public class PQManager {
private static Map<VehicleSize, PriorityBlockingQueue<Spot>> pqMap = new HashMap<>();
static {
pqMap.put(VehicleSize.Compact, new PriorityBlockingQueue<Spot>(10,
(a, b) -> Integer.compare(a.getTimeFromGround(), b.getTimeFromGround())));
pqMap.put(VehicleSize.Large, new PriorityBlockingQueue<>(10,
(a, b) -> Integer.compare(a.getTimeFromGround(), b.getTimeFromGround())));
}
public static PriorityBlockingQueue<Spot> getPriorityBlockingQueue(VehicleSize vehicleSize) {
return pqMap.get(vehicleSize);
}
}
public class Booking {
private final String id = new String(""); // a random string
private final String vehicleId;
private final long allottedAt = System.currentTimeMillis();
private final Spot spot;
public Booking(String vehicleId, Spot spot) {
this.vehicleId = vehicleId;
this.spot = spot;
}
public String getId() {
return this.id;
}
public String getVehicleId() {
return this.vehicleId;
}
public long getAllottedAt() {
return allottedAt;
}
public Spot getSpot() {
return spot;
}
}
public interface AssignmentStrategy {
public Booking assignSpot(String vehicleId, VehicleSize vehicleSize);
}
public class PQAssignmentStrategy implements AssignmentStrategy {
@Override
public Booking assignSpot(String vehicleId, VehicleSize vehicleSize) {
PriorityBlockingQueue<Spot> pq = PQManager.getPriorityBlockingQueue(vehicleSize);
Spot spot = pq.poll();
return new Booking(vehicleId, spot);
}
}
public class Floor {
private String id = new String(""); // a random string
private int timeFromGround;
Floor(int timeFromGround) {
this.timeFromGround = timeFromGround;
}
public String getId() {
return id;
}
public int getTimeFromGround() {
return timeFromGround;
}
}
public interface FareCalculatorStrategy {
public long calculateFare(long minutes);
}
class CompactFareCalculatorStrategy implements FareCalculatorStrategy {
public long calculateFare(long minutes) {
return 100 * minutes;
}
}
class LargeFareCalculatorStrategy implements FareCalculatorStrategy {
public long calculateFare(long minutes) {
return 200 * minutes;
}
}
public class FareCalculatorManager {
private static Map<VehicleSize, FareCalculatorStrategy> fareCalculatorMap = new HashMap<>();
static {
fareCalculatorMap.put(VehicleSize.Compact, new CompactFareCalculatorStrategy());
fareCalculatorMap.put(VehicleSize.Large, new LargeFareCalculatorStrategy());
}
public static FareCalculatorStrategy getFareCalculatorStrategy(VehicleSize vehicleSize) {
return fareCalculatorMap.get(vehicleSize);
}
}
public enum PaymentType {
CARD, PAYPAL
}
public interface PaymentProcessor {
public boolean makePayment(long fee);
}
public class CardPaymentProcessor implements PaymentProcessor {
public boolean makePayment(long fee) {
// make card api call
return true;
}
}
class PaypalPaymentProcessor implements PaymentProcessor {
public boolean makePayment(long fee) {
// make paypal api call
return true;
}
}
public class PaymentProcessorFactory {
public static PaymentProcessor getPaymentProcessor(PaymentType paymentType) {
switch (paymentType) {
case CARD:
return new CardPaymentProcessor();
case PAYPAL:
return new PaypalPaymentProcessor();
default:
return null;
}
}
}
Adhere to SOLID Guidelines
All components have only single responsibility
All components are closed for modification and open for extensions
We are not missing any methods in child classes which are in base classes
Only iterfaces with relevant methods are implemented
All dependencies are with interfaces
Consider Scalability and Flexibility
1. New vehicle sizes could be added without affecting the rest of the system
2. New floors could be added without affecting the rest of the system
3. New spots could be added without affecting the rest of the system
4. New payment methods could be added without affecting the rest of the system
5. Fee calculation strategy could be changed without affecting the rest of the system
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
Bounded waiting list when the parking lot is full
observer pattern for spot addition and its addition to pq