Created
February 6, 2025 23:47
-
-
Save moshaad7/21c56443b2ec793b262c1a8cae5f0726 to your computer and use it in GitHub Desktop.
LLD_PARKING_LOT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"sync" | |
"errors" | |
) | |
// --- Vehicle Types --- | |
// VehicleType indicates the type of a vehicle. | |
type VehicleType int | |
const ( | |
Car VehicleType = iota | |
Bike | |
) | |
func (vt VehicleType) String() string { | |
if vt == Car { | |
return "Car" | |
} | |
return "Bike" | |
} | |
// Vehicle interface for all vehicles. | |
type Vehicle interface { | |
GetID() string | |
GetType() VehicleType | |
} | |
// CarVehicle represents a car. | |
type CarVehicle struct { | |
ID string | |
} | |
func (c *CarVehicle) GetID() string { | |
return c.ID | |
} | |
func (c *CarVehicle) GetType() VehicleType { | |
return Car | |
} | |
// BikeVehicle represents a bike. | |
type BikeVehicle struct { | |
ID string | |
} | |
func (b *BikeVehicle) GetID() string { | |
return b.ID | |
} | |
func (b *BikeVehicle) GetType() VehicleType { | |
return Bike | |
} | |
// --- Parking Spot and Floor --- | |
// ParkingSpot represents an individual parking spot. | |
type ParkingSpot struct { | |
SpotID string | |
Floor int // Floor index | |
SpotType VehicleType | |
Occupied bool | |
Distance int // Lower value means closer to entrance | |
} | |
// Floor contains multiple parking spots. | |
type Floor struct { | |
FloorNumber int | |
Spots []*ParkingSpot | |
mu sync.Mutex | |
} | |
// Utility function to create a floor with given number of car and bike spots. | |
func createFloor(floorNumber int, numCarSpots, numBikeSpots int) *Floor { | |
spots := []*ParkingSpot{} | |
// Create car spots. | |
for i := 0; i < numCarSpots; i++ { | |
spot := &ParkingSpot{ | |
SpotID: fmt.Sprintf("F%d-C%d", floorNumber, i+1), | |
Floor: floorNumber, | |
SpotType: Car, | |
Occupied: false, | |
Distance: i + 1, | |
} | |
spots = append(spots, spot) | |
} | |
// Create bike spots. | |
for i := 0; i < numBikeSpots; i++ { | |
spot := &ParkingSpot{ | |
SpotID: fmt.Sprintf("F%d-B%d", floorNumber, i+1), | |
Floor: floorNumber, | |
SpotType: Bike, | |
Occupied: false, | |
Distance: i + 1, | |
} | |
spots = append(spots, spot) | |
} | |
return &Floor{ | |
FloorNumber: floorNumber, | |
Spots: spots, | |
} | |
} | |
// --- Strategy Pattern for Parking Allocation --- | |
// ParkingStrategy defines an interface for parking spot selection algorithms. | |
type ParkingStrategy interface { | |
FindSpot(lot *ParkingLot, vehicle Vehicle) (*ParkingSpot, error) | |
} | |
// NearestSpotStrategy selects the nearest available spot across all floors. | |
type NearestSpotStrategy struct{} | |
func (n *NearestSpotStrategy) FindSpot(lot *ParkingLot, vehicle Vehicle) (*ParkingSpot, error) { | |
var bestSpot *ParkingSpot | |
// Check each floor. | |
for _, floor := range lot.Floors { | |
floor.mu.Lock() | |
for _, spot := range floor.Spots { | |
if !spot.Occupied && spot.SpotType == vehicle.GetType() { | |
// Choose the spot with the smallest distance (and lower floor if tie). | |
if bestSpot == nil || spot.Distance < bestSpot.Distance || | |
(spot.Distance == bestSpot.Distance && floor.FloorNumber < bestSpot.Floor) { | |
bestSpot = spot | |
} | |
} | |
} | |
floor.mu.Unlock() | |
} | |
if bestSpot == nil { | |
return nil, errors.New("no available spot") | |
} | |
return bestSpot, nil | |
} | |
// --- ParkingLot (Facade & Core Functionalities) --- | |
// ParkingLot represents the entire parking system. | |
type ParkingLot struct { | |
Floors []*Floor | |
vehicleSpotMap map[string]*ParkingSpot // vehicleID -> ParkingSpot | |
parkingStrategy ParkingStrategy | |
mu sync.RWMutex | |
} | |
// NewParkingLot creates a new ParkingLot with the given floors and parking strategy. | |
func NewParkingLot(floors []*Floor, strategy ParkingStrategy) *ParkingLot { | |
return &ParkingLot{ | |
Floors: floors, | |
vehicleSpotMap: make(map[string]*ParkingSpot), | |
parkingStrategy: strategy, | |
} | |
} | |
// Park attempts to park a vehicle using the parking strategy. | |
func (lot *ParkingLot) Park(vehicle Vehicle) error { | |
lot.mu.Lock() | |
defer lot.mu.Unlock() | |
if _, exists := lot.vehicleSpotMap[vehicle.GetID()]; exists { | |
return errors.New("vehicle already parked") | |
} | |
spot, err := lot.parkingStrategy.FindSpot(lot, vehicle) | |
if err != nil { | |
return err | |
} | |
// Mark the spot as occupied. | |
floor := lot.Floors[spot.Floor] | |
floor.mu.Lock() | |
spot.Occupied = true | |
floor.mu.Unlock() | |
lot.vehicleSpotMap[vehicle.GetID()] = spot | |
fmt.Printf("Parked %s (%s) at spot %s on floor %d\n", vehicle.GetID(), vehicle.GetType(), spot.SpotID, spot.Floor) | |
return nil | |
} | |
// Unpark frees the parking spot for the given vehicle. | |
func (lot *ParkingLot) Unpark(vehicleID string) error { | |
lot.mu.Lock() | |
defer lot.mu.Unlock() | |
spot, exists := lot.vehicleSpotMap[vehicleID] | |
if !exists { | |
return errors.New("vehicle not found") | |
} | |
floor := lot.Floors[spot.Floor] | |
floor.mu.Lock() | |
spot.Occupied = false | |
floor.mu.Unlock() | |
delete(lot.vehicleSpotMap, vehicleID) | |
fmt.Printf("Unparked vehicle %s from spot %s on floor %d\n", vehicleID, spot.SpotID, spot.Floor) | |
return nil | |
} | |
// Search returns the parking spot for a given vehicle ID. | |
func (lot *ParkingLot) Search(vehicleID string) (*ParkingSpot, error) { | |
lot.mu.RLock() | |
defer lot.mu.RUnlock() | |
spot, exists := lot.vehicleSpotMap[vehicleID] | |
if !exists { | |
return nil, errors.New("vehicle not parked") | |
} | |
return spot, nil | |
} | |
// --- Main and Test Scenario --- | |
func main() { | |
// Create two floors. | |
floor1 := createFloor(0, 5, 5) | |
floor2 := createFloor(1, 5, 5) | |
floors := []*Floor{floor1, floor2} | |
// Instantiate the parking lot with the NearestSpotStrategy. | |
strategy := &NearestSpotStrategy{} | |
parkingLot := NewParkingLot(floors, strategy) | |
// Create some vehicles. | |
car1 := &CarVehicle{ID: "CAR123"} | |
bike1 := &BikeVehicle{ID: "BIKE456"} | |
car2 := &CarVehicle{ID: "CAR789"} | |
// Park vehicles asynchronously. | |
// In a real system, these calls might be invoked concurrently. | |
if err := parkingLot.Park(car1); err != nil { | |
fmt.Println("Error parking car1:", err) | |
} | |
if err := parkingLot.Park(bike1); err != nil { | |
fmt.Println("Error parking bike1:", err) | |
} | |
if err := parkingLot.Park(car2); err != nil { | |
fmt.Println("Error parking car2:", err) | |
} | |
// Search for a vehicle. | |
if spot, err := parkingLot.Search("CAR123"); err == nil { | |
fmt.Printf("Vehicle CAR123 is parked at spot %s on floor %d\n", spot.SpotID, spot.Floor) | |
} else { | |
fmt.Println(err) | |
} | |
// Unpark a vehicle. | |
if err := parkingLot.Unpark("BIKE456"); err != nil { | |
fmt.Println("Error unparking bike1:", err) | |
} | |
// Try parking another bike. | |
bike2 := &BikeVehicle{ID: "BIKE999"} | |
if err := parkingLot.Park(bike2); err != nil { | |
fmt.Println("Error parking bike2:", err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment