├── .gitignore ├── README.md ├── atm-machine ├── atm │ ├── Account.go │ ├── atm.go │ ├── atm_state.go │ ├── card_reader.go │ ├── insert_card.go │ ├── insert_pin.go │ ├── read_card.go │ ├── select_account.go │ ├── withdraw.go │ └── withdrawAmount.go ├── go.mod ├── main.go └── model │ └── atm_card.go ├── elevator_system ├── building.go ├── elevator.go ├── elevatorManager.go ├── elevator_panel.go ├── floor.go ├── go.mod ├── hall_panel.go └── main.go ├── library-management_system ├── book.go ├── book_item.go ├── go.mod ├── library.go ├── main.go └── member.go ├── meeting_scheduler ├── MeetingScheduler.go ├── calender.go ├── go.mod ├── go.sum ├── main.go ├── meeting.go ├── room.go └── user.go ├── parking_lot ├── floor.go ├── go.mod ├── lld_go_parking_lot ├── main.go ├── parking_lot.go ├── parking_spot.go ├── parking_ticket.go ├── payment.go └── vehicles │ └── vehicle.go ├── social_media_platform ├── activity_manager.go ├── comment.go ├── go.mod ├── main.go ├── notification.go ├── notification_manager.go ├── post.go ├── post_manager.go ├── user.go └── user_manager.go └── vending_machine ├── go.mod ├── inventory.go ├── main.go ├── product.go ├── state.go └── vending_machine.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore macOS system files 2 | .DS_Store 3 | 4 | # Ignore IDE project settings (JetBrains, etc.) 5 | .idea 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Low-Level System Design in Go 3 | 4 | Welcome to the **Low-Level System Design in Go** repository! This repository contains various low-level system design problems and their solutions implemented in Go. The primary aim is to demonstrate the design and architecture of systems through practical examples. 5 | 6 | ## Table of Contents 7 | 8 | - [Overview](#overview) 9 | - [Parking Lot System](#parking-lot-system) 10 | - [Elevator System](#elevator-system) 11 | - [Library Management System](#library-management-system) 12 | - [Vending Machine System](#vending-machine-system) 13 | - [Social Media Platform](#social-media-platform) 14 | - [Meeting Scheduler](#meeting-scheduler) 15 | - [Contributing Guidelines](#contributing-guidelines) 16 | 17 | ## Overview 18 | 19 | Low-level system design involves understanding the core concepts of system architecture and designing scalable, maintainable, and efficient systems. This repository will try to cover solutions of various problems and scenarios using Go. 20 | 21 | ## Parking Lot System 22 | 23 | The first project in this repository is a **Parking Lot System**. This system simulates a parking lot where vehicles can be parked and unparked. It demonstrates: 24 | 25 | - Singleton design pattern for managing the parking lot instance. 26 | - Handling different types of vehicles (e.g., cars, trucks). 27 | - Parking space management across multiple floors. 28 | - Payment processing for parked vehicles. 29 | 30 | ### Features 31 | 32 | - Add and remove vehicles from the parking lot. 33 | - Display the availability of parking spots. 34 | - Calculate parking charges based on the duration of stay. 35 | 36 | ## Elevator System 37 | 38 | The second project in this repository is an Elevator System. This system simulates an elevator control system in a multi-floor building. It demonstrates: 39 | 40 | - Multi-Elevator Management: Manages multiple elevators within a single building. 41 | - Request Handling: Processes floor requests and assigns the most suitable elevator based on proximity and current direction. 42 | - Direction Control: Dynamically changes elevator direction based on requests. 43 | - Concurrent Operations: Uses Go routines and synchronization to handle multiple elevator requests simultaneously. 44 | 45 | ### Features 46 | 47 | - Request an elevator from any floor, specifying the desired direction (up or down). 48 | - Assign the nearest elevator to respond to requests. 49 | - Manage elevator movement and optimize direction changes based on requests and destinations. 50 | 51 | ## Library Management System 52 | 53 | The third project in this repository is a Library Management System. This system simulates a library where members can borrow and return books. It demonstrates: 54 | 55 | - Singleton design pattern for managing the library instance. 56 | - Book management with support for multiple copies of each book. 57 | - Member management, including borrowing history and borrowing limits. 58 | - Concurrency control to handle multiple borrow and return requests simultaneously. 59 | 60 | ### Features 61 | 62 | - Add and remove books from the library collection. 63 | - Allow members to borrow and return books, with automatic status updates. 64 | - Check book availability and member borrowing quoto limit. 65 | - Maintain a history of borrowed books for each member. 66 | 67 | ## Vending Machine System 68 | 69 | The fourth project in this repository is a Vending Machine System. This system simulates a vending machine that accepts payments, dispenses products, and manages inventory. It demonstrates: 70 | 71 | - State Management: Uses the state pattern to manage vending machine states (e.g., waiting for money, product selection, dispensing). 72 | - Inventory Management: Keeps track of product stock and availability. 73 | - Transaction Processing: Handles money insertion, change return, and product dispensing. 74 | 75 | ### Features 76 | - Insert money and select a product for purchase. 77 | - Dispense product if sufficient funds are provided, or return change if funds are insufficient. 78 | - Automatically update inventory levels upon each transaction. 79 | - Handle various states in the vending machine’s workflow, including error handling for out-of-stock items or insufficient funds. 80 | 81 | ## Social Media Platform 82 | 83 | The fifth project in this repository is a **Social Media Platform**. This system simulates a basic social media platform where users can connect, create posts, interact with posts, and view activity feeds. It demonstrates: 84 | 85 | - **Facade Pattern**: Simplifies user interaction with various social media functionalities (e.g., managing users, posts, friendships) through a unified interface. 86 | - **User and Post Management**: Allows the creation and management of users, posts, comments, and likes, encapsulated in dedicated manager classes. 87 | - **Concurrency Control**: Manages concurrent operations such as posting, commenting, and friend requests using synchronization techniques. 88 | 89 | ### Features 90 | 91 | - **User Registration and Profile Management**: Users can register on the platform with profile details, add a profile bio, and edit their information. 92 | - **Posting and Feed System**: Users can create posts and see posts from their friends in their feed. 93 | - **Friendship System**: Users can send and accept friend requests, and posts from friends are included in their feeds. 94 | - **Post Interactions**: Users can like and comment on posts from other users. The number of likes and all comments are viewable on each post. 95 | - **Feed Management**: Users can view a personalized feed of posts from their friends, sorted to display recent posts first. 96 | 97 | ### Implementation Highlights 98 | 99 | - **Concurrency with RWMutex**: Manages concurrent reads and writes to shared data (such as posts and users) to avoid race conditions. 100 | - **Error Handling**: Provides informative error messages for actions such as attempting to interact with nonexistent users or posts. 101 | - **Encapsulation of Features**: Each feature (users, posts, friendships) is encapsulated in a separate manager class, promoting modularity and ease of maintenance. 102 | 103 | 104 | ## Meeting Scheduler 105 | 106 | The sixth project in this repository is a Meeting Scheduler, a system that simulates meeting scheduling for users. It demonstrates the following concepts: 107 | 108 | - Observer pattern for notifying users about scheduled meetings. 109 | - Book meeting for muliple users in particular time slot, so that room slot does not collide with each other. 110 | - Room calendar management, tracking when a room is booked or available. 111 | - Concurrency control, preventing scheduling conflicts in real time. 112 | 113 | ## ATM 114 | 115 | The seventh project in this repository is a ATM machine, a system that simulates ATM. It demonstrates the following concepts: 116 | 117 | - State design pattern for different state of ATM. 118 | - Chain of responsibility principle for withdrawing cash. 119 | - Template pattern for handling error of each place in one place. 120 | - Concurrency control, preventing atm to conflict. 121 | 122 | ### Features 123 | 124 | - Book and cancel meetings seamlessly. 125 | - Prevent double booking, allowing reservations only if the time slot is available. 126 | - Capacity-based booking, ensuring a room can only be booked if it has enough seats for all attendees. 127 | - Automated notifications to inform users about meeting bookings or cancellations. 128 | - Fetching all free rooms for a specific time slot. 129 | 130 | ## Contributing Guidelines 131 | Contributions to expand this repository with more low-level system design problems and solutions are welcomed. Here’s how you can contribute: 132 | 133 | ### Steps to Contribute 134 | 135 | - Choose a System Design Problem. 136 | - Select a problem to implement, feel free to propose it in an issue. 137 | - Open a new issue in the repository to let maintainers know about the system design problem you plan to add. Provide a brief overview of the problem and your approach. 138 | - Follow Repository Standards. Use Go for the implementation. Follow the project structure and coding style used in the repository. 139 | - Include a clear README section for the problem with the following details: problem description, key features, design patterns used, concurrency or error handling highlights (if any) 140 | - Fork the repository, make your changes, and submit a pull request. 141 | - Ensure your code is clean, well-documented, and tested. 142 | - Your pull request will be reviewed by the maintainer. After addressing feedback, it will be merged into the repository. 143 | 144 | ### Contribution Tips 145 | - Break the problem into smaller components and implement them incrementally. 146 | - Document your solution clearly to help others understand your implementation. 147 | 148 | Thank you for contributing to Low-Level System Design in Go! 149 | -------------------------------------------------------------------------------- /atm-machine/atm/Account.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | import "errors" 4 | 5 | type Account interface { 6 | GetBalance() float64 7 | } 8 | 9 | type AccountType string 10 | 11 | const ( 12 | Saving AccountType = "SAVING" 13 | Current AccountType = "CURRENT" 14 | ) 15 | 16 | func AccountFactory(accType AccountType) (Account, error) { 17 | switch accType { 18 | case Current: 19 | return &CurrentAccount{}, nil 20 | case Saving: 21 | return &SavingAccount{}, nil 22 | default: 23 | return nil, errors.New("Not a valid account type") 24 | } 25 | } 26 | 27 | type BaseAccount struct { 28 | acctNo string 29 | balance float64 30 | } 31 | 32 | func (b *BaseAccount) GetBalance() float64 { 33 | return b.balance 34 | } 35 | 36 | type SavingAccount struct { 37 | BaseAccount 38 | } 39 | 40 | type CurrentAccount struct { 41 | BaseAccount 42 | } 43 | -------------------------------------------------------------------------------- /atm-machine/atm/atm.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | import ( 4 | "atm-machine/model" 5 | "fmt" 6 | "sync" 7 | ) 8 | 9 | type WithdrawNote struct { 10 | FiveHundred int 11 | Hundred int 12 | Left int 13 | } 14 | 15 | type ATM struct { 16 | countOfNotes map[string]int 17 | card model.Card 18 | account Account 19 | uiOption []string 20 | WithdrawAs *WithdrawNote 21 | insertPin ATMState 22 | insertCard ATMState 23 | readCard ATMState 24 | selectAccount ATMState 25 | dispenserAmount ATMState 26 | currentState ATMState 27 | mu sync.RWMutex 28 | } 29 | 30 | func (a *ATM) SetState(s ATMState) { 31 | a.currentState = s 32 | } 33 | 34 | func (a *ATM) CleanPreviousAtmTransaction() { 35 | a.mu.Lock() 36 | defer a.mu.Unlock() 37 | a.WithdrawAs = &WithdrawNote{} 38 | a.card = model.Card{} 39 | a.account = nil 40 | } 41 | 42 | func (a *ATM) PrintMoney() { 43 | a.mu.Lock() 44 | defer a.mu.Unlock() 45 | fmt.Printf("\n500 note present:%d, 100 not present:%d", a.countOfNotes["500"], a.countOfNotes["100"]) 46 | } 47 | 48 | func (a *ATM) StateName() string { 49 | a.mu.Lock() 50 | defer a.mu.Unlock() 51 | return a.currentState.StateName() 52 | } 53 | 54 | func (a *ATM) InsertCard() error { 55 | a.mu.Lock() 56 | defer a.mu.Unlock() 57 | return a.currentState.InsertCard() 58 | } 59 | 60 | func (a *ATM) GetCardDetail() error { 61 | a.mu.Lock() 62 | defer a.mu.Unlock() 63 | return a.currentState.GetCardDetail() 64 | } 65 | 66 | func (a *ATM) InsertPin() error { 67 | a.mu.Lock() 68 | defer a.mu.Unlock() 69 | return a.currentState.InsertPin() 70 | } 71 | 72 | func (a *ATM) DispenserAmount() error { 73 | a.mu.Lock() 74 | if err := a.currentState.DispenserAmount(); err != nil { 75 | return err 76 | } 77 | 78 | a.mu.Unlock() 79 | 80 | a.CleanPreviousAtmTransaction() 81 | 82 | return nil 83 | } 84 | 85 | func (a *ATM) SelectAccount() error { 86 | a.mu.Lock() 87 | defer a.mu.Unlock() 88 | return a.currentState.SelectAccount() 89 | } 90 | 91 | func (a *ATM) Execute(operation func() error) { 92 | err := operation() 93 | if err != nil { 94 | a.CleanPreviousAtmTransaction() 95 | a.SetState(a.insertCard) 96 | fmt.Println("All operation will be nil operation:") 97 | fmt.Println("Error while operation:", err.Error()) 98 | } 99 | } 100 | 101 | func NewATM() *ATM { 102 | atm := &ATM{ 103 | countOfNotes: map[string]int{ 104 | "500": 1000, 105 | "200": 2000, 106 | "100": 1000, 107 | }, 108 | WithdrawAs: &WithdrawNote{}, 109 | } 110 | 111 | atm.insertCard = &InsertCard{ 112 | atm: atm, 113 | } 114 | 115 | atm.readCard = &ReadCard{ 116 | atm: atm, 117 | } 118 | 119 | atm.insertPin = &InsertPin{ 120 | atm: atm, 121 | } 122 | 123 | atm.selectAccount = &SelectAccount{ 124 | atm: atm, 125 | } 126 | 127 | atm.dispenserAmount = &DispenserAmount{ 128 | atm: atm, 129 | } 130 | 131 | atm.SetState(atm.insertCard) 132 | 133 | return atm 134 | } 135 | -------------------------------------------------------------------------------- /atm-machine/atm/atm_state.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | type ATMState interface { 4 | StateName() string 5 | InsertCard() error 6 | InsertPin() error 7 | SelectAccount() error 8 | GetCardDetail() error 9 | DispenserAmount() error 10 | } 11 | 12 | type ATMAbstract struct{} 13 | 14 | func (s *ATMAbstract) InsertCard() error { 15 | return nil 16 | } 17 | 18 | func (s *ATMAbstract) InsertPin() error { 19 | return nil 20 | } 21 | 22 | func (s *ATMAbstract) AuthticateCard() error { 23 | return nil 24 | } 25 | 26 | func (s *ATMAbstract) DispenserAmount() error { 27 | return nil 28 | } 29 | 30 | func (s *ATMAbstract) SelectAccount() error { 31 | return nil 32 | 33 | } 34 | 35 | func (s *ATMAbstract) GetCardDetail() error { 36 | return nil 37 | 38 | } 39 | 40 | func (s *ATMAbstract) StateName() string { 41 | return "ATMAbstract" 42 | } 43 | -------------------------------------------------------------------------------- /atm-machine/atm/card_reader.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | import "atm-machine/model" 4 | 5 | const ( 6 | InvalidCard = "invalid" 7 | ) 8 | 9 | // This will be actuall implementation of card reader // i am returning a card from here 10 | type CardReader struct { 11 | } 12 | 13 | func NewCardReader() CardReader { 14 | return CardReader{} 15 | } 16 | 17 | // for now it will return some dummy card depend of input for acting as multiple car 18 | func (c CardReader) ReadCard(cardType string) model.Card { 19 | switch cardType { 20 | case InvalidCard: 21 | return model.Card{ 22 | Status: model.NoCard, 23 | } 24 | default: 25 | return model.Card{ 26 | BankName: "hdfc", 27 | CardNo: "34567-sd55498459", 28 | AccountNo: "bshy4859-sdhhj66", 29 | Status: model.Active, 30 | UserName: "Himanshu sharma"} 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /atm-machine/atm/insert_card.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | type InsertCard struct { 4 | atm *ATM 5 | ATMAbstract 6 | } 7 | 8 | func (d *InsertCard) InsertCard() error { 9 | d.atm.SetState(d.atm.readCard) 10 | return nil 11 | } 12 | 13 | func (d *InsertCard) StateName() string { 14 | return "InsertCard" 15 | } 16 | -------------------------------------------------------------------------------- /atm-machine/atm/insert_pin.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type InsertPin struct { 9 | atm *ATM 10 | ATMAbstract 11 | } 12 | 13 | func (s *InsertPin) InsertPin() error { 14 | // logic to valid pin, we can call api client inside it, i will return sucess and error depend on input 15 | fmt.Println("Enter 4 letter pin for card:\n") 16 | 17 | var pin string 18 | 19 | fmt.Scanf("%s", &pin) 20 | 21 | if pin == "1111" { 22 | s.atm.SetState(s.atm.selectAccount) 23 | return nil 24 | } else { 25 | return errors.New("Pin is not valid") 26 | } 27 | } 28 | 29 | func (d *InsertPin) StateName() string { 30 | return "InsetPin" 31 | } 32 | -------------------------------------------------------------------------------- /atm-machine/atm/read_card.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | type ReadCard struct { 4 | atm *ATM 5 | ATMAbstract 6 | } 7 | 8 | func (d *ReadCard) GetCardDetail() error { 9 | card := NewCardReader().ReadCard("valid") 10 | if card.CardNo == "" { 11 | d.atm.SetState(d.atm.insertCard) 12 | } 13 | d.atm.card = card 14 | d.atm.SetState(d.atm.insertPin) 15 | return nil 16 | } 17 | 18 | func (d *ReadCard) StateName() string { 19 | return "ReadCard" 20 | } 21 | -------------------------------------------------------------------------------- /atm-machine/atm/select_account.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type SelectAccount struct { 8 | atm *ATM 9 | ATMAbstract 10 | } 11 | 12 | func (s *SelectAccount) SelectAccount() error { 13 | 14 | var accountType string 15 | var err error 16 | 17 | fmt.Println("Select account type SAVING OR CURRENT:\n") 18 | fmt.Scanf("%s", &accountType) 19 | 20 | if s.atm.account, err = AccountFactory(AccountType(accountType)); err != nil { 21 | return err 22 | } 23 | 24 | s.atm.SetState(s.atm.dispenserAmount) 25 | 26 | return nil 27 | 28 | } 29 | 30 | func (s *SelectAccount) StateName() string { 31 | return "SelectAccount" 32 | } 33 | -------------------------------------------------------------------------------- /atm-machine/atm/withdraw.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | type Withdraw interface { 4 | ProcessAmount(atm *ATM, atmamount float64) 5 | } 6 | 7 | func NewWithDrawPipeline() Withdraw { 8 | return fiveHundredWithdraw{ 9 | next: oneHundredWithdraw{}, 10 | } 11 | } 12 | 13 | type fiveHundredWithdraw struct { 14 | next Withdraw 15 | } 16 | 17 | func (f fiveHundredWithdraw) ProcessAmount(atm *ATM, atmamount float64) { 18 | div := atmamount / 500 19 | rem := int(atmamount) % 500 20 | atm.WithdrawAs.FiveHundred = int(div) 21 | f.next.ProcessAmount(atm, float64(rem)) 22 | } 23 | 24 | type oneHundredWithdraw struct { 25 | next Withdraw 26 | } 27 | 28 | func (h oneHundredWithdraw) ProcessAmount(atm *ATM, atmamount float64) { 29 | div := atmamount / 100 30 | rem := int(atmamount) % 100 31 | atm.WithdrawAs.Hundred = int(div) 32 | atm.WithdrawAs.Left = rem 33 | 34 | } 35 | -------------------------------------------------------------------------------- /atm-machine/atm/withdrawAmount.go: -------------------------------------------------------------------------------- 1 | package atm 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type DispenserAmount struct { 9 | atm *ATM 10 | ATMAbstract 11 | } 12 | 13 | func (w *DispenserAmount) DispenserAmount() error { 14 | var amount float64 15 | 16 | fmt.Println("\n Enter amount to withdraw from atm:") 17 | fmt.Scanf("%f", &amount) 18 | // w.atm.amount = amount 19 | out := NewWithDrawPipeline() 20 | 21 | out.ProcessAmount(w.atm, amount) // this process the amount 22 | 23 | if w.atm.WithdrawAs.Left > 0 { 24 | return errors.New("This amount is not divisible of 100") 25 | } else { 26 | if w.atm.countOfNotes["500"] >= w.atm.WithdrawAs.FiveHundred { 27 | w.atm.countOfNotes["500"] = w.atm.countOfNotes["500"] - w.atm.WithdrawAs.FiveHundred 28 | } else { 29 | return errors.New("Bank don't have enough fund 500") 30 | } 31 | 32 | if w.atm.countOfNotes["100"] >= w.atm.WithdrawAs.Hundred { 33 | w.atm.countOfNotes["100"] = w.atm.countOfNotes["100"] - w.atm.WithdrawAs.Hundred 34 | } else { 35 | return errors.New("Bank don't have enough fund 100") 36 | } 37 | } 38 | 39 | w.atm.SetState(w.atm.insertCard) 40 | 41 | return nil 42 | } 43 | 44 | func (w *DispenserAmount) StateName() string { 45 | return "DispenserAmount" 46 | } 47 | -------------------------------------------------------------------------------- /atm-machine/go.mod: -------------------------------------------------------------------------------- 1 | module atm-machine 2 | 3 | go 1.23.5 4 | -------------------------------------------------------------------------------- /atm-machine/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "atm-machine/atm" 5 | "fmt" 6 | ) 7 | 8 | // flow of ATM 9 | // insertCard state -> readCard state -> insertPin state -> selectAccount state -> withdrawAmount state 10 | func main() { 11 | atm := atm.NewATM() 12 | 13 | for { 14 | 15 | var runProcess string 16 | 17 | fmt.Printf("\nATM state:%s\n", atm.StateName()) 18 | fmt.Println("Input do you want to proceed, press n to exit") 19 | fmt.Scanf("%s", &runProcess) 20 | 21 | fmt.Println(runProcess) 22 | if runProcess == "n" { 23 | return 24 | } 25 | // 26 | fmt.Printf("\nATM state:%s\n", atm.StateName()) 27 | // insert card 28 | atm.Execute(atm.InsertCard) 29 | 30 | fmt.Printf("\nATM state:%s\n", atm.StateName()) 31 | 32 | atm.Execute(atm.GetCardDetail) 33 | 34 | fmt.Printf("\nATM state:%s\n", atm.StateName()) 35 | 36 | atm.Execute(atm.InsertPin) 37 | 38 | fmt.Printf("\nATM state:%s\n", atm.StateName()) 39 | // insert card 40 | atm.Execute(atm.SelectAccount) 41 | 42 | fmt.Printf("\nATM state:%s", atm.StateName()) 43 | 44 | atm.PrintMoney() 45 | // insert card 46 | atm.Execute(atm.DispenserAmount) 47 | 48 | atm.PrintMoney() 49 | 50 | fmt.Printf("\nATM state:%s", atm.StateName()) 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /atm-machine/model/atm_card.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type cardStatus string 4 | 5 | const ( 6 | Active cardStatus = "ACTIVE" 7 | Expiry cardStatus = "EXPIRE" 8 | Blocked cardStatus = "BLOCKED" 9 | NoCard cardStatus = "NOCARD" 10 | ) 11 | 12 | type Card struct { 13 | BankName string 14 | CardNo string 15 | AccountNo string 16 | Status cardStatus 17 | UserName string 18 | } 19 | -------------------------------------------------------------------------------- /elevator_system/building.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Building struct { 4 | Floors []*Floor 5 | Elevators []*Elevator 6 | } 7 | 8 | func NewBuilding() *Building { 9 | building := &Building{Floors: make([]*Floor, 0)} 10 | 11 | for i := 1; i <= 15; i++ { 12 | floor := NewFloor(i) 13 | building.Floors = append(building.Floors, floor) 14 | } 15 | 16 | for i := 1; i <= 3; i++ { 17 | elevator := NewElevator(i) 18 | building.Elevators = append(building.Elevators, elevator) 19 | } 20 | 21 | return building 22 | } 23 | -------------------------------------------------------------------------------- /elevator_system/elevator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type Elevator struct { 9 | ID int 10 | Capacity int 11 | CurrentFloor int 12 | CurrentDirection Directions 13 | CurrentLoad int 14 | ElevatorPanel *ElevatorPanel 15 | Destinations []int 16 | sync.Mutex 17 | } 18 | 19 | func NewElevator(id int) *Elevator { 20 | return &Elevator{ID: id, Capacity: 10, CurrentFloor: 1, CurrentDirection: Still, CurrentLoad: 0, ElevatorPanel: NewElevatorPanel(id)} 21 | } 22 | 23 | func (e *Elevator) AddDestination(destinationFloor int) { 24 | e.Lock() 25 | e.ElevatorPanel.AddDestinationFloor(destinationFloor) 26 | e.Destinations = append(e.Destinations, destinationFloor) 27 | fmt.Printf("Elevator %d received destination floor %d\n", e.ID, destinationFloor) 28 | e.Unlock() 29 | } 30 | 31 | func (e *Elevator) RemoveDestination(destinationFloor int) { 32 | e.Lock() 33 | for i, floor := range e.Destinations { 34 | if floor == destinationFloor { 35 | e.Destinations = append(e.Destinations[:i], e.Destinations[i+1:]...) 36 | e.ElevatorPanel.RemoveDestinationFloor(destinationFloor) 37 | break 38 | } 39 | } 40 | e.Unlock() 41 | } 42 | 43 | func (e *Elevator) UpdateCurrentFloor(newFloor int) { 44 | e.Lock() 45 | e.CurrentFloor = newFloor 46 | e.Unlock() 47 | } 48 | 49 | func (e *Elevator) UpdateCurrentLoad(newLoad int) { 50 | e.Lock() 51 | e.CurrentLoad = newLoad 52 | e.Unlock() 53 | } 54 | 55 | func (e *Elevator) UpdateCurrentDirection(newDirection Directions) { 56 | e.Lock() 57 | e.CurrentDirection = newDirection 58 | e.Unlock() 59 | } 60 | 61 | func (e *Elevator) FarthestDestination() int { 62 | maxFloor := 0 63 | 64 | for _, floor := range e.Destinations { 65 | if floor > maxFloor { 66 | maxFloor = floor 67 | } 68 | } 69 | 70 | return maxFloor 71 | } 72 | 73 | func (e *Elevator) NearestDestination() int { 74 | minFloor := 100 75 | 76 | for _, floor := range e.Destinations { 77 | if floor < minFloor { 78 | minFloor = floor 79 | } 80 | } 81 | 82 | return minFloor 83 | } 84 | -------------------------------------------------------------------------------- /elevator_system/elevatorManager.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | type ElevatorManager struct { 9 | Building *Building 10 | } 11 | 12 | func NewElevatorManager(building *Building) *ElevatorManager { 13 | return &ElevatorManager{Building: building} 14 | } 15 | 16 | func (em *ElevatorManager) OperateAllElevators() { 17 | for _, elevator := range em.Building.Elevators { 18 | go em.OperateElevator(elevator) 19 | } 20 | } 21 | 22 | func (em *ElevatorManager) OperateElevator(elevator *Elevator) { 23 | for { 24 | elevator.Lock() 25 | if len(elevator.Destinations) == 0 { 26 | elevator.CurrentDirection = Still 27 | elevator.Unlock() 28 | continue 29 | } 30 | 31 | sort.Ints(elevator.Destinations) 32 | fmt.Printf("Elevator %d is starting from %d and going to %s\n", elevator.ID, elevator.CurrentFloor, elevator.CurrentDirection) 33 | 34 | if elevator.CurrentDirection == Up { 35 | em.MoveElevatorUp(elevator) 36 | } else if elevator.CurrentDirection == Down { 37 | em.MoveElevatorDown(elevator) 38 | } else { 39 | em.DecideDirection(elevator) 40 | } 41 | elevator.Unlock() 42 | } 43 | } 44 | 45 | func (em *ElevatorManager) DecideDirection(elevator *Elevator) { 46 | currentFloor := elevator.CurrentFloor 47 | if len(elevator.Destinations) == 0 { 48 | return 49 | } 50 | 51 | nearestDestination := elevator.Destinations[0] 52 | if nearestDestination > currentFloor { 53 | elevator.UpdateCurrentDirection(Up) 54 | em.MoveElevatorUp(elevator) 55 | } else { 56 | elevator.UpdateCurrentDirection(Down) 57 | em.MoveElevatorDown(elevator) 58 | } 59 | } 60 | 61 | func (em *ElevatorManager) MoveElevatorUp(elevator *Elevator) { 62 | for i := 0; i < len(elevator.Destinations); i++ { 63 | destination := elevator.Destinations[i] 64 | if destination >= elevator.CurrentFloor { 65 | fmt.Printf("Elevator %d moving up to floor %d\n", elevator.ID, destination) 66 | elevator.UpdateCurrentFloor(destination) 67 | elevator.RemoveDestination(destination) 68 | i-- 69 | } 70 | } 71 | 72 | if len(elevator.Destinations) == 0 { 73 | elevator.UpdateCurrentDirection(Still) 74 | } else { 75 | elevator.UpdateCurrentDirection(Down) 76 | } 77 | } 78 | 79 | func (em *ElevatorManager) MoveElevatorDown(elevator *Elevator) { 80 | for i := len(elevator.Destinations) - 1; i >= 0; i-- { 81 | destination := elevator.Destinations[i] 82 | if destination <= elevator.CurrentFloor { 83 | fmt.Printf("Elevator %d moving down to floor %d\n", elevator.ID, destination) 84 | elevator.UpdateCurrentFloor(destination) 85 | elevator.RemoveDestination(destination) 86 | } 87 | } 88 | 89 | if len(elevator.Destinations) == 0 { 90 | elevator.UpdateCurrentDirection(Still) 91 | } else { 92 | elevator.UpdateCurrentDirection(Up) 93 | } 94 | } 95 | 96 | func (em *ElevatorManager) AssignElevator(floor int, direction Directions) (bestElevator *Elevator) { 97 | bestElevator = em.FindClosestElevator(floor, direction) 98 | if bestElevator != nil { 99 | bestElevator.AddDestination(floor) 100 | fmt.Printf("Elevator %d assigned to floor %d with direction %s\n", bestElevator.ID, floor, direction) 101 | } 102 | return bestElevator 103 | } 104 | 105 | func (em *ElevatorManager) FindClosestElevator(floor int, direction Directions) *Elevator { 106 | var closestElevator *Elevator 107 | minDistance := int(1e9) 108 | 109 | for _, elevator := range em.Building.Elevators { 110 | elevator.Lock() 111 | distance := em.calculateDistance(elevator, floor, direction) 112 | 113 | if distance < minDistance { 114 | minDistance = distance 115 | closestElevator = elevator 116 | } 117 | 118 | elevator.Unlock() 119 | } 120 | return closestElevator 121 | } 122 | 123 | func (em *ElevatorManager) calculateDistance(elevator *Elevator, floor int, direction Directions) int { 124 | currentFloor := elevator.CurrentFloor 125 | currentDirection := elevator.CurrentDirection 126 | 127 | if currentDirection == Still || (currentDirection == direction && ((direction == Up && floor > currentFloor) || (direction == Down && floor < currentFloor))) { 128 | return abs(floor - currentFloor) 129 | } 130 | 131 | if (currentDirection == Up && direction == Down) || (currentDirection == Down && direction == Up) { 132 | if currentDirection == Up { 133 | return abs(elevator.FarthestDestination()-currentFloor) + abs(elevator.FarthestDestination()-floor) 134 | } else { 135 | return abs(elevator.NearestDestination()-currentFloor) + abs(elevator.NearestDestination()-floor) 136 | } 137 | } 138 | 139 | return 100 140 | } 141 | 142 | func abs(x int) int { 143 | if x < 0 { 144 | return -x 145 | } 146 | return x 147 | } 148 | -------------------------------------------------------------------------------- /elevator_system/elevator_panel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type ElevatorPanel struct { 4 | PanelID int 5 | FloorButtons [15]bool 6 | } 7 | 8 | func NewElevatorPanel(panelID int) *ElevatorPanel { 9 | return &ElevatorPanel{PanelID: panelID, FloorButtons: [15]bool{}} 10 | } 11 | 12 | func (ep *ElevatorPanel) AddDestinationFloor(floor int) { 13 | ep.FloorButtons[floor] = true 14 | } 15 | 16 | func (ep *ElevatorPanel) RemoveDestinationFloor(floor int) { 17 | ep.FloorButtons[floor] = false 18 | } 19 | -------------------------------------------------------------------------------- /elevator_system/floor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Floor struct { 4 | Number int 5 | HallPanels []*HallPanel 6 | } 7 | 8 | func NewFloor(number int) *Floor { 9 | floor := &Floor{Number: number, HallPanels: make([]*HallPanel, 0)} 10 | 11 | for i := 1; i <= 3; i++ { 12 | hallPanel := NewHallPanel(i, number) 13 | floor.HallPanels = append(floor.HallPanels, hallPanel) 14 | } 15 | 16 | return floor 17 | } 18 | -------------------------------------------------------------------------------- /elevator_system/go.mod: -------------------------------------------------------------------------------- 1 | module lld_go_elevator_system 2 | 3 | go 1.22.5 4 | 5 | // elevatorPanel -> inside, ouside 6 | // elevator care 7 | // floor 8 | // building 9 | // elevator system -------------------------------------------------------------------------------- /elevator_system/hall_panel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Directions string 6 | 7 | const ( 8 | Up Directions = "Up" 9 | Down Directions = "Down" 10 | Still Directions = "Still" 11 | ) 12 | 13 | type HallPanel struct { 14 | PanelID int 15 | DirectionInstruction Directions 16 | SourceFloor int 17 | } 18 | 19 | func NewHallPanel(panelID int, sourceFloor int) *HallPanel { 20 | return &HallPanel{PanelID: panelID, SourceFloor: sourceFloor, DirectionInstruction: Still} 21 | } 22 | 23 | func (h *HallPanel) SetDirectionInstructions(directionInstruction Directions) { 24 | h.DirectionInstruction = directionInstruction 25 | } 26 | 27 | func (h *HallPanel) RequestElevator(manager *ElevatorManager, direction Directions) (elevator *Elevator) { 28 | fmt.Printf("Panel %d requesting elevator with direction %s from floor %d\n", h.PanelID, direction, h.SourceFloor) 29 | return manager.AssignElevator(h.SourceFloor, direction) 30 | } 31 | -------------------------------------------------------------------------------- /elevator_system/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | building := NewBuilding() 7 | manager := NewElevatorManager(building) 8 | 9 | var wg sync.WaitGroup 10 | 11 | wg.Add(1) 12 | go func() { 13 | defer wg.Done() 14 | elevator := building.Floors[1].HallPanels[1].RequestElevator(manager, Up) 15 | elevator.AddDestination(6) 16 | }() 17 | 18 | wg.Add(1) 19 | go func() { 20 | defer wg.Done() 21 | elevator := building.Floors[8].HallPanels[2].RequestElevator(manager, Down) 22 | elevator.AddDestination(7) 23 | }() 24 | 25 | wg.Add(1) 26 | go func() { 27 | defer wg.Done() 28 | thirdElevator := building.Floors[3].HallPanels[0].RequestElevator(manager, Up) 29 | thirdElevator.AddDestination(12) 30 | }() 31 | 32 | wg.Wait() 33 | 34 | go manager.OperateAllElevators() 35 | 36 | select {} 37 | } 38 | -------------------------------------------------------------------------------- /library-management_system/book.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type Book struct { 8 | ID int 9 | BookItem []BookItem 10 | Title string 11 | Author string 12 | PublishedYear string 13 | mu sync.RWMutex 14 | } 15 | 16 | func NewBook(id int, title, author string, publishedYear string) *Book { 17 | bookCopies := &Book{ID: id, Title: title, Author: author, PublishedYear: publishedYear, BookItem: make([]BookItem, 0)} 18 | for i := 1; i <= 10; i++ { 19 | bookCopies.BookItem = append(bookCopies.BookItem, *NewBookItem(i, id)) 20 | } 21 | return bookCopies 22 | } 23 | 24 | func (b *Book) IsBookAvailable() bool { 25 | b.mu.RLock() 26 | defer b.mu.RUnlock() 27 | for _, bookCopy := range b.BookItem { 28 | if bookCopy.Status == Available { 29 | 30 | return true 31 | } 32 | } 33 | return false 34 | } 35 | 36 | func (b *Book) BorrowBook() *BookItem { 37 | b.mu.Lock() 38 | defer b.mu.Unlock() 39 | for i := range b.BookItem { 40 | if b.BookItem[i].Status == Available { 41 | b.BookItem[i].BorrowBook() 42 | return &b.BookItem[i] 43 | } 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /library-management_system/book_item.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Status string 4 | 5 | const ( 6 | Borrowed Status = "Borrowed" 7 | Available Status = "Available" 8 | ) 9 | 10 | type BookItem struct { 11 | ID int 12 | BookID int 13 | Status Status 14 | } 15 | 16 | func NewBookItem(id, bookID int) *BookItem { 17 | return &BookItem{ID: id, BookID: bookID, Status: Available} 18 | } 19 | 20 | func (bi *BookItem) BorrowBook() { 21 | bi.Status = Borrowed 22 | } 23 | 24 | func (bi *BookItem) ReturnBook() { 25 | bi.Status = Available 26 | } 27 | -------------------------------------------------------------------------------- /library-management_system/go.mod: -------------------------------------------------------------------------------- 1 | module library_management_system 2 | 3 | go 1.22.5 4 | 5 | // book 6 | // bookitem 7 | // member 8 | // library -------------------------------------------------------------------------------- /library-management_system/library.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var ( 9 | libraryInstance *Library 10 | once sync.Once 11 | ) 12 | 13 | type Library struct { 14 | books map[int]*Book 15 | members map[int]*Member 16 | } 17 | 18 | func GetLibraryInstance() *Library { 19 | once.Do(func() { 20 | libraryInstance = &Library{books: make(map[int]*Book), members: make(map[int]*Member)} 21 | }) 22 | return libraryInstance 23 | } 24 | 25 | func (l *Library) AddBook(book *Book) { 26 | l.books[book.ID] = book 27 | fmt.Printf("Book %d has been added\n", book.ID) 28 | } 29 | 30 | func (l *Library) RemoveBook(id int) { 31 | delete(l.books, id) 32 | fmt.Printf("Book %d has been removed\n", id) 33 | } 34 | 35 | func (l *Library) AddMember(member *Member) { 36 | l.members[member.ID] = member 37 | fmt.Printf("Member %d has been added\n", member.ID) 38 | } 39 | 40 | func (l *Library) RemoveMember(id int) { 41 | delete(l.members, id) 42 | fmt.Printf("Member %d has been removed\n", id) 43 | } 44 | 45 | func (l *Library) BorrowBookByMember(memberID int, bookID int) (*BookItem, error) { 46 | if l.members[memberID] == nil || l.books[bookID] == nil { 47 | return nil, fmt.Errorf("Member or book not found") 48 | } 49 | 50 | member := l.members[memberID] 51 | 52 | if member.IsQuotaFull() { 53 | return nil, fmt.Errorf("Member %d has reached their borrowing quota\n", memberID) 54 | } 55 | 56 | book := l.books[bookID] 57 | 58 | if !book.IsBookAvailable() { 59 | return nil, fmt.Errorf("Book %d is not available\n", bookID) 60 | } 61 | 62 | borrowedBook := book.BorrowBook() 63 | 64 | member.AddBorrowedBook(borrowedBook) 65 | fmt.Printf("Member %d has borrowed book %d with item id %d\n", memberID, bookID, borrowedBook.ID) 66 | return borrowedBook, nil 67 | } 68 | 69 | func (l *Library) ReturnBookByMember(memberID int, bookItemID int) { 70 | member := l.members[memberID] 71 | 72 | for _, book := range member.CurrentBorrowed { 73 | if book.ID == bookItemID { 74 | book.ReturnBook() 75 | member.RemoveBorrowedBook(book) 76 | fmt.Printf("Member %d has returned book %d with item id %d\n", memberID, book.BookID, bookItemID) 77 | return 78 | } 79 | } 80 | } 81 | 82 | func (l *Library) DisplayAvailableBooks() { 83 | fmt.Println("Available Books:") 84 | for id, book := range l.books { 85 | if book.IsBookAvailable() { 86 | fmt.Printf("Book %d: %s by %s (published in %s)\n", id, book.Title, book.Author, book.PublishedYear) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /library-management_system/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | library := GetLibraryInstance() 7 | 8 | book1 := NewBook(1, "Book 1", "John Doe", "2000") 9 | book2 := NewBook(2, "Book 2", "Jane Doe", "2005") 10 | book3 := NewBook(3, "Book 3", "John Doe", "2010") 11 | 12 | library.AddBook(book1) 13 | library.AddBook(book2) 14 | library.AddBook(book3) 15 | 16 | library.DisplayAvailableBooks() 17 | 18 | member1 := NewMember(1, "Craig Bob", "123-456-7890") 19 | member2 := NewMember(2, "Alice Johnson", "987-654-3210") 20 | 21 | library.AddMember(member1) 22 | library.AddMember(member2) 23 | 24 | user1borrow1, err := library.BorrowBookByMember(member1.ID, book1.ID) 25 | if err != nil { 26 | fmt.Println("Error borrowing book:", err) 27 | } 28 | user1borrow2, err := library.BorrowBookByMember(member1.ID, book2.ID) 29 | if err != nil { 30 | fmt.Println("Error borrowing book:", err) 31 | } 32 | user2borrow1, err := library.BorrowBookByMember(member2.ID, book1.ID) 33 | if err != nil { 34 | fmt.Println("Error borrowing book:", err) 35 | } 36 | 37 | member1.DisplayCurrentBorrowedBooks() 38 | member2.DisplayCurrentBorrowedBooks() 39 | 40 | library.DisplayAvailableBooks() 41 | 42 | library.ReturnBookByMember(member1.ID, user1borrow1.ID) 43 | library.ReturnBookByMember(member2.ID, user2borrow1.ID) 44 | library.ReturnBookByMember(member1.ID, user1borrow2.ID) 45 | 46 | member1.DisplayBorrowHistory() 47 | member2.DisplayBorrowHistory() 48 | 49 | library.DisplayAvailableBooks() 50 | } 51 | -------------------------------------------------------------------------------- /library-management_system/member.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Member struct { 6 | ID int 7 | Name string 8 | ContactInfo string 9 | CurrentBorrowed []*BookItem 10 | BorrowHistory []*BookItem 11 | } 12 | 13 | func NewMember(id int, name, contactInfo string) *Member { 14 | return &Member{ID: id, Name: name, ContactInfo: contactInfo, CurrentBorrowed: make([]*BookItem, 0), BorrowHistory: make([]*BookItem, 0)} 15 | } 16 | 17 | func (m *Member) IsQuotaFull() bool { 18 | return len(m.CurrentBorrowed) >= 3 19 | } 20 | 21 | func (m *Member) AddBorrowedBook(bookItem *BookItem) { 22 | m.CurrentBorrowed = append(m.CurrentBorrowed, bookItem) 23 | } 24 | 25 | func (m *Member) RemoveBorrowedBook(bookItem *BookItem) { 26 | for i, bi := range m.CurrentBorrowed { 27 | if bi.ID == bookItem.ID { 28 | m.CurrentBorrowed = append(m.CurrentBorrowed[:i], m.CurrentBorrowed[i+1:]...) 29 | break 30 | } 31 | } 32 | m.BorrowHistory = append(m.BorrowHistory, bookItem) 33 | fmt.Printf("Book %d has been added to borrow history for Member %s\n", bookItem.ID, m.Name) 34 | } 35 | 36 | func (m *Member) DisplayCurrentBorrowedBooks() { 37 | fmt.Printf("Current borrowed books for Member %s:\n", m.Name) 38 | for _, bi := range m.CurrentBorrowed { 39 | fmt.Printf(" - Book %d (Item ID: %d)\n", bi.BookID, bi.ID) 40 | } 41 | } 42 | 43 | func (m *Member) DisplayBorrowHistory() { 44 | fmt.Printf("Borrow history for Member %s:\n", m.Name) 45 | for _, bi := range m.BorrowHistory { 46 | fmt.Printf(" - Book %d (Item ID: %d)\n", bi.BookID, bi.ID) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /meeting_scheduler/MeetingScheduler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | type MeetingScheduler struct { 6 | rooms []*MeetingRoom 7 | meeting []*Meeting 8 | } 9 | 10 | func NewMeetingScheduler() *MeetingScheduler { 11 | return &MeetingScheduler{ 12 | rooms: []*MeetingRoom{}, 13 | meeting: []*Meeting{}, 14 | } 15 | } 16 | 17 | func (ms *MeetingScheduler) GetRoom(roomId int) *MeetingRoom { 18 | for _, room := range ms.rooms { 19 | if room.id == roomId { 20 | return room 21 | } 22 | } 23 | return nil 24 | } 25 | 26 | // book meeting for a given room 27 | func (ms *MeetingScheduler) BookMeeting(roomId int, meetingName string, participant []*User, host User, dur *interval, capacity int) (int, error) { 28 | 29 | room := ms.GetRoom(roomId) 30 | if room == nil { 31 | return 0, errors.New("Room not found") 32 | } 33 | 34 | err := room.BookRoom(capacity, dur) 35 | if err != nil { 36 | return 0, err 37 | } 38 | 39 | meeting := NewMeeting(len(ms.meeting), meetingName, dur.id, room, &host) 40 | ms.meeting = append(ms.meeting, meeting) 41 | 42 | meeting.AddParticipant(participant...) 43 | 44 | return len(ms.meeting) - 1, nil 45 | } 46 | 47 | func (ms *MeetingScheduler) CancelMeeting(meetingId int) error { 48 | if meetingId >= len(ms.meeting) || meetingId < 0 || ms.meeting[meetingId] == nil { 49 | return errors.New("There is no such meeting") 50 | } 51 | 52 | // cancel meeting 53 | ms.meeting[meetingId].CancelMeeting() 54 | ms.meeting[meetingId] = nil 55 | 56 | return nil 57 | } 58 | 59 | // GetFreeRoom returns all rooms which are free in particular time interval 60 | func (ms *MeetingScheduler) GetFreeRoom(dur *interval) []*MeetingRoom { 61 | freeRooms := []*MeetingRoom{} 62 | for _, room := range ms.rooms { 63 | if room.IsFree(dur) { 64 | freeRooms = append(freeRooms, room) 65 | } 66 | 67 | } 68 | 69 | return freeRooms 70 | } 71 | -------------------------------------------------------------------------------- /meeting_scheduler/calender.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | type interval struct { 9 | id int 10 | date time.Time 11 | start time.Time 12 | end time.Time 13 | status BookingStatus 14 | } 15 | 16 | type Calendar struct { 17 | interval map[int]*interval 18 | } 19 | 20 | func (c *Calendar) isFree(dur *interval) bool { 21 | // Check if the calendar is free for the given interval, you can add logic to better check if the interval is free 22 | for _, ci := range c.interval { 23 | // check if time is colliding 24 | if ci.status == BOOKED && ci.start.Before(dur.end) && ci.end.After(dur.start) { 25 | return false 26 | } 27 | } 28 | return true 29 | } 30 | 31 | func (c *Calendar) setIntervalStatus(durId int, status BookingStatus) { 32 | c.interval[durId].status = status 33 | } 34 | 35 | func (c *Calendar) bookInterval(dur *interval) error { 36 | if c.isFree(dur) { 37 | c.interval[dur.id] = dur 38 | c.setIntervalStatus(dur.id, BOOKED) 39 | return nil 40 | } 41 | 42 | return errors.New("Interval is not free") 43 | } 44 | 45 | func (c *Calendar) cancelInterval(durId int) error { 46 | if val, ok := c.interval[durId]; ok { 47 | c.setIntervalStatus(val.id, CANCEL) 48 | return nil 49 | } 50 | 51 | return errors.New("Interval is not free") 52 | } 53 | -------------------------------------------------------------------------------- /meeting_scheduler/go.mod: -------------------------------------------------------------------------------- 1 | module lld_meeting_scheduler 2 | 3 | go 1.22.6 4 | 5 | require github.com/pkg/errors v0.9.1 6 | -------------------------------------------------------------------------------- /meeting_scheduler/go.sum: -------------------------------------------------------------------------------- 1 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 2 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 3 | -------------------------------------------------------------------------------- /meeting_scheduler/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // user code flow 9 | func main() { 10 | 11 | // prerequisites before scheduling the meeting 12 | 13 | // create rooms1 14 | room1 := &MeetingRoom{id: 1, capacity: 10, name: "Room 1", location: location{}, calendar: &Calendar{ 15 | interval: make(map[int]*interval), 16 | }} 17 | 18 | // create rooms2 19 | room2 := &MeetingRoom{id: 2, capacity: 10, name: "Room 2", location: location{}, calendar: &Calendar{ 20 | interval: make(map[int]*interval), 21 | }} 22 | 23 | // create user 24 | users := []*User{ 25 | &User{id: 1, name: "sam", email: "sam@email.com"}, 26 | &User{id: 2, name: "ron", email: "ron@email.com"}, 27 | &User{id: 3, name: "don", email: "don@email.com"}, 28 | &User{id: 4, name: "him", email: "him@email.com"}, 29 | } 30 | 31 | // first we will have already present rooms 32 | scheduler := NewMeetingScheduler() 33 | 34 | scheduler.rooms = append(scheduler.rooms, []*MeetingRoom{room1, room2}...) 35 | 36 | interval1 := &interval{ 37 | id: 1, 38 | date: time.Date(2009, 11, 17, 0, 0, 0, 0, time.UTC), 39 | start: time.Date(2009, 11, 17, 1, 0, 0, 0, time.UTC), 40 | end: time.Date(2009, 11, 17, 1, 30, 0, 0, time.UTC)} 41 | 42 | // prerequisites complete,Now we start the scheduling meeting, taking sam as host and ron as participant 43 | _, err := scheduler.BookMeeting(1, "Daily status", []*User{users[1], users[2]}, *users[0], interval1, 10) 44 | if err != nil { 45 | fmt.Printf("\nError while scheduling meeting:%#v", err) 46 | } 47 | 48 | // lets try to book the same room again 49 | _, err = scheduler.BookMeeting(2, "Party", []*User{users[1], users[2]}, *users[0], interval1, 10) 50 | if err != nil { 51 | fmt.Printf("\nError while scheduling meeting:%#v", err) 52 | } 53 | 54 | // lets try to book room with more capacity 55 | _, err = scheduler.BookMeeting(3, "Discussion", []*User{users[1], users[2]}, *users[0], interval1, 15) 56 | if err != nil { 57 | fmt.Printf("\nError while scheduling meeting:%#v", err) 58 | } 59 | 60 | // book for other interval 61 | interval2 := &interval{ 62 | id: 2, 63 | date: time.Date(2009, 11, 17, 0, 0, 0, 0, time.UTC), 64 | start: time.Date(2009, 11, 17, 2, 0, 0, 0, time.UTC), 65 | end: time.Date(2009, 11, 17, 2, 30, 0, 0, time.UTC)} 66 | 67 | // lets try to book room with more capacity 68 | meetinIDx, err := scheduler.BookMeeting(1, "Discussion", []*User{users[1], users[3]}, *users[0], interval2, 3) 69 | if err != nil { 70 | fmt.Printf("\nError while scheduling meeting:%#v", err) 71 | } 72 | 73 | // lets try to book room with more capacity 74 | _, err = scheduler.BookMeeting(1, "Discussion", []*User{users[1], users[3]}, *users[0], interval2, 3) 75 | if err != nil { 76 | fmt.Printf("\nError while scheduling meeting:%#v", err) 77 | } 78 | 79 | // cancel the meeting and book again 80 | scheduler.CancelMeeting(meetinIDx) 81 | 82 | // lets try to book room with more capacity 83 | _, err = scheduler.BookMeeting(1, "Discussion again", []*User{users[1], users[3]}, *users[0], interval2, 3) 84 | if err != nil { 85 | fmt.Printf("\nError while scheduling meeting:%#v", err) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /meeting_scheduler/meeting.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type BookingStatus string 9 | 10 | const ( 11 | BOOKED BookingStatus = "booked" 12 | CANCEL BookingStatus = "cancel" 13 | ) 14 | 15 | type Meeting struct { 16 | id int 17 | name string 18 | room *MeetingRoom 19 | participant []*User 20 | host *User 21 | meetingDurationId int 22 | status BookingStatus 23 | mu sync.Mutex 24 | } 25 | 26 | func NewMeeting(id int, name string, meetingDurationId int, room *MeetingRoom, host *User) *Meeting { 27 | return &Meeting{ 28 | id: id, 29 | name: name, 30 | room: room, 31 | meetingDurationId: meetingDurationId, 32 | participant: []*User{}, 33 | host: host, 34 | status: BOOKED, 35 | } 36 | } 37 | 38 | func (m *Meeting) AddParticipant(u ...*User) { 39 | m.mu.Lock() 40 | defer m.mu.Unlock() 41 | m.participant = append(m.participant, u...) 42 | } 43 | 44 | func (m *Meeting) RemoveParticipant(userId int) { 45 | m.mu.Lock() 46 | defer m.mu.Unlock() 47 | 48 | // remove user from the participant list 49 | for i, user := range m.participant { 50 | if user.id == userId { 51 | m.participant = append(m.participant[:i], m.participant[i+1:]...) 52 | } 53 | } 54 | } 55 | 56 | func (m *Meeting) setStatus(status BookingStatus) { 57 | m.status = status 58 | } 59 | 60 | func (m *Meeting) CancelMeeting() { 61 | m.mu.Lock() 62 | defer m.mu.Unlock() 63 | 64 | m.setStatus(CANCEL) 65 | m.room.CancelRoom(m.meetingDurationId) 66 | m.notifyParticipants(CANCEL) 67 | } 68 | 69 | // notify use oberver pattern to notify all the participants 70 | func (m *Meeting) notifyParticipants(status BookingStatus) { 71 | fmt.Printf("\nMeeting %s has been %s\n", m.name, status) 72 | for _, user := range m.participant { 73 | user.notification() 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /meeting_scheduler/room.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | type location struct { 11 | building string 12 | floor string 13 | } 14 | 15 | type MeetingRoom struct { 16 | id int 17 | capacity int 18 | name string 19 | status BookingStatus 20 | location location 21 | calendar *Calendar 22 | mu sync.Mutex 23 | } 24 | 25 | func NewMeetingRoom(id int, capacity int, name string, location location) *MeetingRoom { 26 | return &MeetingRoom{ 27 | id: id, 28 | capacity: capacity, 29 | name: name, 30 | location: location, 31 | calendar: &Calendar{ 32 | interval: make(map[int]*interval), 33 | }, 34 | } 35 | } 36 | 37 | func (m *MeetingRoom) GetName() string { 38 | return m.name 39 | } 40 | 41 | func (m *MeetingRoom) BookRoom(capacity int, dur *interval) error { 42 | m.mu.Lock() 43 | defer m.mu.Unlock() 44 | 45 | // Check if the room has the capacity 46 | if !m.hasCapacity(capacity) { 47 | return errors.New("Room does not have the capacity") 48 | } 49 | 50 | // Check if the room calendar is free for the given interval 51 | if m.calendar.isFree(dur) { 52 | fmt.Printf("\nRoom %s is booked for the given interval startTime:%s endTime:%v\n", m.name, dur.start.String(), dur.end.String()) 53 | m.calendar.bookInterval(dur) 54 | return nil 55 | } 56 | 57 | return errors.New("Room is not free for the given interval") 58 | } 59 | 60 | func (m *MeetingRoom) CancelRoom(durId int) { 61 | m.mu.Lock() 62 | defer m.mu.Unlock() 63 | 64 | m.calendar.cancelInterval(durId) 65 | } 66 | 67 | // IsFree is use to explose outside 68 | func (m *MeetingRoom) IsFree(dur *interval) bool { 69 | m.mu.Lock() 70 | defer m.mu.Unlock() 71 | 72 | return m.calendar.isFree(dur) 73 | } 74 | 75 | func (m *MeetingRoom) hasCapacity(capacity int) bool { 76 | // Check if the room has the capacity 77 | if m.capacity >= capacity { 78 | return true 79 | } 80 | return false 81 | } 82 | -------------------------------------------------------------------------------- /meeting_scheduler/user.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type IUser interface { 6 | notification() 7 | } 8 | 9 | type User struct { 10 | id int 11 | name string 12 | email string 13 | } 14 | 15 | func (u *User) notification() { 16 | // Send email to user 17 | fmt.Printf("Email sent to %s\n", u.email) 18 | } 19 | -------------------------------------------------------------------------------- /parking_lot/floor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "lld_go_parking_lot/vehicles" 6 | ) 7 | 8 | const ( 9 | CarSpotCount = 5 10 | VanSpotCount = 3 11 | TruckSpotCount = 2 12 | MotorcycleSpotCount = 10 13 | ) 14 | 15 | type ParkingFloor struct { 16 | FloorID int 17 | ParkingSpots map[vehicles.VehicleType]map[int]*ParkingSpot 18 | } 19 | 20 | func NewParkingFloor(floorID int) *ParkingFloor { 21 | parkingSpots := make(map[vehicles.VehicleType]map[int]*ParkingSpot) 22 | 23 | parkingSpots[vehicles.CarType] = createParkingSpots(CarSpotCount, vehicles.CarType) 24 | parkingSpots[vehicles.VanType] = createParkingSpots(VanSpotCount, vehicles.VanType) 25 | parkingSpots[vehicles.TruckType] = createParkingSpots(TruckSpotCount, vehicles.TruckType) 26 | parkingSpots[vehicles.MotorcycleType] = createParkingSpots(MotorcycleSpotCount, vehicles.MotorcycleType) 27 | 28 | return &ParkingFloor{FloorID: floorID, ParkingSpots: parkingSpots} 29 | } 30 | 31 | func createParkingSpots(count int, vehicleType vehicles.VehicleType) map[int]*ParkingSpot { 32 | spots := make(map[int]*ParkingSpot) 33 | for i := 1; i <= count; i++ { 34 | spots[i] = NewParkingSpot(i, vehicleType) 35 | } 36 | return spots 37 | } 38 | 39 | func (p *ParkingFloor) FindParkingSpot(vehicleType vehicles.VehicleType) *ParkingSpot { 40 | for _, spot := range p.ParkingSpots[vehicleType] { 41 | if spot.IsParkingSpotFree() { 42 | return spot 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | func (p *ParkingFloor) DisplayFloorStatus(parkingFloor *ParkingFloor) { 49 | fmt.Printf("Floor ID: %d\n", parkingFloor.FloorID) 50 | 51 | for vehicleType, spotMap := range parkingFloor.ParkingSpots { 52 | fmt.Printf("\n%s Spots:\n", vehicleType) 53 | count := 0 54 | 55 | for _, spot := range spotMap { 56 | if spot.IsParkingSpotFree() { 57 | count++ 58 | } 59 | } 60 | 61 | fmt.Printf("\n%s Spot: %d Free\n", vehicleType, count) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /parking_lot/go.mod: -------------------------------------------------------------------------------- 1 | module lld_go_parking_lot 2 | 3 | go 1.22.5 4 | -------------------------------------------------------------------------------- /parking_lot/lld_go_parking_lot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thesaltree/low-level-design-golang/b4c68e61cea626fa86e2c158d3de7546fc6c471f/parking_lot/lld_go_parking_lot -------------------------------------------------------------------------------- /parking_lot/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "lld_go_parking_lot/vehicles" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var ( 11 | wg sync.WaitGroup 12 | ) 13 | 14 | func main() { 15 | parkingLot := GetParkingLotInstance() 16 | 17 | parkingLot.Name = "Central Parking Lot" 18 | 19 | parkingLot.AddFloor(0) 20 | parkingLot.AddFloor(1) 21 | 22 | parkingLot.DisplayAvailability() 23 | 24 | for i := 1; i <= 10; i++ { 25 | wg.Add(1) 26 | 27 | go parkCar(i, parkingLot) 28 | } 29 | 30 | wg.Wait() 31 | 32 | parkingLot.DisplayAvailability() 33 | 34 | ticket, _ := parkingLot.ParkVehicle(vehicles.NewTruck("truck-1")) 35 | 36 | time.Sleep(10 * time.Second) 37 | err := parkingLot.UnparkVehicle(ticket) 38 | if err != nil { 39 | return 40 | } 41 | 42 | formattedCharge := fmt.Sprintf("%.2f", ticket.CalculateTotalCharge()) 43 | 44 | fmt.Printf("bill for %s = %s\n", ticket.Vehicle.GetLicenceNumber(), formattedCharge) 45 | 46 | } 47 | 48 | func parkCar(ind int, parkingLot *ParkingLot) { 49 | defer wg.Done() 50 | 51 | car := vehicles.NewCar(fmt.Sprintf("car-%d", ind)) 52 | 53 | ticket, err := parkingLot.ParkVehicle(car) 54 | if err != nil { 55 | fmt.Printf("Failed to park %s: %v\n", car.LicenceNumber, err) 56 | return 57 | } 58 | 59 | fmt.Printf("%s parked successfully. Ticket: %s\n", car.LicenceNumber, ticket.EntryTime) 60 | } 61 | -------------------------------------------------------------------------------- /parking_lot/parking_lot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "lld_go_parking_lot/vehicles" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var ( 11 | parkingLotInstance *ParkingLot 12 | once sync.Once 13 | ) 14 | 15 | type ParkingLot struct { 16 | Name string 17 | floors []*ParkingFloor 18 | } 19 | 20 | func GetParkingLotInstance() *ParkingLot { 21 | 22 | once.Do(func() { 23 | parkingLotInstance = &ParkingLot{} 24 | }) 25 | return parkingLotInstance 26 | } 27 | 28 | func (p *ParkingLot) AddFloor(floorID int) { 29 | p.floors = append(p.floors, NewParkingFloor(floorID)) 30 | } 31 | 32 | func (p *ParkingLot) DisplayAvailability() { 33 | fmt.Printf("Parking Lot: %s\n", p.Name) 34 | 35 | for _, floor := range p.floors { 36 | floor.DisplayFloorStatus(floor) 37 | } 38 | } 39 | 40 | func (p *ParkingLot) findParkingSpot(vehicleType vehicles.VehicleType) (*ParkingSpot, error) { 41 | for _, floor := range p.floors { 42 | if spot := floor.FindParkingSpot(vehicleType); spot != nil { 43 | return spot, nil 44 | } 45 | } 46 | 47 | return nil, fmt.Errorf("no available parking spot found for %s", vehicleType) 48 | } 49 | 50 | func (p *ParkingLot) ParkVehicle(vehicle vehicles.VehicleInterface) (*ParkingTicket, error) { 51 | parkingSpot, err := p.findParkingSpot(vehicle.GetVehicleType()) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | err = parkingSpot.ParkVehicle(vehicle) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | parkingTicket := NewParkingTicket(vehicle, parkingSpot) 62 | 63 | return parkingTicket, nil 64 | } 65 | 66 | func (p *ParkingLot) UnparkVehicle(parkingTicket *ParkingTicket) error { 67 | parkingTicket.SetExitTime(time.Now()) 68 | charge := parkingTicket.CalculateTotalCharge() 69 | 70 | paymentSystem := NewPaymentSystem(charge, parkingTicket) 71 | 72 | if err := paymentSystem.ProcessPayment(); err != nil { 73 | return fmt.Errorf("payment failed: %v. Vehicle is still parked", err) 74 | } 75 | 76 | parkingTicket.Spot.RemoveVehicle() 77 | 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /parking_lot/parking_spot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "lld_go_parking_lot/vehicles" 6 | "sync" 7 | ) 8 | 9 | type ParkingSpot struct { 10 | SpotID int 11 | VehicleType vehicles.VehicleType 12 | CurrentVehicle *vehicles.VehicleInterface 13 | lock sync.Mutex 14 | } 15 | 16 | func NewParkingSpot(spotID int, vehicleType vehicles.VehicleType) *ParkingSpot { 17 | return &ParkingSpot{SpotID: spotID, VehicleType: vehicleType} 18 | } 19 | 20 | func (p *ParkingSpot) IsParkingSpotFree() bool { 21 | return p.CurrentVehicle == nil 22 | } 23 | 24 | func (p *ParkingSpot) ParkVehicle(vehicle vehicles.VehicleInterface) error { 25 | p.lock.Lock() 26 | defer p.lock.Unlock() 27 | 28 | if vehicle.GetVehicleType() != p.VehicleType { 29 | return fmt.Errorf("vehicle type mismatch: expected %s, got %s", p.VehicleType, vehicle.GetVehicleType()) 30 | } 31 | if p.CurrentVehicle != nil { 32 | return fmt.Errorf("parking spot already occupied") 33 | } 34 | 35 | p.CurrentVehicle = &vehicle 36 | return nil 37 | } 38 | 39 | func (p *ParkingSpot) RemoveVehicle() { 40 | p.CurrentVehicle = nil 41 | } 42 | -------------------------------------------------------------------------------- /parking_lot/parking_ticket.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "lld_go_parking_lot/vehicles" 5 | "time" 6 | ) 7 | 8 | const baseCharge = 100.00 9 | 10 | type ParkingTicket struct { 11 | EntryTime time.Time 12 | ExitTime time.Time 13 | Vehicle vehicles.VehicleInterface 14 | Spot *ParkingSpot 15 | TotalCharge float64 16 | } 17 | 18 | func NewParkingTicket(vehicle vehicles.VehicleInterface, spot *ParkingSpot) *ParkingTicket { 19 | return &ParkingTicket{EntryTime: time.Now(), ExitTime: time.Time{}, Vehicle: vehicle, Spot: spot, TotalCharge: 0.00} 20 | } 21 | 22 | func (p *ParkingTicket) SetExitTime(exitTime time.Time) { 23 | p.ExitTime = exitTime 24 | } 25 | 26 | func (p *ParkingTicket) CalculateTotalCharge() float64 { 27 | if p.ExitTime == (time.Time{}) { 28 | p.TotalCharge = baseCharge 29 | return p.TotalCharge 30 | } 31 | duration := p.ExitTime.Sub(p.EntryTime) 32 | hours := duration.Hours() 33 | additionalCharge := hours * p.Vehicle.GetVehicleCost() 34 | p.TotalCharge = baseCharge + additionalCharge 35 | return p.TotalCharge 36 | } 37 | -------------------------------------------------------------------------------- /parking_lot/payment.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Status string 8 | 9 | const ( 10 | PaymentStatusCompleted Status = "Completed" 11 | PaymentStatusFailed Status = "Failed" 12 | PaymentStatusPending Status = "Pending" 13 | ) 14 | 15 | type PaymentSystem struct { 16 | Status Status 17 | Amount float64 18 | ParkingTicket *ParkingTicket 19 | } 20 | 21 | func NewPaymentSystem(amount float64, ticket *ParkingTicket) *PaymentSystem { 22 | return &PaymentSystem{Status: PaymentStatusPending, Amount: amount, ParkingTicket: ticket} 23 | } 24 | 25 | func (p *PaymentSystem) ProcessPayment() error { 26 | if p.ParkingTicket == nil { 27 | return fmt.Errorf("payment failed: no parking ticket found") 28 | } 29 | if p.ParkingTicket.TotalCharge < p.Amount { 30 | p.Status = PaymentStatusFailed 31 | return fmt.Errorf("payment failed: insufficient funds") 32 | } 33 | 34 | p.Status = PaymentStatusCompleted 35 | return nil 36 | } 37 | 38 | func (p *PaymentSystem) GetPaymentStatus() Status { 39 | return p.Status 40 | } 41 | -------------------------------------------------------------------------------- /parking_lot/vehicles/vehicle.go: -------------------------------------------------------------------------------- 1 | package vehicles 2 | 3 | type VehicleType string 4 | 5 | const ( 6 | CarType VehicleType = "Car" 7 | VanType VehicleType = "Van" 8 | TruckType VehicleType = "Truck" 9 | MotorcycleType VehicleType = "Motorcycle" 10 | ) 11 | 12 | var vehicleCosts = map[VehicleType]float64{ 13 | CarType: 100, 14 | VanType: 200, 15 | TruckType: 300, 16 | MotorcycleType: 50, 17 | } 18 | 19 | type Vehicle struct { 20 | LicenceNumber string 21 | VehicleType VehicleType 22 | Cost float64 23 | } 24 | 25 | type VehicleInterface interface { 26 | GetLicenceNumber() string 27 | GetVehicleType() VehicleType 28 | GetVehicleCost() float64 29 | } 30 | 31 | func (v *Vehicle) GetLicenceNumber() string { 32 | return v.LicenceNumber 33 | } 34 | 35 | func (v *Vehicle) GetVehicleType() VehicleType { 36 | return v.VehicleType 37 | } 38 | 39 | func (v *Vehicle) GetVehicleCost() float64 { 40 | return v.Cost 41 | } 42 | 43 | func NewVehicle(licenceNumber string, vehicleType VehicleType) *Vehicle { 44 | cost := vehicleCosts[vehicleType] 45 | return &Vehicle{LicenceNumber: licenceNumber, VehicleType: vehicleType, Cost: cost} 46 | } 47 | 48 | type Car struct { 49 | Vehicle 50 | } 51 | 52 | func NewCar(licenceNumber string) *Car { 53 | return &Car{Vehicle: *NewVehicle(licenceNumber, CarType)} 54 | } 55 | 56 | type Van struct { 57 | Vehicle 58 | } 59 | 60 | func NewVan(licenceNumber string) *Van { 61 | return &Van{Vehicle: *NewVehicle(licenceNumber, VanType)} 62 | } 63 | 64 | type Truck struct { 65 | Vehicle 66 | } 67 | 68 | func NewTruck(licenceNumber string) *Truck { 69 | return &Truck{Vehicle: *NewVehicle(licenceNumber, TruckType)} 70 | } 71 | 72 | type Motorcycle struct { 73 | Vehicle 74 | } 75 | 76 | func NewMotorcycle(licenceNumber string) *Motorcycle { 77 | return &Motorcycle{Vehicle: *NewVehicle(licenceNumber, MotorcycleType)} 78 | } 79 | -------------------------------------------------------------------------------- /social_media_platform/activity_manager.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type ActivityFacade struct { 8 | UserManager *UserManager 9 | PostManager *PostManager 10 | NotificationManager *NotificationManager 11 | } 12 | 13 | func NewActivityFacade() *ActivityFacade { 14 | return &ActivityFacade{ 15 | UserManager: GetUserManagerInstance(), 16 | PostManager: GetPostManagerInstance(), 17 | NotificationManager: GetNotificationManagerInstance(), 18 | } 19 | } 20 | 21 | // User related operations 22 | 23 | func (af *ActivityFacade) AddUser(user *User) { 24 | af.UserManager.AddUser(user) 25 | } 26 | 27 | func (af *ActivityFacade) RemoveUser(userID int) { 28 | af.UserManager.RemoveUser(userID) 29 | } 30 | 31 | func (af *ActivityFacade) LoginUser(email, password string) (*User, error) { 32 | return af.UserManager.LoginUser(email, password) 33 | } 34 | 35 | func (af *ActivityFacade) SendFriendRequest(requesterID, receiverID int) error { 36 | _, err := af.UserManager.GetUserByID(receiverID) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | af.NotificationManager.AddNotification(receiverID, FriendRequestNotificationType, fmt.Sprintf("%d has sent you a friend request", requesterID)) 42 | 43 | fmt.Printf("Friend request sent to user %d\n", receiverID) 44 | return nil 45 | } 46 | 47 | func (af *ActivityFacade) AcceptFriendRequest(requesterID, receiverID int) error { 48 | err := af.UserManager.AddFriend(requesterID, receiverID) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | af.NotificationManager.AddNotification(requesterID, FriendRequestAcceptedNotificationType, fmt.Sprintf("%d has accepted your friend request", receiverID)) 54 | 55 | fmt.Printf("Friend request accepted by user %d\n", receiverID) 56 | return nil 57 | } 58 | 59 | // Post related operations 60 | 61 | func (af *ActivityFacade) AddPost(post *Post) error { 62 | user, err := af.UserManager.GetUserByID(post.UserID) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | af.PostManager.AddPost(post, user) 68 | fmt.Printf("Post added: %d\n", post.ID) 69 | return nil 70 | } 71 | 72 | func (af *ActivityFacade) GetFeedPosts(userID int) ([]*Post, error) { 73 | 74 | user, err := af.UserManager.GetUserByID(userID) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return af.PostManager.GetUserFeed(user) 79 | } 80 | 81 | func (af *ActivityFacade) LikePost(userID, postID int) error { 82 | post, err := af.PostManager.LikePost(postID) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | af.NotificationManager.AddNotification(post.UserID, LikeNotificationType, fmt.Sprintf("%d has liked your post: %d", userID, post.ID)) 88 | 89 | fmt.Printf("Post liked: %d\n", post.ID) 90 | return nil 91 | } 92 | 93 | func (af *ActivityFacade) CommentPost(userID, postID int, content string) error { 94 | user, err := af.UserManager.GetUserByID(userID) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | post, err := af.PostManager.CommentPost(user, postID, content) 100 | 101 | af.NotificationManager.AddNotification(post.UserID, CommentNotificationType, fmt.Sprintf("%d has commented on your post: %d", userID, post.ID)) 102 | 103 | fmt.Printf("Comment added to post: %d\n", post.ID) 104 | return nil 105 | } 106 | 107 | func (af *ActivityFacade) MentionUserInPost(postID, mentionedUserID int) error { 108 | post, err := af.PostManager.GetPost(postID) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | af.NotificationManager.AddNotification(post.UserID, MentionNotificationType, fmt.Sprintf("%d has mentioned you in their post: %d", mentionedUserID, post.ID)) 114 | return nil 115 | } 116 | 117 | func (af *ActivityFacade) UnpublishPost(postID int) error { 118 | return af.PostManager.UnpublishPost(postID) 119 | } 120 | 121 | func (af *ActivityFacade) PublishPost(postID int) error { 122 | return af.PostManager.PublishPost(postID) 123 | } 124 | 125 | func (af *ActivityFacade) UpdatePost(postID int, content string) error { 126 | return af.PostManager.UpdatePost(postID, content) 127 | } 128 | 129 | func (af *ActivityFacade) HidePostFromUser(postID int, userID int) error { 130 | _, err := af.UserManager.GetUserByID(userID) 131 | if err != nil { 132 | return err 133 | } 134 | 135 | return af.PostManager.HidePostFromUser(postID, userID) 136 | } 137 | 138 | func (af *ActivityFacade) UnhidePostFromUser(postID int, userID int) error { 139 | _, err := af.UserManager.GetUserByID(userID) 140 | if err != nil { 141 | return err 142 | } 143 | 144 | return af.PostManager.UnhidePostFromUser(postID, userID) 145 | } 146 | 147 | func (af *ActivityFacade) EnableComments(postID int) error { 148 | return af.PostManager.EnableComments(postID) 149 | } 150 | 151 | func (af *ActivityFacade) DisableComments(postID int) error { 152 | return af.PostManager.DisableComments(postID) 153 | } 154 | 155 | // Notification related operations 156 | 157 | func (af *ActivityFacade) GetNotifications(userID int) ([]*Notification, error) { 158 | return af.NotificationManager.GetNotificationsForUser(userID) 159 | } 160 | -------------------------------------------------------------------------------- /social_media_platform/comment.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Comment struct { 4 | ID string 5 | PostID int 6 | Content string 7 | User *User 8 | } 9 | 10 | func NewComment(id string, postID int, content string, user *User) *Comment { 11 | return &Comment{ID: id, PostID: postID, Content: content, User: user} 12 | } 13 | -------------------------------------------------------------------------------- /social_media_platform/go.mod: -------------------------------------------------------------------------------- 1 | module lld_social_media_platform 2 | 3 | go 1.22.5 4 | 5 | // user 6 | // post 7 | // comment 8 | // notification 9 | // feed 10 | -------------------------------------------------------------------------------- /social_media_platform/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | socialMedia := NewActivityFacade() 7 | 8 | // Simulate user actions 9 | 10 | // Create users and add them to the social media platform 11 | user1 := NewUser(1, "John Doe", "john.doe@example.com", "password123", "", "I'm a lovely person!") 12 | user2 := NewUser(2, "Jane Smith", "jane.smith@example.com", "secret123", "", "I'm a talented artist!") 13 | 14 | socialMedia.AddUser(user1) 15 | socialMedia.AddUser(user2) 16 | 17 | //Create posts and add them to the social media platform 18 | post1 := NewPost(1, 1, "Today, I went to the park and walked 1000 steps!", nil) 19 | post2 := NewPost(2, 2, "I painted a beautiful sunset on the wall!", nil) 20 | post3 := NewPost(3, 1, "I've been working on a new video game!", nil) 21 | 22 | err := socialMedia.AddPost(post1) 23 | if err != nil { 24 | fmt.Println("Error adding post:", err) 25 | } 26 | err = socialMedia.AddPost(post2) 27 | if err != nil { 28 | fmt.Println("Error adding post:", err) 29 | } 30 | err = socialMedia.AddPost(post3) 31 | if err != nil { 32 | fmt.Println("Error adding post:", err) 33 | } 34 | 35 | // Get user feeds 36 | err = getUserFeed(socialMedia, user1.ID) 37 | if err != nil { 38 | fmt.Println("Error getting user feed:", err) 39 | } 40 | err = getUserFeed(socialMedia, user2.ID) 41 | if err != nil { 42 | fmt.Println("Error getting user feed:", err) 43 | } 44 | 45 | // Send friend requests and accept them 46 | err = socialMedia.SendFriendRequest(1, 2) 47 | if err != nil { 48 | fmt.Println("Error sending friend request:", err) 49 | } 50 | err = socialMedia.AcceptFriendRequest(1, 2) 51 | if err != nil { 52 | fmt.Println("Error accepting friend request:", err) 53 | } 54 | 55 | err = getUserFeed(socialMedia, user1.ID) 56 | if err != nil { 57 | fmt.Println("Error getting user feed:", err) 58 | } 59 | 60 | // Publish and unpublish posts 61 | err = socialMedia.UnpublishPost(1) 62 | 63 | err = getUserFeed(socialMedia, user2.ID) 64 | if err != nil { 65 | fmt.Println("Error getting user feed:", err) 66 | } 67 | 68 | // Comment on a post 69 | err = socialMedia.CommentPost(1, 2, "I really like this post!") 70 | if err != nil { 71 | fmt.Println("Error commenting on post:", err) 72 | } 73 | 74 | comments := post2.GetComments() 75 | for _, comment := range comments { 76 | fmt.Printf("User %s: %s\n", comment.User.Name, comment.Content) 77 | } 78 | 79 | // Like a post 80 | err = socialMedia.LikePost(2, 3) 81 | if err != nil { 82 | fmt.Println("Error liking post:", err) 83 | } 84 | 85 | fmt.Printf("Post %d's likes: %d\n", post3.ID, post3.GetLikes()) 86 | 87 | err = socialMedia.LikePost(2, 3) 88 | if err != nil { 89 | fmt.Println("Error liking post:", err) 90 | } 91 | 92 | fmt.Printf("Post %d's likes: %d\n", post3.ID, post3.GetLikes()) 93 | } 94 | 95 | func getUserFeed(socialMedia *ActivityFacade, userID int) error { 96 | feed, err := socialMedia.GetFeedPosts(userID) 97 | if err != nil { 98 | fmt.Println("Error getting feed posts:", err) 99 | } 100 | for _, post := range feed { 101 | fmt.Printf("User %d: %s\n", post.UserID, post.Content) 102 | } 103 | 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /social_media_platform/notification.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type NotificationType string 4 | 5 | const ( 6 | FriendRequestNotificationType NotificationType = "FriendRequest" 7 | FriendRequestAcceptedNotificationType NotificationType = "FriendRequestAccepted" 8 | CommentNotificationType NotificationType = "Comment" 9 | LikeNotificationType NotificationType = "Like" 10 | MentionNotificationType NotificationType = "Mention" 11 | MessageNotificationType NotificationType = "Message" 12 | ) 13 | 14 | type Notification struct { 15 | ID string 16 | Type NotificationType 17 | Content string 18 | UserID int 19 | } 20 | 21 | func NewNotification(id string, notifType NotificationType, content string, userID int) *Notification { 22 | return &Notification{ID: id, Type: notifType, Content: content, UserID: userID} 23 | } 24 | -------------------------------------------------------------------------------- /social_media_platform/notification_manager.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var ( 10 | notificationManagerInstance *NotificationManager 11 | notifOnce sync.Once 12 | ) 13 | 14 | type NotificationManager struct { 15 | notifications map[int][]*Notification 16 | mu sync.RWMutex 17 | } 18 | 19 | func GetNotificationManagerInstance() *NotificationManager { 20 | notifOnce.Do(func() { 21 | notificationManagerInstance = &NotificationManager{notifications: make(map[int][]*Notification)} 22 | }) 23 | return notificationManagerInstance 24 | } 25 | 26 | func (nm *NotificationManager) AddNotification(userID int, notificationType NotificationType, message string) { 27 | nm.mu.Lock() 28 | defer nm.mu.Unlock() 29 | 30 | notification := NewNotification(fmt.Sprintf("notification-%d", time.Now().UnixMicro()), notificationType, message, userID) 31 | nm.notifications[userID] = append(nm.notifications[userID], notification) 32 | } 33 | 34 | func (nm *NotificationManager) GetNotificationsForUser(userID int) ([]*Notification, error) { 35 | nm.mu.RLock() 36 | defer nm.mu.RUnlock() 37 | 38 | notifications, ok := nm.notifications[userID] 39 | if !ok { 40 | return nil, fmt.Errorf("user not found") 41 | } 42 | return notifications, nil 43 | } 44 | -------------------------------------------------------------------------------- /social_media_platform/post.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | type Post struct { 9 | ID int 10 | UserID int 11 | Content string 12 | IsPublished bool 13 | URLs []*string 14 | Likes int 15 | Comments []*Comment 16 | PublishedAt time.Time 17 | CommentsEnabled bool 18 | HiddenFromUsers map[int]bool 19 | mu sync.RWMutex 20 | } 21 | 22 | func NewPost(id int, userID int, content string, urls []*string) *Post { 23 | return &Post{ 24 | ID: id, 25 | UserID: userID, 26 | Content: content, 27 | IsPublished: true, 28 | URLs: urls, 29 | Likes: 0, 30 | Comments: make([]*Comment, 0), 31 | PublishedAt: time.Now(), 32 | CommentsEnabled: true, 33 | HiddenFromUsers: make(map[int]bool), 34 | } 35 | } 36 | 37 | func (p *Post) AddComment(comment *Comment) { 38 | p.mu.Lock() 39 | defer p.mu.Unlock() 40 | p.Comments = append(p.Comments, comment) 41 | } 42 | 43 | func (p *Post) GetComments() []*Comment { 44 | p.mu.RLock() 45 | defer p.mu.RUnlock() 46 | return p.Comments 47 | } 48 | 49 | func (p *Post) Like() { 50 | p.mu.Lock() 51 | defer p.mu.Unlock() 52 | p.Likes++ 53 | } 54 | 55 | func (p *Post) GetLikes() int { 56 | p.mu.RLock() 57 | defer p.mu.RUnlock() 58 | return p.Likes 59 | } 60 | 61 | func (p *Post) HideFromUser(userID int) { 62 | p.HiddenFromUsers[userID] = true 63 | } 64 | 65 | func (p *Post) UnhideFromUser(userID int) { 66 | delete(p.HiddenFromUsers, userID) 67 | } 68 | 69 | func (p *Post) IsHiddenFromUser(user User) bool { 70 | _, ok := p.HiddenFromUsers[user.ID] 71 | return ok 72 | } 73 | 74 | func (p *Post) EnableComments() { 75 | p.CommentsEnabled = true 76 | } 77 | 78 | func (p *Post) DisableComments() { 79 | p.CommentsEnabled = false 80 | } 81 | 82 | func (p *Post) IsCommentsEnabled() bool { 83 | return p.CommentsEnabled 84 | } 85 | 86 | func (p *Post) PublishPost() { 87 | p.IsPublished = true 88 | p.PublishedAt = time.Now() 89 | } 90 | 91 | func (p *Post) UnpublishPost() { 92 | p.IsPublished = false 93 | } 94 | 95 | func (p *Post) UpdateContent(content string) { 96 | p.Content = content 97 | } 98 | -------------------------------------------------------------------------------- /social_media_platform/post_manager.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var ( 10 | postManagerInstance *PostManager 11 | postOnce sync.Once 12 | ) 13 | 14 | type PostManager struct { 15 | posts map[int]*Post 16 | mu sync.RWMutex 17 | } 18 | 19 | func GetPostManagerInstance() *PostManager { 20 | postOnce.Do(func() { 21 | postManagerInstance = &PostManager{posts: make(map[int]*Post)} 22 | }) 23 | return postManagerInstance 24 | } 25 | 26 | func (pm *PostManager) GetPost(postID int) (*Post, error) { 27 | pm.mu.RLock() 28 | defer pm.mu.RUnlock() 29 | 30 | post, exists := pm.posts[postID] 31 | if !exists { 32 | return nil, fmt.Errorf("post not found") 33 | } 34 | return post, nil 35 | } 36 | 37 | func (pm *PostManager) AddPost(post *Post, user *User) { 38 | pm.mu.Lock() 39 | defer pm.mu.Unlock() 40 | pm.posts[post.ID] = post 41 | 42 | user.AddPost(post) 43 | } 44 | 45 | func (pm *PostManager) LikePost(postID int) (*Post, error) { 46 | pm.mu.Lock() 47 | post := pm.posts[postID] 48 | pm.mu.Unlock() 49 | 50 | if post == nil { 51 | return nil, fmt.Errorf("post not found") 52 | } 53 | 54 | pm.mu.Lock() 55 | defer pm.mu.Unlock() 56 | 57 | post.Like() 58 | return post, nil 59 | } 60 | 61 | func (pm *PostManager) CommentPost(user *User, postID int, content string) (*Post, error) { 62 | pm.mu.Lock() 63 | post := pm.posts[postID] 64 | pm.mu.Unlock() 65 | 66 | if post == nil { 67 | return nil, fmt.Errorf("post not found") 68 | } 69 | 70 | pm.mu.Lock() 71 | defer pm.mu.Unlock() 72 | 73 | comment := NewComment(fmt.Sprintf("comment-%d", time.Now().UnixNano()), postID, content, user) 74 | post.AddComment(comment) 75 | 76 | return post, nil 77 | } 78 | 79 | func (pm *PostManager) GetUserFeed(user *User) ([]*Post, error) { 80 | pm.mu.RLock() 81 | defer pm.mu.RUnlock() 82 | 83 | var feed []*Post 84 | 85 | friends := user.GetFriends() 86 | 87 | feed = append(feed, user.GetPosts()...) 88 | 89 | for _, friend := range friends { 90 | userPosts := friend.GetPosts() 91 | for _, post := range userPosts { 92 | if !post.IsHiddenFromUser(*user) && post.IsPublished { 93 | feed = append(feed, post) 94 | } 95 | } 96 | } 97 | 98 | return feed, nil 99 | } 100 | 101 | func (pm *PostManager) UnpublishPost(postID int) error { 102 | pm.mu.Lock() 103 | post := pm.posts[postID] 104 | pm.mu.Unlock() 105 | 106 | if post == nil { 107 | return fmt.Errorf("post not found") 108 | } 109 | 110 | pm.mu.Lock() 111 | defer pm.mu.Unlock() 112 | 113 | post.UnpublishPost() 114 | fmt.Printf("Post %d unpublished\n", postID) 115 | return nil 116 | } 117 | 118 | func (pm *PostManager) PublishPost(postID int) error { 119 | pm.mu.Lock() 120 | post := pm.posts[postID] 121 | pm.mu.Unlock() 122 | 123 | if post == nil { 124 | return fmt.Errorf("post not found") 125 | } 126 | 127 | pm.mu.Lock() 128 | defer pm.mu.Unlock() 129 | 130 | post.PublishPost() 131 | fmt.Printf("Post %d published\n", postID) 132 | return nil 133 | } 134 | 135 | func (pm *PostManager) UpdatePost(postID int, content string) error { 136 | pm.mu.Lock() 137 | post := pm.posts[postID] 138 | pm.mu.Unlock() 139 | 140 | if post == nil { 141 | return fmt.Errorf("post not found") 142 | } 143 | 144 | pm.mu.Lock() 145 | defer pm.mu.Unlock() 146 | 147 | post.UpdateContent(content) 148 | fmt.Printf("Post %d updated with content: %s\n", postID, content) 149 | return nil 150 | } 151 | 152 | func (pm *PostManager) HidePostFromUser(postID, userID int) error { 153 | pm.mu.Lock() 154 | post := pm.posts[postID] 155 | pm.mu.Unlock() 156 | 157 | if post == nil { 158 | return fmt.Errorf("post not found") 159 | } 160 | 161 | pm.mu.Lock() 162 | defer pm.mu.Unlock() 163 | 164 | post.HideFromUser(userID) 165 | post.HiddenFromUsers[userID] = true 166 | fmt.Printf("Post %d hidden from user %d\n", postID, userID) 167 | return nil 168 | } 169 | 170 | func (pm *PostManager) UnhidePostFromUser(postID, userID int) error { 171 | pm.mu.Lock() 172 | post := pm.posts[postID] 173 | pm.mu.Unlock() 174 | 175 | if post == nil { 176 | return fmt.Errorf("post not found") 177 | } 178 | 179 | pm.mu.Lock() 180 | defer pm.mu.Unlock() 181 | 182 | post.UnhideFromUser(userID) 183 | delete(post.HiddenFromUsers, userID) 184 | fmt.Printf("Post %d unhidden from user %d\n", postID, userID) 185 | return nil 186 | } 187 | 188 | func (pm *PostManager) EnableComments(postID int) error { 189 | pm.mu.Lock() 190 | post := pm.posts[postID] 191 | pm.mu.Unlock() 192 | 193 | if post == nil { 194 | return fmt.Errorf("post not found") 195 | } 196 | 197 | pm.mu.Lock() 198 | defer pm.mu.Unlock() 199 | 200 | post.EnableComments() 201 | fmt.Printf("Comments enabled for post %d\n", postID) 202 | return nil 203 | } 204 | 205 | func (pm *PostManager) DisableComments(postID int) error { 206 | pm.mu.Lock() 207 | post := pm.posts[postID] 208 | pm.mu.Unlock() 209 | 210 | if post == nil { 211 | return fmt.Errorf("post not found") 212 | } 213 | 214 | pm.mu.Lock() 215 | defer pm.mu.Unlock() 216 | 217 | post.DisableComments() 218 | fmt.Printf("Comments disabled for post %d\n", postID) 219 | return nil 220 | } 221 | -------------------------------------------------------------------------------- /social_media_platform/user.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type User struct { 4 | ID int 5 | Name string 6 | Email string 7 | Password string 8 | DisplayPicture *string 9 | Bio *string 10 | friends map[int]*User 11 | posts []*Post 12 | } 13 | 14 | func NewUser(id int, name, email, password, displayPicture, bio string) *User { 15 | return &User{ID: id, Name: name, Email: email, Password: password, DisplayPicture: &displayPicture, Bio: &bio, friends: make(map[int]*User), posts: make([]*Post, 0)} 16 | } 17 | 18 | func (u *User) AddPost(post *Post) { 19 | u.posts = append(u.posts, post) 20 | } 21 | 22 | func (u *User) GetPosts() []*Post { 23 | return u.posts 24 | } 25 | 26 | func (u *User) AddFriend(friend *User) { 27 | u.friends[friend.ID] = friend 28 | } 29 | 30 | func (u *User) RemoveFriend(friend *User) { 31 | delete(u.friends, friend.ID) 32 | } 33 | 34 | func (u *User) GetFriends() []*User { 35 | friends := make([]*User, 0, len(u.friends)) 36 | for _, friend := range u.friends { 37 | friends = append(friends, friend) 38 | } 39 | return friends 40 | } 41 | -------------------------------------------------------------------------------- /social_media_platform/user_manager.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var ( 9 | userManagerInstance *UserManager 10 | userOnce sync.Once 11 | ) 12 | 13 | type UserManager struct { 14 | users map[int]*User 15 | mu sync.RWMutex 16 | } 17 | 18 | func GetUserManagerInstance() *UserManager { 19 | userOnce.Do(func() { 20 | userManagerInstance = &UserManager{users: make(map[int]*User)} 21 | }) 22 | return userManagerInstance 23 | } 24 | 25 | func (um *UserManager) GetUserByID(userID int) (*User, error) { 26 | um.mu.RLock() 27 | defer um.mu.RUnlock() 28 | user, ok := um.users[userID] 29 | if !ok { 30 | return nil, fmt.Errorf("user not found") 31 | } 32 | return user, nil 33 | } 34 | 35 | func (um *UserManager) AddUser(user *User) { 36 | um.mu.Lock() 37 | defer um.mu.Unlock() 38 | um.users[user.ID] = user 39 | fmt.Printf("User added: %d\n", user.ID) 40 | } 41 | 42 | func (um *UserManager) RemoveUser(userID int) { 43 | um.mu.Lock() 44 | defer um.mu.Unlock() 45 | delete(um.users, userID) 46 | fmt.Printf("User removed: %d\n", userID) 47 | } 48 | 49 | func (um *UserManager) LoginUser(email, password string) (*User, error) { 50 | um.mu.RLock() 51 | defer um.mu.RUnlock() 52 | for _, u := range um.users { 53 | if u.Email == email && u.Password == password { 54 | fmt.Printf("Login successful for email: %s\n", email) 55 | return u, nil 56 | } 57 | } 58 | return nil, fmt.Errorf("invalid email or password") 59 | } 60 | 61 | func (um *UserManager) UpdateUser(user *User) error { 62 | um.mu.Lock() 63 | defer um.mu.Unlock() 64 | um.users[user.ID] = user 65 | fmt.Printf("User updated: %d\n", user.ID) 66 | return nil 67 | } 68 | 69 | func (um *UserManager) AddFriend(requesterID, receiverID int) error { 70 | requester, err := um.GetUserByID(requesterID) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | receiver, err := um.GetUserByID(receiverID) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | requester.AddFriend(receiver) 81 | receiver.AddFriend(requester) 82 | fmt.Printf("Friendship added between users: %d and %d\n", requesterID, receiverID) 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /vending_machine/go.mod: -------------------------------------------------------------------------------- 1 | module lld-golang/vending_machine 2 | 3 | go 1.22.5 4 | -------------------------------------------------------------------------------- /vending_machine/inventory.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Inventory struct { 6 | Products map[int]*Product 7 | } 8 | 9 | func NewInventory() *Inventory { 10 | return &Inventory{Products: make(map[int]*Product)} 11 | } 12 | 13 | func (i *Inventory) AddProduct(product *Product) { 14 | i.Products[product.ID] = product 15 | fmt.Printf("Added product with ID %d\n", product.ID) 16 | } 17 | 18 | func (i *Inventory) DeleteProduct(id int) { 19 | delete(i.Products, id) 20 | fmt.Printf("Deleted product with ID %d\n", id) 21 | } 22 | 23 | func (i *Inventory) TransactProduct(id int) (*Product, error) { 24 | product, ok := i.Products[id] 25 | if !ok { 26 | return nil, fmt.Errorf("product with ID %d not found", id) 27 | } 28 | if product.Quantity > 0 { 29 | product.Quantity-- 30 | if product.Quantity == 0 { 31 | delete(i.Products, id) 32 | } 33 | fmt.Printf("Transacted product with ID %d, remaining quantity: %d\n", id, product.Quantity) 34 | return product, nil 35 | } 36 | return nil, fmt.Errorf("product with ID %d is out of stock", id) 37 | } 38 | 39 | func (i *Inventory) IsProductAvailable(id int) bool { 40 | product, ok := i.Products[id] 41 | return ok && product.Quantity > 0 42 | } 43 | -------------------------------------------------------------------------------- /vending_machine/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | vendingMachine := NewVendingMachine() 5 | 6 | // Add products to the inventory 7 | coke := NewProduct(1, "Coke", 1.50) 8 | pepsi := NewProduct(2, "Pepsi", 1.75) 9 | 10 | vendingMachine.inventory.AddProduct(coke) 11 | vendingMachine.inventory.AddProduct(pepsi) 12 | 13 | // Insert money into the vending machine 14 | vendingMachine.InsertMoney(2.00) 15 | 16 | // Select a product to purchase 17 | vendingMachine.SelectProduct(coke) 18 | 19 | // Dispense the selected product 20 | vendingMachine.DispenseProduct() 21 | 22 | // Return any remaining change 23 | vendingMachine.ReturnChange() 24 | 25 | // Insert more money to purchase another product with insufficient funds 26 | vendingMachine.InsertMoney(1.50) 27 | 28 | // Select a second product to purchase 29 | vendingMachine.SelectProduct(pepsi) 30 | 31 | // Dispense the selected product 32 | vendingMachine.DispenseProduct() 33 | 34 | // Return any remaining change 35 | vendingMachine.ReturnChange() 36 | 37 | } 38 | -------------------------------------------------------------------------------- /vending_machine/product.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Product struct { 4 | ID int 5 | Name string 6 | Price float64 7 | Quantity int 8 | } 9 | 10 | func NewProduct(id int, name string, price float64) *Product { 11 | return &Product{ID: id, Name: name, Price: price, Quantity: 3} 12 | } 13 | -------------------------------------------------------------------------------- /vending_machine/state.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type State interface { 6 | InsertMoney(amount float64) 7 | SelectProduct(product *Product) 8 | ReturnChange() 9 | DispenseProduct() 10 | } 11 | 12 | type MoneyInsertedState struct { 13 | vendingMachine *VendingMachine 14 | } 15 | 16 | func (s *MoneyInsertedState) InsertMoney(amount float64) { 17 | s.vendingMachine.insertedMoney += amount 18 | fmt.Printf("Inserted amount: $%.2f\n", amount) 19 | s.vendingMachine.UpdateState(s.vendingMachine.productSelectedState) 20 | } 21 | 22 | func (s *MoneyInsertedState) SelectProduct(product *Product) { 23 | fmt.Println("Insert money to proceed") 24 | } 25 | 26 | func (s *MoneyInsertedState) ReturnChange() { 27 | if s.vendingMachine.insertedMoney > 0 { 28 | change := s.vendingMachine.insertedMoney 29 | s.vendingMachine.insertedMoney = 0 30 | fmt.Printf("Returning change: $%.2f\n", change) 31 | } else { 32 | fmt.Println("No change to return.") 33 | } 34 | } 35 | 36 | func (s *MoneyInsertedState) DispenseProduct() { 37 | fmt.Println("Insert money to purchase product") 38 | } 39 | 40 | type ProductSelectedState struct { 41 | vendingMachine *VendingMachine 42 | } 43 | 44 | func (s *ProductSelectedState) InsertMoney(amount float64) { 45 | fmt.Println("Product already selected, please wait.") 46 | } 47 | 48 | func (s *ProductSelectedState) SelectProduct(product *Product) { 49 | if !s.vendingMachine.inventory.IsProductAvailable(product.ID) { 50 | fmt.Println("Product not available.") 51 | s.vendingMachine.UpdateState(s.vendingMachine.moneyInsertedState) 52 | return 53 | } 54 | 55 | if product.Price > s.vendingMachine.insertedMoney { 56 | fmt.Printf("Insufficient funds. Inserted: $%.2f, Price: $%.2f\n", s.vendingMachine.insertedMoney, product.Price) 57 | s.vendingMachine.UpdateState(s.vendingMachine.moneyInsertedState) 58 | } else { 59 | s.vendingMachine.selectedProduct = product 60 | fmt.Printf("Product %s selected.\n", product.Name) 61 | s.vendingMachine.UpdateState(s.vendingMachine.productDispensedState) 62 | } 63 | } 64 | 65 | func (s *ProductSelectedState) ReturnChange() { 66 | s.vendingMachine.moneyInsertedState.ReturnChange() 67 | } 68 | 69 | func (s *ProductSelectedState) DispenseProduct() { 70 | fmt.Println("Cannot dispense. Select product first.") 71 | } 72 | 73 | type ProductDispensedState struct { 74 | vendingMachine *VendingMachine 75 | } 76 | 77 | func (s *ProductDispensedState) InsertMoney(amount float64) { 78 | fmt.Println("Please wait. Product is being dispensed.") 79 | } 80 | 81 | func (s *ProductDispensedState) SelectProduct(product *Product) { 82 | fmt.Println("Product already dispensed.") 83 | } 84 | 85 | func (s *ProductDispensedState) ReturnChange() { 86 | s.vendingMachine.moneyInsertedState.ReturnChange() 87 | } 88 | 89 | func (s *ProductDispensedState) DispenseProduct() { 90 | product, err := s.vendingMachine.inventory.TransactProduct(s.vendingMachine.selectedProduct.ID) 91 | if err != nil { 92 | fmt.Println("Error dispensing product:", err) 93 | s.vendingMachine.UpdateState(s.vendingMachine.moneyInsertedState) 94 | return 95 | } 96 | 97 | change := s.vendingMachine.insertedMoney - product.Price 98 | s.vendingMachine.insertedMoney = 0 99 | if change > 0 { 100 | fmt.Printf("Returning change: $%.2f\n", change) 101 | } 102 | fmt.Printf("Dispensed product: %s\n", product.Name) 103 | s.vendingMachine.UpdateState(s.vendingMachine.moneyInsertedState) 104 | } 105 | -------------------------------------------------------------------------------- /vending_machine/vending_machine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type VendingMachine struct { 4 | inventory *Inventory 5 | currentState State 6 | moneyInsertedState *MoneyInsertedState 7 | productSelectedState *ProductSelectedState 8 | productDispensedState *ProductDispensedState 9 | selectedProduct *Product 10 | insertedMoney float64 11 | returnedChange float64 12 | } 13 | 14 | func NewVendingMachine() *VendingMachine { 15 | vendingMachine := &VendingMachine{inventory: NewInventory()} 16 | 17 | vendingMachine.moneyInsertedState = &MoneyInsertedState{vendingMachine} 18 | vendingMachine.productSelectedState = &ProductSelectedState{vendingMachine} 19 | vendingMachine.productDispensedState = &ProductDispensedState{vendingMachine} 20 | vendingMachine.currentState = &MoneyInsertedState{vendingMachine: vendingMachine} 21 | return vendingMachine 22 | } 23 | 24 | func (v *VendingMachine) InsertMoney(amount float64) { 25 | v.currentState.InsertMoney(amount) 26 | } 27 | 28 | func (v *VendingMachine) SelectProduct(product *Product) { 29 | v.currentState.SelectProduct(product) 30 | } 31 | 32 | func (v *VendingMachine) ReturnChange() { 33 | v.currentState.ReturnChange() 34 | } 35 | 36 | func (v *VendingMachine) DispenseProduct() { 37 | v.currentState.DispenseProduct() 38 | } 39 | 40 | func (v *VendingMachine) UpdateState(newState State) { 41 | v.currentState = newState 42 | } 43 | --------------------------------------------------------------------------------