Created
February 6, 2025 21:40
-
-
Save moshaad7/6d31c5d7660fa4c2680572b19279f0e2 to your computer and use it in GitHub Desktop.
LLD_RIDE_SHARING
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 ( | |
"errors" | |
"fmt" | |
"log" | |
"math" | |
"math/rand" | |
"sync" | |
"time" | |
) | |
// ======================== Core Interfaces ======================== | |
type Rider interface { | |
ID() string | |
Location() Location | |
Notify(string) | |
} | |
type Driver interface { | |
ID() string | |
Location() Location | |
Status() DriverStatus | |
AssignRide(ride Ride) | |
} | |
type Ride interface { | |
ID() string | |
Status() RideStatus | |
CalculateFare() float64 | |
} | |
type PricingStrategy interface { | |
CalculateFare(Ride) float64 | |
} | |
// ======================== Base Types ======================== | |
type Location struct { | |
Lat float64 | |
Lng float64 | |
} | |
type DriverStatus int | |
type RideStatus int | |
const ( | |
Available DriverStatus = iota | |
InRide | |
Offline | |
) | |
const ( | |
Requested RideStatus = iota | |
DriverAssigned | |
InProgress | |
Completed | |
Cancelled | |
) | |
// ======================== Implementations ======================== | |
type RiderImpl struct { | |
id string | |
location Location | |
notifyCh chan string | |
} | |
func NewRider(id string, loc Location) *RiderImpl { | |
return &RiderImpl{ | |
id: id, | |
location: loc, | |
notifyCh: make(chan string, 100), | |
} | |
} | |
func (r *RiderImpl) ID() string { return r.id } | |
func (r *RiderImpl) Location() Location { return r.location } | |
func (r *RiderImpl) Notify(msg string) { | |
select { | |
case r.notifyCh <- msg: | |
default: | |
log.Println("Notification queue full for rider", r.id) | |
} | |
} | |
type DriverImpl struct { | |
id string | |
location Location | |
status DriverStatus | |
mu sync.RWMutex | |
rideCh chan Ride | |
} | |
func NewDriver(id string, loc Location) *DriverImpl { | |
return &DriverImpl{ | |
id: id, | |
location: loc, | |
status: Available, | |
rideCh: make(chan Ride, 5), | |
} | |
} | |
func (d *DriverImpl) ID() string { return d.id } | |
func (d *DriverImpl) Location() Location { return d.location } | |
func (d *DriverImpl) Status() DriverStatus { | |
d.mu.RLock() | |
defer d.mu.RUnlock() | |
return d.status | |
} | |
func (d *DriverImpl) AssignRide(ride Ride) { | |
d.mu.Lock() | |
defer d.mu.Unlock() | |
if d.status == Available { | |
d.status = InRide | |
d.rideCh <- ride | |
} | |
} | |
// ======================== Ride Implementation ======================== | |
type RideImpl struct { | |
id string | |
rider Rider | |
driver Driver | |
startLocation Location | |
endLocation Location | |
status RideStatus | |
startTime time.Time | |
endTime time.Time | |
pricing PricingStrategy | |
mu sync.RWMutex | |
} | |
func NewRide(id string, rider Rider, start, end Location, strategy PricingStrategy) *RideImpl { | |
return &RideImpl{ | |
id: id, | |
rider: rider, | |
startLocation: start, | |
endLocation: end, | |
status: Requested, | |
pricing: strategy, | |
} | |
} | |
func (r *RideImpl) ID() string { return r.id } | |
func (r *RideImpl) Status() RideStatus { | |
r.mu.RLock() | |
defer r.mu.RUnlock() | |
return r.status | |
} | |
func (r *RideImpl) CalculateFare() float64 { | |
return r.pricing.CalculateFare(r) | |
} | |
// ======================== Pricing Strategies ======================== | |
type StandardPricing struct { | |
baseFare float64 | |
perMileRate float64 | |
perMinuteRate float64 | |
} | |
func (s *StandardPricing) CalculateFare(ride Ride) float64 { | |
r := ride.(*RideImpl) | |
duration := r.endTime.Sub(r.startTime).Minutes() | |
distance := calculateDistance(r.startLocation, r.endLocation) | |
return s.baseFare + (distance * s.perMileRate) + (duration * s.perMinuteRate) | |
} | |
type SurgePricing struct { | |
*StandardPricing | |
multiplier float64 | |
} | |
func (s *SurgePricing) CalculateFare(ride Ride) float64 { | |
return s.StandardPricing.CalculateFare(ride) * s.multiplier | |
} | |
// ======================== Ride Matching Service ======================== | |
type RideMatcher struct { | |
drivers map[string]Driver | |
rideRequests chan *RideImpl | |
mu sync.RWMutex | |
strategy MatchingStrategy | |
} | |
type MatchingStrategy interface { | |
MatchDriver(ride *RideImpl, drivers []Driver) (Driver, error) | |
} | |
func NewRideMatcher(strategy MatchingStrategy) *RideMatcher { | |
return &RideMatcher{ | |
drivers: make(map[string]Driver), | |
rideRequests: make(chan *RideImpl, 1000), | |
strategy: strategy, | |
} | |
} | |
func (rm *RideMatcher) AddDriver(driver Driver) { | |
rm.mu.Lock() | |
defer rm.mu.Unlock() | |
rm.drivers[driver.ID()] = driver | |
} | |
func (rm *RideMatcher) Start() { | |
go func() { | |
for ride := range rm.rideRequests { | |
rm.mu.RLock() | |
drivers := make([]Driver, 0, len(rm.drivers)) | |
for _, d := range rm.drivers { | |
if d.Status() == Available { | |
drivers = append(drivers, d) | |
} | |
} | |
rm.mu.RUnlock() | |
if driver, err := rm.strategy.MatchDriver(ride, drivers); err == nil { | |
driver.AssignRide(ride) | |
ride.driver = driver | |
ride.rider.Notify(fmt.Sprintf("Driver %s accepted your ride", driver.ID())) | |
} else { | |
ride.rider.Notify("No drivers available") | |
} | |
} | |
}() | |
} | |
// ======================== Matching Strategies ======================== | |
type NearestDriverStrategy struct{} | |
func (n *NearestDriverStrategy) MatchDriver(ride *RideImpl, drivers []Driver) (Driver, error) { | |
var nearestDriver Driver | |
minDistance := math.MaxFloat64 | |
for _, d := range drivers { | |
dist := calculateDistance(ride.rider.Location(), d.Location()) | |
if dist < minDistance { | |
minDistance = dist | |
nearestDriver = d | |
} | |
} | |
if nearestDriver == nil { | |
return nil, errors.New("no available drivers") | |
} | |
return nearestDriver, nil | |
} | |
// ======================== Helper Functions ======================== | |
func calculateDistance(loc1, loc2 Location) float64 { | |
// Simplified haversine calculation | |
latDiff := loc2.Lat - loc1.Lat | |
lngDiff := loc2.Lng - loc1.Lng | |
return math.Sqrt(latDiff*latDiff + lngDiff*lngDiff) | |
} | |
// ======================== Main Service ======================== | |
type RideSharingService struct { | |
matcher *RideMatcher | |
rides map[string]*RideImpl | |
mu sync.RWMutex | |
pricing PricingStrategy | |
} | |
func NewRideSharingService() *RideSharingService { | |
strategy := &NearestDriverStrategy{} | |
matcher := NewRideMatcher(strategy) | |
matcher.Start() | |
return &RideSharingService{ | |
matcher: matcher, | |
rides: make(map[string]*RideImpl), | |
pricing: &StandardPricing{ | |
baseFare: 2.50, | |
perMileRate: 1.75, | |
perMinuteRate: 0.25, | |
}, | |
} | |
} | |
func (s *RideSharingService) RequestRide(rider Rider, start, end Location) (Ride, error) { | |
ride := NewRide(generateID(), rider, start, end, s.pricing) | |
s.mu.Lock() | |
s.rides[ride.ID()] = ride | |
s.mu.Unlock() | |
s.matcher.rideRequests <- ride | |
return ride, nil | |
} | |
func generateID() string { | |
return fmt.Sprintf("%d", rand.Intn(1000000)) | |
} | |
// ======================== Main Execution ======================== | |
func main() { | |
service := NewRideSharingService() | |
// Create riders and drivers | |
rider1 := NewRider("rider-1", Location{37.7749, -122.4194}) | |
driver1 := NewDriver("driver-1", Location{37.7755, -122.4182}) | |
driver2 := NewDriver("driver-2", Location{37.7760, -122.4175}) | |
// Add drivers to the system | |
service.matcher.AddDriver(driver1) | |
service.matcher.AddDriver(driver2) | |
// Simulate ride request | |
ride, _ := service.RequestRide(rider1, | |
Location{37.7749, -122.4194}, | |
Location{37.7850, -122.4003}, | |
) | |
// Wait for matching | |
time.Sleep(1 * time.Second) | |
// Check ride status | |
if ride.Status() >= DriverAssigned { | |
fmt.Printf("Ride %s matched with driver %s\n", | |
ride.ID(), | |
ride.(*RideImpl).driver.ID(), | |
) | |
} | |
// Simulate ride completion | |
ride.(*RideImpl).status = Completed | |
fmt.Printf("Total fare: $%.2f\n", ride.CalculateFare()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment