Table of Contents
Overview
While answering any system design question it is important to keep in mind that system design questions can be really broad. Hence never directly jump to the solution. It is good to discuss the use cases with the interviewer so as to grasp what he is looking for. Decide on a set of features that you are going to include in your system design.
This is also one of the aspects the interview is looking for. They might be looking for
- How you are doing requirement analysis
- Are you able to list down all the requirements
- What question you are asking?
Designing a Parking Lot is a very common system design interview question and as with any other system design interview this is what the interviewer is looking for
- Your knowledge of Object-Oriented Design
- How do you frame your design in terms of Design Patterns
Also, note that this question is not a distributed system design.
In this tutorial, we will discuss the low-level design of the Parking Lot. Along with that, we will also see into the full working code for the Parking Lot. Here is the table of contents
- Requirements
- Actor in the design
- UML diagram
- Low-level design represented in Go programming Language
- Full Working Code
- Full Working Code in One file
Requirements
One of the essential things while designing anything is understanding some high-level requirements of the system.
- There could be multiple entrances to the system.
- There could be multiple exist in the parking lot.
- There will be different types of parking spots.
- There will be charges per parking per hour. The charges will be for different types of parking spots and different vehicle
- The system should be adaptable to any kind of changes
- The parking lot can be multilevel
All the actors in a Parking System Design
- Parking System
- Parking Floor
- Parking Spot – Different types of parking Spots
- Car Parking Spot
- Truck Parking Spot
- Motorcycle Parking Spot
- Parking Ticket
- Entry and Exit Gates
- Payment Gateway
- Vehicle
Let’s look at each of these actors
Parking Spot
Now a parking spot can be of different types. It could be
- Car Parking Spot
- Big Vehicle Parking Spot
- Motorcycle Parking Spot
Parking Gate
There could be entry or exit gates. There could gate on different floors.
Parking Ticket
A parking ticket will be issued to every incoming vehicle. Vehicles will be given parking spots based on the type of vehicle. Eg Car vehicles will be allotted a Car Parking Spot Type, a motorcycle vehicle will be allotted a motor cycle Parking Spot, and so on
Payment Gateway Type
A customer could either pay via
- Cash
- Debit Card
- Credit Card
Vehicle
The parking lot as parking spots for the below vehicle types
- Car
- Truck
- Motorcycle
Parking Rate
The rate for parking a car or a truck or a motorcycle vehicle is different. This will be encapsulated in Parking Rate Classes
Parking Floor
A parking floor maintains a list of all parking spots present on that floor. A Parking Floor will be responsible for booking and freeing up the parking spots of different types on the floor
Parking System
It is the main component of the system. It is the driver class in our design
How these actors communicate with each other will be clear with UML diagram and an explanation given after that.
UML Diagram
Below is the UML diagram of the Parking Lot
Let’s understand this UML diagram by looking at different components and how each components integrate with other components
The simplest component in the above UML system is a Parking Spot. A Parking Spot has three pieces of information in it
- full – Denotes whether is it full or not
- floor – Parking Spot lies on which floor
- location – What is the location number of the parking Spot
The next component is the Parking Ticket. A parking Ticket will have below pieces of information
- Vehicle – This ticket is issued for which type of vehicle. The vehicle will vehicle number as well
- Parking Spot – At which Parking Spot the vehicle is parked. You might be wondering why we have this information in the Parking Ticket. This information exists in the Parking Ticket so that we can reclaim the parking spot. You might argue that once a ticket is issued then the parking could be done in any spot. But in this tutorial, we are building a more sophisticated system in which we can know where exactly a vehicle is parked. If you want to build a system in which for a given ticket the parking could be assigned to any spot then it is easier to do so where we can simply maintain a count for each parking spot. In fact, that is a simple version of the design in this tutorial
- Other than that a Parking Ticket will have entry time, exit time, price, gate information, pg information, etc,.
The next component is Parking Floor. A parking floor maintains a list of all parking spots present on that floor. It has below pieces of information
- Parking Spots – There is Double Linked List for each for each type of Parking Spot present on this floor. For example, Car Type Parking Spot will have a separate DLL, and Truck type Parking Spot on that floor will have a different DLL. Why DLL? With DLL it is easy to know the next free Parking Spot in 0(1) time. Also when a Parking Spot gets free then in DLL we can simply move it to the front to denote that it is free
- Floor Number – the floor number simply
- isFull – Denotes whether it is full or not
Then the main component is ParkingSystem. It will have below pieces of information
- An Array of Parking Floors
- The list of Parking Ticket it has issued
- Other than that it will have information about entry and exit gates, isFull variable to denote whether the entire Parking Lot is full or not.
Other than these main components we also have
- Vehicle Component
- Payment Gateway Component
- Entry and Exit Gates
- Parking Rates
- Etc
Design Pattern used in this design
- Factory Pattern to create different instances of Parking Spot Type
- Flyweight Design Pattern to create fixed instances of Parking Rate and Payment Gateway.
Low–Level Design
Below is the low-level design expressed in the Go programming language. Later we will see a working example as well
Parking System
type ParkingSystem struct {
issuedTickets map[string]ParkingTicket
parkingFloor []*ParkingFloor
isFull map[ParkingSpotType]bool
entryGates []Gate
exitGates []Gate
}
func (this *ParkingSystem) addParkingSpot(parkingSpot ParkingSpot) {}
func (this *ParkingSystem) bookParkingSpot(pSpotType ParkingSpotType) (ParkingSpot, error) {}
func (this *ParkingSystem) issueTicket(pSpotType ParkingSpotType, vehicle Vehicle, entryGate Gate) (*ParkingTicket, error) {}
func (this *ParkingSystem) exitVehicle(ticket *ParkingTicket, pGType PaymentGatewayType) {}
Parking Floor
type ParkingFloor struct {
parkingSpots map[ParkingSpotType]*ParkingSpotDLL
parkingSpotsMap map[string]*ParkingSpotDLLNode
isFull map[ParkingSpotType]bool
floor int
}
func (this *ParkingFloor) addParkingSpot(pSpot ParkingSpot) {}
func (this *ParkingFloor) bookParkingSpot(pSpotType ParkingSpotType) (ParkingSpot, error) {}
func (this *ParkingFloor) freeParkingSpot(pSpot ParkingSpot) {}
Parking Ticket
type ParkingTicket struct {
vehicle Vehicle
parkingSpot ParkingSpot
status ParkingTicketStatus
entryTime time.Time
exitTime time.Time
entryGate Gate
exitGate Gate
price int
pgType PaymentGatewayType
}
func (this *ParkingTicket) exit(pgType PaymentGatewayType) {}
Parking Ticket Status Enum
type ParkingTicketStatus uint8
const (
active ParkingTicketStatus = iota
paid
)
Parking Spot Interface
type ParkingSpot interface {
isFull() bool
getFloor() int
getLocation() string
getParkingSpotType() ParkingSpotType
markFull()
markFree()
}
Parking Spot Type Enum
type ParkingSpotType uint8
const (
carPT ParkingSpotType = iota
truckPT
motorcyclePT
)
Car Parking Spot
type CarParkingSpot struct {
full bool
floor int
location string
}
func (this *CarParkingSpot) isFull() bool {}
func (this *CarParkingSpot) getFloor() int {}
func (this *CarParkingSpot) getLocation() string {}
func (this *CarParkingSpot) getParkingSpotType() ParkingSpotType {}
func (this *CarParkingSpot) markFull() {}
func (this *CarParkingSpot) markFree() {}
Truck Parking Spot
type TruckParkingSpot struct {
full bool
floor int
location string
}
func (this *TruckParkingSpot) isFull() bool {}
func (this *TruckParkingSpot) getFloor() int {}
func (this *TruckParkingSpot) getLocation() string {}
func (this *TruckParkingSpot) getParkingSpotType() ParkingSpotType {}
func (this *TruckParkingSpot) markFull() {}
func (this *TruckParkingSpot) markFree() {}
Motorcycle Parking Spot
type MotorCycleParkingSpot struct {
full bool
floor int
location string
}
func (this *MotorCycleParkingSpot) isFull() bool {}
func (this *MotorCycleParkingSpot) getFloor() int {}
func (this *MotorCycleParkingSpot) getLocation() string {}
func (this *MotorCycleParkingSpot) getParkingSpotType() ParkingSpotType {}
func (this *MotorCycleParkingSpot) markFull() {}
func (this *MotorCycleParkingSpot) markFree() {}
Parking Rate Factory and Parking Rate and Child Parking Rate
type ParkingRateFactory struct {
parkingRateMap map[VehicleType]ParkingRate
}
func (this *ParkingRateFactory) getParkingRateInstanceByVehicleType(vType VehicleType) ParkingRate {}
type ParkingRateBase struct {
perHourCharges int
}
type ParkingRate interface {
amountToPay(int) int
}
type CarParkingRate struct {
ParkingRateBase
}
func (this *CarParkingRate) amountToPay(hours int) int {}
type TruckParkingRate struct {
ParkingRateBase
}
func (this *TruckParkingRate) amountToPay(hours int) int {}
type MotorCycleParkingRate struct {
ParkingRateBase
}
func (this *MotorCycleParkingRate) amountToPay(hours int) int {}
Payment Gateway Class and Child Payment Gateway Classes
type PaymentGatewayType uint8
const (
cash PaymentGatewayType = iota
creditCard
debitCard
)
type PaymentGatewayFactory struct {
paymentGatewayMap map[PaymentGatewayType]PaymentGateway
}
func (this *PaymentGatewayFactory) getPaymentGatewayInstanceByPGType(pgType PaymentGatewayType) PaymentGateway {}
type PaymentGateway interface {
pay(int)
}
type CashPaymentGateway struct {
}
func (this CashPaymentGateway) pay(price int) {}
type CreditCardPaymentGateway struct {
}
func (this CreditCardPaymentGateway) pay(price int) {}
type DebitCardPaymentGateway struct {
}
func (this DebitCardPaymentGateway) pay(price int) {}
Program
Here is the full working code if anyone is interested in the Go programming language. In the below example, we are going to look at two examples
- First, one is a small Parking Lot that has only one floor and two car Parking Spots
- The other is a big Parking Lot that has two floors and on each floor, it has two cars, two motorcycles, one truck Parking Spot.
We have used a Doubly Linked List to store the list of Parking Spots so that
- We can find a free parking spot in O(1)
- We should be able to reclaim a parking spot in O(1)
parkingSystem.go
package main
import "fmt"
type ParkingSystem struct {
issuedTickets map[string]ParkingTicket
parkingFloor []*ParkingFloor
isFull map[ParkingSpotType]bool
entryGates []Gate
exitGates []Gate
}
func (this *ParkingSystem) addParkingSpot(parkingSpot ParkingSpot) {
this.parkingFloor[parkingSpot.getFloor()-1].addParkingSpot(parkingSpot)
}
func (this *ParkingSystem) bookParkingSpot(pSpotType ParkingSpotType) (ParkingSpot, error) {
for _, pFloor := range this.parkingFloor {
pSpot, err := pFloor.bookParkingSpot(pSpotType)
if err == nil {
return pSpot, nil
}
}
return nil, fmt.Errorf("Cannot issue ticket. All %s parking spot type are full\n", pSpotType.toString())
}
func (this *ParkingSystem) issueTicket(pSpotType ParkingSpotType, vehicle Vehicle, entryGate Gate) (*ParkingTicket, error) {
fmt.Printf("\nGoing to issue ticket for vehicle number %s\n", vehicle.numberPlate)
pSpot, err := this.bookParkingSpot(pSpotType)
if err != nil {
return nil, err
}
ticket := initParkingTicket(vehicle, pSpot, entryGate)
return ticket, nil
}
func (this *ParkingSystem) printStatus() {
fmt.Println("\nPrinting Status of Parking Spot")
for _, pFloor := range this.parkingFloor {
pFloor.printStatus()
}
}
func (this *ParkingSystem) exitVehicle(ticket *ParkingTicket, pGType PaymentGatewayType) {
this.parkingFloor[ticket.parkingSpot.getFloor()-1].freeParkingSpot(ticket.parkingSpot)
ticket.exit(pGType)
}
parkingFloor.go
package main
import "fmt"
type ParkingFloor struct {
parkingSpots map[ParkingSpotType]*ParkingSpotDLL
parkingSpotsMap map[string]*ParkingSpotDLLNode
isFull map[ParkingSpotType]bool
floor int
}
func initParkingFloor(floor int) *ParkingFloor {
return &ParkingFloor{
floor: floor,
parkingSpots: make(map[ParkingSpotType]*ParkingSpotDLL),
parkingSpotsMap: make(map[string]*ParkingSpotDLLNode),
isFull: make(map[ParkingSpotType]bool),
}
}
func (this *ParkingFloor) addParkingSpot(pSpot ParkingSpot) {
dll, ok := this.parkingSpots[pSpot.getParkingSpotType()]
if ok {
newNode := &ParkingSpotDLLNode{
pSpot: pSpot,
}
dll.AddToFront(newNode)
this.parkingSpotsMap[pSpot.getLocation()] = newNode
return
}
dll = &ParkingSpotDLL{}
this.parkingSpots[pSpot.getParkingSpotType()] = dll
newNode := &ParkingSpotDLLNode{
pSpot: pSpot,
}
this.parkingSpots[pSpot.getParkingSpotType()].AddToFront(newNode)
this.parkingSpotsMap[pSpot.getLocation()] = newNode
}
func (this *ParkingFloor) bookParkingSpot(pSpotType ParkingSpotType) (ParkingSpot, error) {
if this.isFull[pSpotType] {
return nil, fmt.Errorf("%s Parking Spot is full on %d floor", pSpotType.toString(), this.floor)
}
nextPSpot := this.parkingSpots[pSpotType].Front()
nextPSpot.pSpot.markFull()
this.parkingSpots[pSpotType].MoveNodeToEnd(nextPSpot)
if this.parkingSpots[pSpotType].Front().pSpot.isFull() {
this.isFull[pSpotType] = true
}
return nextPSpot.pSpot, nil
}
func (this *ParkingFloor) printStatus() {
for pSpotType, dll := range this.parkingSpots {
fmt.Printf("Details of parking spots of type %s on floor %d\n", pSpotType.toString(), this.floor)
dll.TraverseForward()
}
}
func (this *ParkingFloor) freeParkingSpot(pSpot ParkingSpot) {
node := this.parkingSpotsMap[pSpot.getLocation()]
node.pSpot.markFree()
this.isFull[pSpot.getParkingSpotType()] = false
this.parkingSpots[pSpot.getParkingSpotType()].MoveNodeToFront(node)
}
parkingSpot.go
package main
type ParkingSpot interface {
isFull() bool
getFloor() int
getLocation() string
getParkingSpotType() ParkingSpotType
markFull()
markFree()
}
parkingSpotType.go
package main
type ParkingSpotType uint8
const (
carPT ParkingSpotType = iota
truckPT
motorcyclePT
)
func (s ParkingSpotType) toString() string {
switch s {
case carPT:
return "Car Parking Type"
case truckPT:
return "Truck Parking Type"
case motorcyclePT:
return "Motorcylce Parking Type"
}
return ""
}
func initParkingSpot(floor int, partkingSpotType ParkingSpotType, location string) ParkingSpot {
switch partkingSpotType {
case carPT:
return &CarParkingSpot{full: false,
floor: floor,
location: location,
}
case truckPT:
return &TruckParkingSpot{full: false,
floor: floor,
location: location,
}
case motorcyclePT:
return &MotorCycleParkingSpot{full: false,
floor: floor,
location: location,
}
}
return nil
}
carParkingSpot.go
package main
type CarParkingSpot struct {
full bool
floor int
location string
}
func (this *CarParkingSpot) isFull() bool {
return this.full
}
func (this *CarParkingSpot) getFloor() int {
return this.floor
}
func (this *CarParkingSpot) getLocation() string {
return this.location
}
func (this *CarParkingSpot) getParkingSpotType() ParkingSpotType {
return carPT
}
func (this *CarParkingSpot) markFull() {
this.full = true
}
func (this *CarParkingSpot) markFree() {
this.full = true
}
truckParkingSpot.go
package main
type TruckParkingSpot struct {
full bool
floor int
location string
}
func (this *TruckParkingSpot) isFull() bool {
return this.full
}
func (this *TruckParkingSpot) getFloor() int {
return this.floor
}
func (this *TruckParkingSpot) getLocation() string {
return this.location
}
func (this *TruckParkingSpot) getParkingSpotType() ParkingSpotType {
return truckPT
}
func (this *TruckParkingSpot) markFull() {
this.full = true
}
func (this *TruckParkingSpot) markFree() {
this.full = true
}
motorcycleParkingSpot.go
package main
type MotorCycleParkingSpot struct {
full bool
floor int
location string
}
func (this *MotorCycleParkingSpot) isFull() bool {
return this.full
}
func (this *MotorCycleParkingSpot) getFloor() int {
return this.floor
}
func (this *MotorCycleParkingSpot) getLocation() string {
return this.location
}
func (this *MotorCycleParkingSpot) getParkingSpotType() ParkingSpotType {
return motorcyclePT
}
func (this *MotorCycleParkingSpot) markFull() {
this.full = true
}
func (this *MotorCycleParkingSpot) markFree() {
this.full = true
}
parkingTicket.go
package main
import (
"fmt"
"time"
)
type ParkingTicket struct {
vehicle Vehicle
parkingSpot ParkingSpot
status ParkingTicketStatus
entryTime time.Time
exitTime time.Time
entryGate Gate
exitGate Gate
price int
pgType PaymentGatewayType
}
func initParkingTicket(vehicle Vehicle, pSpot ParkingSpot, entryGate Gate) *ParkingTicket {
return &ParkingTicket{
vehicle: vehicle,
parkingSpot: pSpot,
status: active,
entryTime: time.Now(),
entryGate: entryGate,
}
}
func (this *ParkingTicket) exit(pgType PaymentGatewayType) {
fmt.Printf("Vehicle with number %s exiting from Parking Lot\n", this.vehicle.numberPlate)
this.exitTime = time.Now()
pRateInstance := pRateFactorySingleInstance.getParkingRateInstanceByVehicleType(this.vehicle.vehicleType)
totalDurationInHours := int(this.exitTime.Sub(this.entryTime).Hours())
this.price = pRateInstance.amountToPay(totalDurationInHours) + 1
this.pgType = pgType
pgInstance := pgFactorySingleInstance.getPaymentGatewayInstanceByPGType(pgType)
pgInstance.pay(this.price)
this.status = paid
}
func (this *ParkingTicket) print() {
fmt.Printf("Issued ticket for vehicle number %s at parking spot %s\n ", this.vehicle.numberPlate, this.parkingSpot.getLocation())
//fmt.Printf("\nPrinting Ticket\n")
//fmt.Printf("Status: %s, \nEntryTime: %s, \nEntryGate: %d, \nVehicle: %s, \nParking Spot: \n\n", this.status.toString(), this.entryTime.String(), this.entryGate, this.vehicle.toString())
}
parkingTicketStatus.go
package main
type ParkingTicketStatus uint8
const (
active ParkingTicketStatus = iota
paid
)
func (s ParkingTicketStatus) toString() string {
switch s {
case active:
return "Active"
case paid:
return "Paid"
}
return ""
}
dll.go
package main
import "fmt"
type ParkingSpotDLLNode struct {
pSpot ParkingSpot
prev *ParkingSpotDLLNode
next *ParkingSpotDLLNode
}
type ParkingSpotDLL struct {
len int
tail *ParkingSpotDLLNode
head *ParkingSpotDLLNode
}
func initDoublyList() *ParkingSpotDLL {
return &ParkingSpotDLL{}
}
func (d *ParkingSpotDLL) AddToFront(node *ParkingSpotDLLNode) {
if d.head == nil {
d.head = node
d.tail = node
} else {
node.next = d.head
d.head.prev = node
d.head = node
}
d.len++
return
}
func (d *ParkingSpotDLL) RemoveFromFront() {
if d.head == nil {
return
} else if d.head == d.tail {
d.head = nil
d.tail = nil
} else {
d.head = d.head.next
}
d.len--
}
func (d *ParkingSpotDLL) AddToEnd(node *ParkingSpotDLLNode) {
newNode := node
if d.head == nil {
d.head = newNode
d.tail = newNode
} else {
currentNode := d.head
for currentNode.next != nil {
currentNode = currentNode.next
}
newNode.prev = currentNode
currentNode.next = newNode
d.tail = newNode
}
d.len++
}
func (d *ParkingSpotDLL) Front() *ParkingSpotDLLNode {
return d.head
}
func (d *ParkingSpotDLL) MoveNodeToEnd(node *ParkingSpotDLLNode) {
prev := node.prev
next := node.next
if prev != nil {
prev.next = next
}
if next != nil {
next.prev = prev
}
if d.tail == node {
d.tail = prev
}
if d.head == node {
d.head = next
}
node.next = nil
node.prev = nil
d.len--
d.AddToEnd(node)
}
func (d *ParkingSpotDLL) MoveNodeToFront(node *ParkingSpotDLLNode) {
prev := node.prev
next := node.next
if prev != nil {
prev.next = next
}
if next != nil {
next.prev = prev
}
if d.tail == node {
d.tail = prev
}
if d.head == node {
d.head = next
}
node.next = nil
node.prev = nil
d.len--
d.AddToFront(node)
}
func (d *ParkingSpotDLL) TraverseForward() error {
if d.head == nil {
return fmt.Errorf("TraverseError: List is empty")
}
temp := d.head
for temp != nil {
fmt.Printf("Location = %v, parkingType = %s, floor = %d full =%t\n", temp.pSpot.getLocation(), temp.pSpot.getParkingSpotType().toString(), temp.pSpot.getFloor(), temp.pSpot.isFull())
temp = temp.next
}
fmt.Println()
return nil
}
func (d *ParkingSpotDLL) Size() int {
return d.len
}
gate.go
package main
type Gate struct {
floor int
gateType GateType
}
func initGate(floor int, gateType GateType) Gate {
return Gate{
floor: floor,
gateType: gateType,
}
}
gateType.go
package main
type GateType uint8
const (
entryGateType GateType = iota
exitGateType GateType = iota
)
vehicle.go
package main
import "fmt"
type Vehicle struct {
numberPlate string
vehicleType VehicleType
}
func (v Vehicle) toString() string {
return fmt.Sprintf("{NumberPlate: %s, VehicleType: %s}", v.numberPlate, v.vehicleType.toString())
}
vehicleType.go
package main
type VehicleType uint8
const (
car VehicleType = iota
truck
motorcycle
)
func (s VehicleType) toString() string {
switch s {
case car:
return "Car"
case truck:
return "Truck"
case motorcycle:
return "Motorcylce"
}
return ""
}
parkingRate.go
package main
type ParkingRateFactory struct {
parkingRateMap map[VehicleType]ParkingRate
}
func (this *ParkingRateFactory) getParkingRateInstanceByVehicleType(vType VehicleType) ParkingRate {
if this.parkingRateMap[vType] != nil {
return this.parkingRateMap[vType]
}
if vType == car {
this.parkingRateMap[vType] = &CarParkingRate{
ParkingRateBase{perHourCharges: 2},
}
return this.parkingRateMap[vType]
}
if vType == truck {
this.parkingRateMap[vType] = &TruckParkingRate{
ParkingRateBase{perHourCharges: 3},
}
return this.parkingRateMap[vType]
}
if vType == motorcycle {
this.parkingRateMap[vType] = &MotorCycleParkingRate{
ParkingRateBase{perHourCharges: 1},
}
return this.parkingRateMap[vType]
}
return nil
}
type ParkingRateBase struct {
perHourCharges int
}
type ParkingRate interface {
amountToPay(int) int
}
type CarParkingRate struct {
ParkingRateBase
}
func (this *CarParkingRate) amountToPay(hours int) int {
return this.perHourCharges * hours
}
type TruckParkingRate struct {
ParkingRateBase
}
func (this *TruckParkingRate) amountToPay(hours int) int {
return this.perHourCharges * hours
}
type MotorCycleParkingRate struct {
ParkingRateBase
}
func (this *MotorCycleParkingRate) amountToPay(hours int) int {
return this.perHourCharges * hours
}
paymentGateway.go
package main
import "fmt"
type PaymentGatewayType uint8
const (
cash PaymentGatewayType = iota
creditCard
debitCard
)
type PaymentGatewayFactory struct {
paymentGatewayMap map[PaymentGatewayType]PaymentGateway
}
func (this *PaymentGatewayFactory) getPaymentGatewayInstanceByPGType(pgType PaymentGatewayType) PaymentGateway {
if this.paymentGatewayMap[pgType] != nil {
return this.paymentGatewayMap[pgType]
}
if pgType == cash {
this.paymentGatewayMap[pgType] = &CashPaymentGateway{}
return this.paymentGatewayMap[pgType]
}
if pgType == creditCard {
this.paymentGatewayMap[pgType] = &CreditCardPaymentGateway{}
return this.paymentGatewayMap[pgType]
}
if pgType == debitCard {
this.paymentGatewayMap[pgType] = &DebitCardPaymentGateway{}
return this.paymentGatewayMap[pgType]
}
return nil
}
type PaymentGateway interface {
pay(int)
}
type CashPaymentGateway struct {
}
func (this CashPaymentGateway) pay(price int) {
fmt.Printf("Paying price of %d$ through cash payment\n", price)
}
type CreditCardPaymentGateway struct {
}
func (this CreditCardPaymentGateway) pay(price int) {
fmt.Printf("Paying price of %d$ through credit card payment\n", price)
}
type DebitCardPaymentGateway struct {
}
func (this DebitCardPaymentGateway) pay(price int) {
fmt.Printf("Paying price of %d$ through debit card payment\n", price)
}
main.go
package main
import (
"fmt"
)
var (
pRateFactorySingleInstance = &ParkingRateFactory{
parkingRateMap: make(map[VehicleType]ParkingRate),
}
pgFactorySingleInstance = &PaymentGatewayFactory{
paymentGatewayMap: make(map[PaymentGatewayType]PaymentGateway),
}
)
func main() {
testSmallParkingLot()
testLargeParkingLot()
}
func testSmallParkingLot() {
firstParkingFloor := initParkingFloor(1)
firstFloorEntryGate1 := initGate(1, entryGateType)
firstFloorExitGate := initGate(1, exitGateType)
firstFloorCarParkingSpot1 := initParkingSpot(1, carPT, "A1")
firstFloorCarParkingSpot2 := initParkingSpot(1, carPT, "A2")
parkingSystem := ParkingSystem{
parkingFloor: []*ParkingFloor{firstParkingFloor},
entryGates: []Gate{firstFloorEntryGate1},
exitGates: []Gate{firstFloorExitGate},
issuedTickets: make(map[string]ParkingTicket),
}
//Add first floor parkings
parkingSystem.addParkingSpot(firstFloorCarParkingSpot1)
parkingSystem.addParkingSpot(firstFloorCarParkingSpot2)
carVehicle1 := Vehicle{
numberPlate: "C1",
vehicleType: car,
}
carVehicle2 := Vehicle{
numberPlate: "C2",
vehicleType: car,
}
parkingSystem.printStatus()
carVehicleTicket1, err := parkingSystem.issueTicket(carPT, carVehicle1, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket1.print()
carVehicleTicket2, err := parkingSystem.issueTicket(carPT, carVehicle2, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket2.print()
carVehicle3 := Vehicle{
numberPlate: "C3",
vehicleType: car,
}
carVehicleTicket3, err := parkingSystem.issueTicket(carPT, carVehicle3, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
parkingSystem.printStatus()
parkingSystem.exitVehicle(carVehicleTicket1, cash)
parkingSystem.printStatus()
carVehicleTicket3, err = parkingSystem.issueTicket(carPT, carVehicle3, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket3.print()
parkingSystem.printStatus()
}
func testLargeParkingLot() {
//We have two parking floor
firstParkingFloor := initParkingFloor(1)
secondParkingFloor := initParkingFloor(2)
//We have two entry gates in firstParkingFloor
firstFloorEntryGate1 := initGate(1, entryGateType)
firstFloorEntryGate2 := initGate(1, entryGateType)
//We have one exit gate on firstParkingFloor
firstFloorExitGate := initGate(1, exitGateType)
parkingSystem := ParkingSystem{
parkingFloor: []*ParkingFloor{firstParkingFloor, secondParkingFloor},
entryGates: []Gate{firstFloorEntryGate1, firstFloorEntryGate2},
exitGates: []Gate{firstFloorExitGate},
issuedTickets: make(map[string]ParkingTicket),
}
//We have two car parking spots, two motorcyle parking spots, 1 truck paring spot on each of the floor
firstFloorCarParkingSpot1 := initParkingSpot(1, carPT, "A1")
firstFloorCarParkingSpot2 := initParkingSpot(1, carPT, "A2")
firstFloorMotorCycleParkingSpot1 := initParkingSpot(1, motorcyclePT, "A3")
firstFloorMotorCycleParkingSpot2 := initParkingSpot(1, motorcyclePT, "A4")
firstFloorTruckParkingSpot := initParkingSpot(1, truckPT, "A5")
//We have two car parking spots, two motorcyle parking spots, 1 truck paring spot on each of the floor
secondFloorCarParkingSpot1 := initParkingSpot(2, carPT, "B1")
secondFloorCarParkingSpot2 := initParkingSpot(2, carPT, "B2")
secondFloorMotorCycleParkingSpot1 := initParkingSpot(2, motorcyclePT, "B3")
secondFloorMotorCycleParkingSpot2 := initParkingSpot(2, motorcyclePT, "B4")
secondFloorTruckParkingSpot := initParkingSpot(2, truckPT, "B5")
//Add first floor parkings
parkingSystem.addParkingSpot(firstFloorCarParkingSpot1)
parkingSystem.addParkingSpot(firstFloorCarParkingSpot2)
parkingSystem.addParkingSpot(firstFloorMotorCycleParkingSpot1)
parkingSystem.addParkingSpot(firstFloorMotorCycleParkingSpot2)
parkingSystem.addParkingSpot(firstFloorTruckParkingSpot)
//Add second floor parkings
parkingSystem.addParkingSpot(secondFloorCarParkingSpot1)
parkingSystem.addParkingSpot(secondFloorCarParkingSpot2)
parkingSystem.addParkingSpot(secondFloorMotorCycleParkingSpot1)
parkingSystem.addParkingSpot(secondFloorMotorCycleParkingSpot2)
parkingSystem.addParkingSpot(secondFloorTruckParkingSpot)
carVehicle1 := Vehicle{
numberPlate: "C1",
vehicleType: car,
}
carVehicle2 := Vehicle{
numberPlate: "C2",
vehicleType: car,
}
motorCycleVehicle1 := Vehicle{
numberPlate: "M1",
vehicleType: motorcycle,
}
motorCycleVehicle2 := Vehicle{
numberPlate: "M2",
vehicleType: motorcycle,
}
truckVehicle1 := Vehicle{
numberPlate: "T1",
vehicleType: motorcycle,
}
parkingSystem.printStatus()
carVehicleTicket1, err := parkingSystem.issueTicket(carPT, carVehicle1, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket1.print()
carVehicleTicket2, err := parkingSystem.issueTicket(carPT, carVehicle2, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket2.print()
motorCycleVehicleTicket1, err := parkingSystem.issueTicket(motorcyclePT, motorCycleVehicle1, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
motorCycleVehicleTicket1.print()
motorCycleVehicleTicket2, err := parkingSystem.issueTicket(motorcyclePT, motorCycleVehicle2, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
motorCycleVehicleTicket2.print()
truckVehicleTicket1, err := parkingSystem.issueTicket(truckPT, truckVehicle1, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
truckVehicleTicket1.print()
parkingSystem.printStatus()
carVehicle3 := Vehicle{
numberPlate: "C3",
vehicleType: car,
}
carVehicle4 := Vehicle{
numberPlate: "C4",
vehicleType: car,
}
motorCycleVehicle3 := Vehicle{
numberPlate: "M3",
vehicleType: motorcycle,
}
motorCycleVehicle4 := Vehicle{
numberPlate: "M4",
vehicleType: motorcycle,
}
truckVehicle2 := Vehicle{
numberPlate: "T2",
vehicleType: motorcycle,
}
carVehicleTicket3, err := parkingSystem.issueTicket(carPT, carVehicle3, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket3.print()
carVehicleTicket4, err := parkingSystem.issueTicket(carPT, carVehicle4, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket4.print()
motorCycleVehicleTicket3, err := parkingSystem.issueTicket(motorcyclePT, motorCycleVehicle3, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
motorCycleVehicleTicket3.print()
motorCycleVehicleTicket4, err := parkingSystem.issueTicket(motorcyclePT, motorCycleVehicle4, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
motorCycleVehicleTicket4.print()
truckVehicleTicket2, err := parkingSystem.issueTicket(truckPT, truckVehicle2, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
truckVehicleTicket2.print()
parkingSystem.printStatus()
carVehicle5 := Vehicle{
numberPlate: "C5",
vehicleType: car,
}
carVehicleTicket5, err := parkingSystem.issueTicket(carPT, carVehicle5, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
parkingSystem.printStatus()
parkingSystem.exitVehicle(carVehicleTicket1, cash)
parkingSystem.printStatus()
carVehicleTicket5, err = parkingSystem.issueTicket(carPT, carVehicle5, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket5.print()
parkingSystem.printStatus()
}
Output
Printing Status of Parking Spot
Details of parking spots of type Car Parking Type on floor 1
Location = A2, parkingType = Car Parking Type, floor = 1 full =false
Location = A1, parkingType = Car Parking Type, floor = 1 full =false
Going to issue ticket for vehicle number C1
Issued ticket for vehicle number C1 at parking spot A2
Going to issue ticket for vehicle number C2
Issued ticket for vehicle number C2 at parking spot A1
Going to issue ticket for vehicle number C3
Cannot issue ticket. All Car Parking Type parking spot type are full
Printing Status of Parking Spot
Details of parking spots of type Car Parking Type on floor 1
Location = A2, parkingType = Car Parking Type, floor = 1 full =true
Location = A1, parkingType = Car Parking Type, floor = 1 full =true
Vehicle with number C1 exiting from Parking Lot
Paying price of 1$ through cash payment
Printing Status of Parking Spot
Details of parking spots of type Car Parking Type on floor 1
Location = A2, parkingType = Car Parking Type, floor = 1 full =true
Location = A1, parkingType = Car Parking Type, floor = 1 full =true
Going to issue ticket for vehicle number C3
Issued ticket for vehicle number C3 at parking spot A2
Printing Status of Parking Spot
Details of parking spots of type Car Parking Type on floor 1
Location = A1, parkingType = Car Parking Type, floor = 1 full =true
Location = A2, parkingType = Car Parking Type, floor = 1 full =true
Full Working Code in one File
Here is the full working code
package main
import (
"fmt"
"time"
)
type CarParkingSpot struct {
full bool
floor int
location string
}
func (this *CarParkingSpot) isFull() bool {
return this.full
}
func (this *CarParkingSpot) getFloor() int {
return this.floor
}
func (this *CarParkingSpot) getLocation() string {
return this.location
}
func (this *CarParkingSpot) getParkingSpotType() ParkingSpotType {
return carPT
}
func (this *CarParkingSpot) markFull() {
this.full = true
}
func (this *CarParkingSpot) markFree() {
this.full = true
}
type ParkingSpotDLLNode struct {
pSpot ParkingSpot
prev *ParkingSpotDLLNode
next *ParkingSpotDLLNode
}
type ParkingSpotDLL struct {
len int
tail *ParkingSpotDLLNode
head *ParkingSpotDLLNode
}
func initDoublyList() *ParkingSpotDLL {
return &ParkingSpotDLL{}
}
func (d *ParkingSpotDLL) AddToFront(node *ParkingSpotDLLNode) {
if d.head == nil {
d.head = node
d.tail = node
} else {
node.next = d.head
d.head.prev = node
d.head = node
}
d.len++
return
}
func (d *ParkingSpotDLL) RemoveFromFront() {
if d.head == nil {
return
} else if d.head == d.tail {
d.head = nil
d.tail = nil
} else {
d.head = d.head.next
}
d.len--
}
func (d *ParkingSpotDLL) AddToEnd(node *ParkingSpotDLLNode) {
newNode := node
if d.head == nil {
d.head = newNode
d.tail = newNode
} else {
currentNode := d.head
for currentNode.next != nil {
currentNode = currentNode.next
}
newNode.prev = currentNode
currentNode.next = newNode
d.tail = newNode
}
d.len++
}
func (d *ParkingSpotDLL) Front() *ParkingSpotDLLNode {
return d.head
}
func (d *ParkingSpotDLL) MoveNodeToEnd(node *ParkingSpotDLLNode) {
prev := node.prev
next := node.next
if prev != nil {
prev.next = next
}
if next != nil {
next.prev = prev
}
if d.tail == node {
d.tail = prev
}
if d.head == node {
d.head = next
}
node.next = nil
node.prev = nil
d.len--
d.AddToEnd(node)
}
func (d *ParkingSpotDLL) MoveNodeToFront(node *ParkingSpotDLLNode) {
prev := node.prev
next := node.next
if prev != nil {
prev.next = next
}
if next != nil {
next.prev = prev
}
if d.tail == node {
d.tail = prev
}
if d.head == node {
d.head = next
}
node.next = nil
node.prev = nil
d.len--
d.AddToFront(node)
}
func (d *ParkingSpotDLL) TraverseForward() error {
if d.head == nil {
return fmt.Errorf("TraverseError: List is empty")
}
temp := d.head
for temp != nil {
fmt.Printf("Location = %v, parkingType = %s, floor = %d full =%t\n", temp.pSpot.getLocation(), temp.pSpot.getParkingSpotType().toString(), temp.pSpot.getFloor(), temp.pSpot.isFull())
temp = temp.next
}
fmt.Println()
return nil
}
func (d *ParkingSpotDLL) Size() int {
return d.len
}
type Gate struct {
floor int
gateType GateType
}
func initGate(floor int, gateType GateType) Gate {
return Gate{
floor: floor,
gateType: gateType,
}
}
type GateType uint8
const (
entryGateType GateType = iota
exitGateType GateType = iota
)
var (
pRateFactorySingleInstance = &ParkingRateFactory{
parkingRateMap: make(map[VehicleType]ParkingRate),
}
pgFactorySingleInstance = &PaymentGatewayFactory{
paymentGatewayMap: make(map[PaymentGatewayType]PaymentGateway),
}
)
type MotorCycleParkingSpot struct {
full bool
floor int
location string
}
func (this *MotorCycleParkingSpot) isFull() bool {
return this.full
}
func (this *MotorCycleParkingSpot) getFloor() int {
return this.floor
}
func (this *MotorCycleParkingSpot) getLocation() string {
return this.location
}
func (this *MotorCycleParkingSpot) getParkingSpotType() ParkingSpotType {
return motorcyclePT
}
func (this *MotorCycleParkingSpot) markFull() {
this.full = true
}
func (this *MotorCycleParkingSpot) markFree() {
this.full = true
}
type ParkingFloor struct {
parkingSpots map[ParkingSpotType]*ParkingSpotDLL
parkingSpotsMap map[string]*ParkingSpotDLLNode
isFull map[ParkingSpotType]bool
floor int
}
func initParkingFloor(floor int) *ParkingFloor {
return &ParkingFloor{
floor: floor,
parkingSpots: make(map[ParkingSpotType]*ParkingSpotDLL),
parkingSpotsMap: make(map[string]*ParkingSpotDLLNode),
isFull: make(map[ParkingSpotType]bool),
}
}
func (this *ParkingFloor) addParkingSpot(pSpot ParkingSpot) {
dll, ok := this.parkingSpots[pSpot.getParkingSpotType()]
if ok {
newNode := &ParkingSpotDLLNode{
pSpot: pSpot,
}
dll.AddToFront(newNode)
this.parkingSpotsMap[pSpot.getLocation()] = newNode
return
}
dll = &ParkingSpotDLL{}
this.parkingSpots[pSpot.getParkingSpotType()] = dll
newNode := &ParkingSpotDLLNode{
pSpot: pSpot,
}
this.parkingSpots[pSpot.getParkingSpotType()].AddToFront(newNode)
this.parkingSpotsMap[pSpot.getLocation()] = newNode
}
func (this *ParkingFloor) bookParkingSpot(pSpotType ParkingSpotType) (ParkingSpot, error) {
if this.isFull[pSpotType] {
return nil, fmt.Errorf("%s Parking Spot is full on %d floor", pSpotType.toString(), this.floor)
}
nextPSpot := this.parkingSpots[pSpotType].Front()
nextPSpot.pSpot.markFull()
this.parkingSpots[pSpotType].MoveNodeToEnd(nextPSpot)
if this.parkingSpots[pSpotType].Front().pSpot.isFull() {
this.isFull[pSpotType] = true
}
return nextPSpot.pSpot, nil
}
func (this *ParkingFloor) printStatus() {
for pSpotType, dll := range this.parkingSpots {
fmt.Printf("Details of parking spots of type %s on floor %d\n", pSpotType.toString(), this.floor)
dll.TraverseForward()
}
}
func (this *ParkingFloor) freeParkingSpot(pSpot ParkingSpot) {
node := this.parkingSpotsMap[pSpot.getLocation()]
node.pSpot.markFree()
this.isFull[pSpot.getParkingSpotType()] = false
this.parkingSpots[pSpot.getParkingSpotType()].MoveNodeToFront(node)
}
type ParkingRateFactory struct {
parkingRateMap map[VehicleType]ParkingRate
}
func (this *ParkingRateFactory) getParkingRateInstanceByVehicleType(vType VehicleType) ParkingRate {
if this.parkingRateMap[vType] != nil {
return this.parkingRateMap[vType]
}
if vType == car {
this.parkingRateMap[vType] = &CarParkingRate{
ParkingRateBase{perHourCharges: 2},
}
return this.parkingRateMap[vType]
}
if vType == truck {
this.parkingRateMap[vType] = &TruckParkingRate{
ParkingRateBase{perHourCharges: 3},
}
return this.parkingRateMap[vType]
}
if vType == motorcycle {
this.parkingRateMap[vType] = &MotorCycleParkingRate{
ParkingRateBase{perHourCharges: 1},
}
return this.parkingRateMap[vType]
}
return nil
}
type ParkingRateBase struct {
perHourCharges int
}
type ParkingRate interface {
amountToPay(int) int
}
type CarParkingRate struct {
ParkingRateBase
}
func (this *CarParkingRate) amountToPay(hours int) int {
return this.perHourCharges * hours
}
type TruckParkingRate struct {
ParkingRateBase
}
func (this *TruckParkingRate) amountToPay(hours int) int {
return this.perHourCharges * hours
}
type MotorCycleParkingRate struct {
ParkingRateBase
}
func (this *MotorCycleParkingRate) amountToPay(hours int) int {
return this.perHourCharges * hours
}
type ParkingSpot interface {
isFull() bool
getFloor() int
getLocation() string
getParkingSpotType() ParkingSpotType
markFull()
markFree()
}
type ParkingSpotType uint8
const (
carPT ParkingSpotType = iota
truckPT
motorcyclePT
)
func (s ParkingSpotType) toString() string {
switch s {
case carPT:
return "Car Parking Type"
case truckPT:
return "Truck Parking Type"
case motorcyclePT:
return "Motorcylce Parking Type"
}
return ""
}
func initParkingSpot(floor int, partkingSpotType ParkingSpotType, location string) ParkingSpot {
switch partkingSpotType {
case carPT:
return &CarParkingSpot{full: false,
floor: floor,
location: location,
}
case truckPT:
return &TruckParkingSpot{full: false,
floor: floor,
location: location,
}
case motorcyclePT:
return &MotorCycleParkingSpot{full: false,
floor: floor,
location: location,
}
}
return nil
}
type ParkingSystem struct {
issuedTickets map[string]ParkingTicket
parkingFloor []*ParkingFloor
isFull map[ParkingSpotType]bool
entryGates []Gate
exitGates []Gate
}
func (this *ParkingSystem) addParkingSpot(parkingSpot ParkingSpot) {
this.parkingFloor[parkingSpot.getFloor()-1].addParkingSpot(parkingSpot)
}
func (this *ParkingSystem) bookParkingSpot(pSpotType ParkingSpotType) (ParkingSpot, error) {
for _, pFloor := range this.parkingFloor {
pSpot, err := pFloor.bookParkingSpot(pSpotType)
if err == nil {
return pSpot, nil
}
}
return nil, fmt.Errorf("Cannot issue ticket. All %s parking spot type are full\n", pSpotType.toString())
}
func (this *ParkingSystem) issueTicket(pSpotType ParkingSpotType, vehicle Vehicle, entryGate Gate) (*ParkingTicket, error) {
fmt.Printf("\nGoing to issue ticket for vehicle number %s\n", vehicle.numberPlate)
pSpot, err := this.bookParkingSpot(pSpotType)
if err != nil {
return nil, err
}
ticket := initParkingTicket(vehicle, pSpot, entryGate)
return ticket, nil
}
func (this *ParkingSystem) printStatus() {
fmt.Println("\nPrinting Status of Parking Spot")
for _, pFloor := range this.parkingFloor {
pFloor.printStatus()
}
}
func (this *ParkingSystem) exitVehicle(ticket *ParkingTicket, pGType PaymentGatewayType) {
this.parkingFloor[ticket.parkingSpot.getFloor()-1].freeParkingSpot(ticket.parkingSpot)
ticket.exit(pGType)
}
type ParkingTicket struct {
vehicle Vehicle
parkingSpot ParkingSpot
status ParkingTicketStatus
entryTime time.Time
exitTime time.Time
entryGate Gate
exitGate Gate
price int
pgType PaymentGatewayType
}
func initParkingTicket(vehicle Vehicle, pSpot ParkingSpot, entryGate Gate) *ParkingTicket {
return &ParkingTicket{
vehicle: vehicle,
parkingSpot: pSpot,
status: active,
entryTime: time.Now(),
entryGate: entryGate,
}
}
func (this *ParkingTicket) exit(pgType PaymentGatewayType) {
fmt.Printf("Vehicle with number %s exiting from Parking Lot\n", this.vehicle.numberPlate)
this.exitTime = time.Now()
pRateInstance := pRateFactorySingleInstance.getParkingRateInstanceByVehicleType(this.vehicle.vehicleType)
totalDurationInHours := int(this.exitTime.Sub(this.entryTime).Hours())
this.price = pRateInstance.amountToPay(totalDurationInHours) + 1
this.pgType = pgType
pgInstance := pgFactorySingleInstance.getPaymentGatewayInstanceByPGType(pgType)
pgInstance.pay(this.price)
this.status = paid
}
func (this *ParkingTicket) print() {
fmt.Printf("Issued ticket for vehicle number %s at parking spot %s\n ", this.vehicle.numberPlate, this.parkingSpot.getLocation())
//fmt.Printf("\nPrinting Ticket\n")
//fmt.Printf("Status: %s, \nEntryTime: %s, \nEntryGate: %d, \nVehicle: %s, \nParking Spot: \n\n", this.status.toString(), this.entryTime.String(), this.entryGate, this.vehicle.toString())
}
type ParkingTicketStatus uint8
const (
active ParkingTicketStatus = iota
paid
)
func (s ParkingTicketStatus) toString() string {
switch s {
case active:
return "Active"
case paid:
return "Paid"
}
return ""
}
type PaymentGatewayType uint8
const (
cash PaymentGatewayType = iota
creditCard
debitCard
)
type PaymentGatewayFactory struct {
paymentGatewayMap map[PaymentGatewayType]PaymentGateway
}
func (this *PaymentGatewayFactory) getPaymentGatewayInstanceByPGType(pgType PaymentGatewayType) PaymentGateway {
if this.paymentGatewayMap[pgType] != nil {
return this.paymentGatewayMap[pgType]
}
if pgType == cash {
this.paymentGatewayMap[pgType] = &CashPaymentGateway{}
return this.paymentGatewayMap[pgType]
}
if pgType == creditCard {
this.paymentGatewayMap[pgType] = &CreditCardPaymentGateway{}
return this.paymentGatewayMap[pgType]
}
if pgType == debitCard {
this.paymentGatewayMap[pgType] = &DebitCardPaymentGateway{}
return this.paymentGatewayMap[pgType]
}
return nil
}
type PaymentGateway interface {
pay(int)
}
type CashPaymentGateway struct {
}
func (this CashPaymentGateway) pay(price int) {
fmt.Printf("Paying price of %d$ through cash payment\n", price)
}
type CreditCardPaymentGateway struct {
}
func (this CreditCardPaymentGateway) pay(price int) {
fmt.Printf("Paying price of %d$ through credit card payment\n", price)
}
type DebitCardPaymentGateway struct {
}
func (this DebitCardPaymentGateway) pay(price int) {
fmt.Printf("Paying price of %d$ through debit card payment\n", price)
}
type TruckParkingSpot struct {
full bool
floor int
location string
}
func (this *TruckParkingSpot) isFull() bool {
return this.full
}
func (this *TruckParkingSpot) getFloor() int {
return this.floor
}
func (this *TruckParkingSpot) getLocation() string {
return this.location
}
func (this *TruckParkingSpot) getParkingSpotType() ParkingSpotType {
return truckPT
}
func (this *TruckParkingSpot) markFull() {
this.full = true
}
func (this *TruckParkingSpot) markFree() {
this.full = true
}
type Vehicle struct {
numberPlate string
vehicleType VehicleType
}
func (v Vehicle) toString() string {
return fmt.Sprintf("{NumberPlate: %s, VehicleType: %s}", v.numberPlate, v.vehicleType.toString())
}
type VehicleType uint8
const (
car VehicleType = iota
truck
motorcycle
)
func (s VehicleType) toString() string {
switch s {
case car:
return "Car"
case truck:
return "Truck"
case motorcycle:
return "Motorcylce"
}
return ""
}
func main() {
testSmallParkingLot()
//testLargeParkingLot()
}
func testSmallParkingLot() {
firstParkingFloor := initParkingFloor(1)
firstFloorEntryGate1 := initGate(1, entryGateType)
firstFloorExitGate := initGate(1, exitGateType)
firstFloorCarParkingSpot1 := initParkingSpot(1, carPT, "A1")
firstFloorCarParkingSpot2 := initParkingSpot(1, carPT, "A2")
parkingSystem := ParkingSystem{
parkingFloor: []*ParkingFloor{firstParkingFloor},
entryGates: []Gate{firstFloorEntryGate1},
exitGates: []Gate{firstFloorExitGate},
issuedTickets: make(map[string]ParkingTicket),
}
//Add first floor parkings
parkingSystem.addParkingSpot(firstFloorCarParkingSpot1)
parkingSystem.addParkingSpot(firstFloorCarParkingSpot2)
carVehicle1 := Vehicle{
numberPlate: "C1",
vehicleType: car,
}
carVehicle2 := Vehicle{
numberPlate: "C2",
vehicleType: car,
}
parkingSystem.printStatus()
carVehicleTicket1, err := parkingSystem.issueTicket(carPT, carVehicle1, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket1.print()
carVehicleTicket2, err := parkingSystem.issueTicket(carPT, carVehicle2, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket2.print()
carVehicle3 := Vehicle{
numberPlate: "C3",
vehicleType: car,
}
carVehicleTicket3, err := parkingSystem.issueTicket(carPT, carVehicle3, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
parkingSystem.printStatus()
parkingSystem.exitVehicle(carVehicleTicket1, cash)
parkingSystem.printStatus()
carVehicleTicket3, err = parkingSystem.issueTicket(carPT, carVehicle3, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket3.print()
parkingSystem.printStatus()
}
func testLargeParkingLot() {
//We have two parking floor
firstParkingFloor := initParkingFloor(1)
secondParkingFloor := initParkingFloor(2)
//We have two entry gates in firstParkingFloor
firstFloorEntryGate1 := initGate(1, entryGateType)
firstFloorEntryGate2 := initGate(1, entryGateType)
//We have one exit gate on firstParkingFloor
firstFloorExitGate := initGate(1, exitGateType)
parkingSystem := ParkingSystem{
parkingFloor: []*ParkingFloor{firstParkingFloor, secondParkingFloor},
entryGates: []Gate{firstFloorEntryGate1, firstFloorEntryGate2},
exitGates: []Gate{firstFloorExitGate},
issuedTickets: make(map[string]ParkingTicket),
}
//We have two car parking spots, two motorcyle parking spots, 1 truck paring spot on each of the floor
firstFloorCarParkingSpot1 := initParkingSpot(1, carPT, "A1")
firstFloorCarParkingSpot2 := initParkingSpot(1, carPT, "A2")
firstFloorMotorCycleParkingSpot1 := initParkingSpot(1, motorcyclePT, "A3")
firstFloorMotorCycleParkingSpot2 := initParkingSpot(1, motorcyclePT, "A4")
firstFloorTruckParkingSpot := initParkingSpot(1, truckPT, "A5")
//We have two car parking spots, two motorcyle parking spots, 1 truck paring spot on each of the floor
secondFloorCarParkingSpot1 := initParkingSpot(2, carPT, "B1")
secondFloorCarParkingSpot2 := initParkingSpot(2, carPT, "B2")
secondFloorMotorCycleParkingSpot1 := initParkingSpot(2, motorcyclePT, "B3")
secondFloorMotorCycleParkingSpot2 := initParkingSpot(2, motorcyclePT, "B4")
secondFloorTruckParkingSpot := initParkingSpot(2, truckPT, "B5")
//Add first floor parkings
parkingSystem.addParkingSpot(firstFloorCarParkingSpot1)
parkingSystem.addParkingSpot(firstFloorCarParkingSpot2)
parkingSystem.addParkingSpot(firstFloorMotorCycleParkingSpot1)
parkingSystem.addParkingSpot(firstFloorMotorCycleParkingSpot2)
parkingSystem.addParkingSpot(firstFloorTruckParkingSpot)
//Add second floor parkings
parkingSystem.addParkingSpot(secondFloorCarParkingSpot1)
parkingSystem.addParkingSpot(secondFloorCarParkingSpot2)
parkingSystem.addParkingSpot(secondFloorMotorCycleParkingSpot1)
parkingSystem.addParkingSpot(secondFloorMotorCycleParkingSpot2)
parkingSystem.addParkingSpot(secondFloorTruckParkingSpot)
carVehicle1 := Vehicle{
numberPlate: "C1",
vehicleType: car,
}
carVehicle2 := Vehicle{
numberPlate: "C2",
vehicleType: car,
}
motorCycleVehicle1 := Vehicle{
numberPlate: "M1",
vehicleType: motorcycle,
}
motorCycleVehicle2 := Vehicle{
numberPlate: "M2",
vehicleType: motorcycle,
}
truckVehicle1 := Vehicle{
numberPlate: "T1",
vehicleType: motorcycle,
}
parkingSystem.printStatus()
carVehicleTicket1, err := parkingSystem.issueTicket(carPT, carVehicle1, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket1.print()
carVehicleTicket2, err := parkingSystem.issueTicket(carPT, carVehicle2, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket2.print()
motorCycleVehicleTicket1, err := parkingSystem.issueTicket(motorcyclePT, motorCycleVehicle1, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
motorCycleVehicleTicket1.print()
motorCycleVehicleTicket2, err := parkingSystem.issueTicket(motorcyclePT, motorCycleVehicle2, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
motorCycleVehicleTicket2.print()
truckVehicleTicket1, err := parkingSystem.issueTicket(truckPT, truckVehicle1, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
truckVehicleTicket1.print()
parkingSystem.printStatus()
carVehicle3 := Vehicle{
numberPlate: "C3",
vehicleType: car,
}
carVehicle4 := Vehicle{
numberPlate: "C4",
vehicleType: car,
}
motorCycleVehicle3 := Vehicle{
numberPlate: "M3",
vehicleType: motorcycle,
}
motorCycleVehicle4 := Vehicle{
numberPlate: "M4",
vehicleType: motorcycle,
}
truckVehicle2 := Vehicle{
numberPlate: "T2",
vehicleType: motorcycle,
}
carVehicleTicket3, err := parkingSystem.issueTicket(carPT, carVehicle3, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket3.print()
carVehicleTicket4, err := parkingSystem.issueTicket(carPT, carVehicle4, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket4.print()
motorCycleVehicleTicket3, err := parkingSystem.issueTicket(motorcyclePT, motorCycleVehicle3, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
motorCycleVehicleTicket3.print()
motorCycleVehicleTicket4, err := parkingSystem.issueTicket(motorcyclePT, motorCycleVehicle4, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
motorCycleVehicleTicket4.print()
truckVehicleTicket2, err := parkingSystem.issueTicket(truckPT, truckVehicle2, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
truckVehicleTicket2.print()
parkingSystem.printStatus()
carVehicle5 := Vehicle{
numberPlate: "C5",
vehicleType: car,
}
carVehicleTicket5, err := parkingSystem.issueTicket(carPT, carVehicle5, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
parkingSystem.printStatus()
parkingSystem.exitVehicle(carVehicleTicket1, cash)
parkingSystem.printStatus()
carVehicleTicket5, err = parkingSystem.issueTicket(carPT, carVehicle5, firstFloorEntryGate1)
if err != nil {
fmt.Println(err)
}
carVehicleTicket5.print()
parkingSystem.printStatus()
}
Output
Printing Status of Parking Spot
Details of parking spots of type Car Parking Type on floor 1
Location = A2, parkingType = Car Parking Type, floor = 1 full =false
Location = A1, parkingType = Car Parking Type, floor = 1 full =false
Going to issue ticket for vehicle number C1
Issued ticket for vehicle number C1 at parking spot A2
Going to issue ticket for vehicle number C2
Issued ticket for vehicle number C2 at parking spot A1
Going to issue ticket for vehicle number C3
Cannot issue ticket. All Car Parking Type parking spot type are full
Printing Status of Parking Spot
Details of parking spots of type Car Parking Type on floor 1
Location = A2, parkingType = Car Parking Type, floor = 1 full =true
Location = A1, parkingType = Car Parking Type, floor = 1 full =true
Vehicle with number C1 exiting from Parking Lot
Paying price of 1$ through cash payment
Printing Status of Parking Spot
Details of parking spots of type Car Parking Type on floor 1
Location = A2, parkingType = Car Parking Type, floor = 1 full =true
Location = A1, parkingType = Car Parking Type, floor = 1 full =true
Going to issue ticket for vehicle number C3
Issued ticket for vehicle number C3 at parking spot A2
Printing Status of Parking Spot
Details of parking spots of type Car Parking Type on floor 1
Location = A1, parkingType = Car Parking Type, floor = 1 full =true
Location = A2, parkingType = Car Parking Type, floor = 1 full =true
Conclusion
This is all about designing a Parking Lot. Hope you have liked this article. Please share feedback in the comments