I was trying to designing a simplified parking lot management system. It has three types of slots (s,m,l). The goal here is that we want to make the placement/release of cars as efficient as possible, ideally O(1).
The way I implemented is to use 3 separate stacks to hold empty spots for small, medium and large vehicles and whenever we have a vehicle to place, we check the stack of its size, if there is no empty spots for its size, we upgrade to the next stack of a larger size. So adding and releasing cars would be popping/pushing onto a stack so constant time complexity is guaranteed. And I also maintained a hash map to map the vehicle license id to the actual spot so we can do a constant time lookup when we need to retrieve a car.
Here is my code.
class Spot {
constructor({ size, vehicle } = {}) {
this.size = size
this.vehicle = vehicle
}
addVehicle(vehicle) {
if(this.isOccupied()) return false
this.vehicle = vehicle
return true
}
isOccupied() {
return !!this.vehicle
}
getVehicle() {
return this.vehicle
}
releaseVehicle() {
const currentVehicle = this.vehicle
this.vehicle = null
return currentVehicle
}
}
class ParkingLotManager {
constructor({ size: { numOfSmallSpot, numOfMediumSpot, numOfLargeSpot } }) {
this.emptySpots = Array.from({ length: 3 }, (_, i) => {
if(this._getSizeIndex(1) === i) return Array.from({length: numOfSmallSpot}, () => (new Spot({size: 1})))
if(this._getSizeIndex(2) === i) return Array.from({length: numOfMediumSpot}, () => (new Spot({size: 2})))
if(this._getSizeIndex(3) === i) return Array.from({length: numOfLargeSpot}, () => (new Spot({size: 3})))
})
this.vehicles = new Map()
}
placeVehicle(vehicle) {
if(this.hasVehicle(vehicle.licenseId)) throw new Error('duplicate vehicle found')
const sizeIndex = this._getSizeIndex(vehicle.size)
for (let i = sizeIndex; i < this.emptySpots.length; i++) {
if (this.emptySpots[i].length > 0) {
const spot = this.emptySpots[i].pop()
spot.addVehicle(vehicle)
this.vehicles.set(vehicle.licenseId, spot)
return true
}
}
return false
}
_getSizeIndex(size) {
return size - 1
}
hasVehicle(licenseId) {
return this.vehicles.has(licenseId)
}
removeVehicle(vehicle) {
if (!this.hasVehicle(vehicle.licenseId)) return false
const spot = this.vehicles.get(vehicle)
spot.releaseVehicle()
this.vehicles.delete(vehicle)
this.emptySpots[this._getSizeIndex(this.spot.size)].push(spot)
return true
}
}
class Vehicle {
constructor(id) {
this.licenseId = id
}
}
class Motorcycle extends Vehicle {
constructor(id) {
super(id)
this.size = 1
}
}
class Car extends Vehicle {
constructor(id) {
super(id)
this.size = 2
}
}
class Truck extends Vehicle {
constructor(id) {
super(id)
this.size = 3
}
}
const parkingLotManager = new ParkingLotManager({
size: {
numOfSmallSpot: 2,
numOfMediumSpot: 2,
numOfLargeSpot: 2
}
})
const car1 = new Car('car1')
const car2 = new Car('car2')
const car3 = new Car('car3')
parkingLotManager.placeVehicle(car1)
parkingLotManager.placeVehicle(car2)
parkingLotManager.placeVehicle(car3)
const car4 = new Truck('car4')
const car5 = new Truck('car5')
parkingLotManager.placeVehicle(car4)
console.log(parkingLotManager);
Feel free to give me any feedback - including naming, design, encapsulation, extensibility etc.
Specifically, I hope I can improve these following aspects:
I am not particular clear on when I should throw an exception or return
false
for unsuccessful operation. For example, forplaceVehicle
method, if there is no empty spots, should I returnfalse
to indicate that the operation failed or should I just throw an exception? I guess it depends. but I wonder under which specific situation one approach is favorable than the other?The way I handle the mapping from
size
to the index of the stack which holds the empty spots isn't very elegant. I am an integar to indicate different sizes because I cannot think of a better way. If I am writing TypeScript I guess I can useenum
or union types forSize
. but even withenum
, I still need to translate it into the actual index. I wonder how I can achieve this gracefully and it is also easily extensible so in the future, if I wanted to expand the parking lot, can I easily add a forth size?If I have multiple entrances for this parking lot and there is one car coming into this spot in the parking lot and at the same time, another car will be out of the spot. Then there is a deadlock, how can I avoid or resolve that?