diff --git a/Behavioral/ChainOfResponsibility/chain_of_responsibility.go b/Behavioral/ChainOfResponsibility/chain_of_responsibility.go index ed59a8d..e2364ed 100644 --- a/Behavioral/ChainOfResponsibility/chain_of_responsibility.go +++ b/Behavioral/ChainOfResponsibility/chain_of_responsibility.go @@ -1,52 +1,67 @@ // Package chain_of_responsibility is an example of the Chain Of Responsibility Pattern. +// +// In the following example, we are processing requests through a chain of handlers. +// Each handler either handles the request or passes it to the next handler in the chain. +// +// This usage example demonstrates: +// 1. Creating a chain of handlers (A -> B -> C) +// 2. Each handler processes messages with a specific value (1, 2, or 3) +// 3. Requests propagate through the chain until handled +// +// The example is meant to show how requests travel through the chain +// and how each handler decides to process or forward the request. package chain_of_responsibility -// Handler provides a handler interface. +// Handler defines the interface for processing requests in the chain. type Handler interface { + // SendRequest processes a message or forwards it to the next handler. SendRequest(message int) string } -// ConcreteHandlerA implements handler "A". -type ConcreteHandlerA struct { +// HandlerA handles messages with value 1. +type HandlerA struct { next Handler } -// SendRequest implementation. -func (h *ConcreteHandlerA) SendRequest(message int) (result string) { +// SendRequest processes message 1 or forwards to the next handler. +func (h *HandlerA) SendRequest(message int) string { if message == 1 { - result = "Im handler 1" - } else if h.next != nil { - result = h.next.SendRequest(message) + return "Handler A processed message 1" } - return + if h.next != nil { + return h.next.SendRequest(message) + } + return "" } -// ConcreteHandlerB implements handler "B". -type ConcreteHandlerB struct { +// HandlerB handles messages with value 2. +type HandlerB struct { next Handler } -// SendRequest implementation. -func (h *ConcreteHandlerB) SendRequest(message int) (result string) { +// SendRequest processes message 2 or forwards to the next handler. +func (h *HandlerB) SendRequest(message int) string { if message == 2 { - result = "Im handler 2" - } else if h.next != nil { - result = h.next.SendRequest(message) + return "Handler B processed message 2" + } + if h.next != nil { + return h.next.SendRequest(message) } - return + return "" } -// ConcreteHandlerC implements handler "C". -type ConcreteHandlerC struct { +// HandlerC handles messages with value 3. +type HandlerC struct { next Handler } -// SendRequest implementation. -func (h *ConcreteHandlerC) SendRequest(message int) (result string) { +// SendRequest processes message 3 or forwards to the next handler. +func (h *HandlerC) SendRequest(message int) string { if message == 3 { - result = "Im handler 3" - } else if h.next != nil { - result = h.next.SendRequest(message) + return "Handler C processed message 3" + } + if h.next != nil { + return h.next.SendRequest(message) } - return + return "" } diff --git a/Behavioral/Command/command.go b/Behavioral/Command/command.go index 3f9f5a3..cfff6be 100644 --- a/Behavioral/Command/command.go +++ b/Behavioral/Command/command.go @@ -1,4 +1,17 @@ // Package command is an example of the Command Pattern. +// +// In the following example, we are implementing a simple toggle system. +// Commands encapsulate requests as objects, allowing parameterization +// and queueing of operations. +// +// This usage example demonstrates: +// 1. Command interface for executing operations +// 2. Concrete commands (ToggleOn, ToggleOff) that call receiver methods +// 3. Receiver that performs the actual work +// 4. Invoker that stores and executes commands +// +// The example is meant to show how commands decouple the sender (Invoker) +// from the receiver, and how multiple commands can be queued and executed. package command // Command provides a command interface. @@ -11,7 +24,7 @@ type ToggleOnCommand struct { receiver *Receiver } -// Execute command. +// Execute calls ToggleOn on the receiver. func (c *ToggleOnCommand) Execute() string { return c.receiver.ToggleOn() } @@ -21,43 +34,44 @@ type ToggleOffCommand struct { receiver *Receiver } -// Execute command. +// Execute calls ToggleOff on the receiver. func (c *ToggleOffCommand) Execute() string { return c.receiver.ToggleOff() } -// Receiver implementation. +// Receiver performs the actual work when commands are executed. type Receiver struct { } -// ToggleOn implementation. +// ToggleOn returns string indicating device turned on. func (r *Receiver) ToggleOn() string { return "Toggle On" } -// ToggleOff implementation. +// ToggleOff returns string indicating device turned off. func (r *Receiver) ToggleOff() string { return "Toggle Off" } -// Invoker implementation. +// Invoker stores and executes commands. +// It acts as the sender that triggers command execution. type Invoker struct { commands []Command } -// StoreCommand adds command. +// StoreCommand adds a command to the execution queue. func (i *Invoker) StoreCommand(command Command) { i.commands = append(i.commands, command) } -// UnStoreCommand removes command. +// UnStoreCommand removes the last command from the queue. func (i *Invoker) UnStoreCommand() { if len(i.commands) != 0 { i.commands = i.commands[:len(i.commands)-1] } } -// Execute all commands. +// Execute runs all stored commands in order. func (i *Invoker) Execute() string { var result string for _, command := range i.commands { diff --git a/Behavioral/Iterator/iterator.go b/Behavioral/Iterator/iterator.go index 7ffb0ff..62d5402 100644 --- a/Behavioral/Iterator/iterator.go +++ b/Behavioral/Iterator/iterator.go @@ -1,7 +1,20 @@ // Package iterator is an example of the Iterator Pattern. +// +// In the following example, we are implementing a book shelf that can be +// traversed with an iterator. The iterator provides a way to access elements +// of a collection sequentially without exposing the underlying structure. +// +// This usage example demonstrates: +// 1. Iterator interface with navigation methods (Next, Prev, Reset, End) +// 2. Aggregate interface for creating iterators +// 3. Concrete collection (BookShelf) that stores books +// 4. Concrete iterator (BookIterator) that traverses the collection +// +// The example is meant to show how clients can iterate over a collection +// without knowing its internal representation. package iterator -// Iterator provides a iterator interface. +// Iterator provides an interface for traversing a collection. type Iterator interface { Index() int Value() interface{} @@ -12,37 +25,37 @@ type Iterator interface { End() } -// Aggregate provides a collection interface. +// Aggregate provides an interface for creating an iterator. type Aggregate interface { Iterator() Iterator } -// BookIterator implements the Iterator interface. +// BookIterator implements the Iterator interface for BookShelf. type BookIterator struct { shelf *BookShelf - index int - internal int + index int // current position for external access + internal int // internal position for navigation } -// Index returns current index +// Index returns the current position in the collection. func (i *BookIterator) Index() int { return i.index } -// Value returns current value +// Value returns the current book at the iterator position. func (i *BookIterator) Value() interface{} { + if i.index < 0 || i.index >= len(i.shelf.Books) { + return nil + } return i.shelf.Books[i.index] } -// Has implementation. +// Has checks if the current internal position is valid. func (i *BookIterator) Has() bool { - if i.internal < 0 || i.internal >= len(i.shelf.Books) { - return false - } - return true + return i.internal >= 0 && i.internal < len(i.shelf.Books) } -// Next goes to the next item. +// Next moves the iterator forward by one position. func (i *BookIterator) Next() { i.internal++ if i.Has() { @@ -50,7 +63,7 @@ func (i *BookIterator) Next() { } } -// Prev goes to the previous item. +// Prev moves the iterator backward by one position. func (i *BookIterator) Prev() { i.internal-- if i.Has() { @@ -58,34 +71,34 @@ func (i *BookIterator) Prev() { } } -// Reset resets iterator. +// Reset positions the iterator at the beginning. func (i *BookIterator) Reset() { i.index = 0 i.internal = 0 } -// End goes to the last item. +// End positions the iterator at the last element. func (i *BookIterator) End() { i.index = len(i.shelf.Books) - 1 i.internal = i.index } -// BookShelf implements the Aggregate interface. +// BookShelf implements the Aggregate interface and stores books. type BookShelf struct { Books []*Book } -// Iterator creates and returns the iterator over the collection. +// Iterator creates a new iterator for the book shelf. func (b *BookShelf) Iterator() Iterator { return &BookIterator{shelf: b} } -// Add adds an item to the collection. +// Add appends a book to the collection. func (b *BookShelf) Add(book *Book) { b.Books = append(b.Books, book) } -// Book implements a item of the collection. +// Book represents a single book in the collection. type Book struct { Name string } diff --git a/Behavioral/Mediator/mediator.go b/Behavioral/Mediator/mediator.go index af6621b..86fe014 100644 --- a/Behavioral/Mediator/mediator.go +++ b/Behavioral/Mediator/mediator.go @@ -1,123 +1,140 @@ // Package mediator is an example of the Mediator Pattern. +// +// In the following example, we are implementing a production chain: +// Farmer grows tomatoes, Cannery makes ketchup, Shop sells it. +// The mediator coordinates communication between all colleagues, +// reducing direct dependencies between them. +// +// This usage example demonstrates: +// 1. Mediator interface for communication between colleagues +// 2. Concrete mediator that coordinates Farmer, Cannery, and Shop +// 3. Colleagues that communicate only through the mediator +// 4. Money transfers and product transformations along the chain +// +// The example is meant to show how the mediator centralizes complex +// communication logic and keeps colleagues loosely coupled. package mediator -// Mediator provides a mediator interface. +// Mediator defines the interface for communication between colleagues. type Mediator interface { Notify(msg string) } -// Тип ConcreteMediator, реализует посредника +// ConcreteMediator implements the mediator and coordinates all colleagues. type ConcreteMediator struct { *Farmer *Cannery *Shop } -// Notify implementation. +// Notify handles messages from colleagues and coordinates the workflow. func (m *ConcreteMediator) Notify(msg string) { - if msg == "Farmer: Tomato complete..." { + switch msg { + case "Farmer: Tomato complete...": + // Farmer gets paid, Cannery starts production m.Cannery.AddMoney(-15000.00) m.Farmer.AddMoney(15000.00) m.Cannery.MakeKetchup(m.Farmer.GetTomato()) - } else if msg == "Cannery: Ketchup complete..." { + case "Cannery: Ketchup complete...": + // Cannery gets paid, Shop starts selling m.Shop.AddMoney(-30000.00) m.Cannery.AddMoney(30000.00) m.Shop.SellKetchup(m.Cannery.GetKetchup()) } } -// СonnectСolleagues connects all colleagues. -func СonnectСolleagues(farmer *Farmer, cannery *Cannery, shop *Shop) { +// ConnectColleagues creates a mediator and connects all colleagues together. +func ConnectColleagues(farmer *Farmer, cannery *Cannery, shop *Shop) { mediator := &ConcreteMediator{ Farmer: farmer, Cannery: cannery, Shop: shop, } - mediator.Farmer.SetMediator(mediator) - mediator.Cannery.SetMediator(mediator) - mediator.Shop.SetMediator(mediator) + farmer.SetMediator(mediator) + cannery.SetMediator(mediator) + shop.SetMediator(mediator) } -// Farmer implements a Farmer colleague +// Farmer represents a colleague that grows tomatoes. type Farmer struct { mediator Mediator tomato int money float64 } -// SetMediator sets mediator. +// SetMediator assigns a mediator to the farmer. func (f *Farmer) SetMediator(mediator Mediator) { f.mediator = mediator } -// AddMoney adds money. +// AddMoney changes the farmer's balance. func (f *Farmer) AddMoney(m float64) { f.money += m } -// GrowTomato implementation. +// GrowTomato starts the production chain by growing tomatoes. func (f *Farmer) GrowTomato(tomato int) { f.tomato = tomato f.money -= 7500.00 f.mediator.Notify("Farmer: Tomato complete...") } -// GetTomato returns tomatos. +// GetTomato returns the amount of grown tomatoes. func (f *Farmer) GetTomato() int { return f.tomato } -// Cannery implements a Cannery colleague. +// Cannery represents a colleague that processes tomatoes into ketchup. type Cannery struct { mediator Mediator ketchup int money float64 } -// SetMediator sets mediator. +// SetMediator assigns a mediator to the cannery. func (c *Cannery) SetMediator(mediator Mediator) { c.mediator = mediator } -// AddMoney adds money. +// AddMoney changes the cannery's balance. func (c *Cannery) AddMoney(m float64) { c.money += m } -// MakeKetchup implementation. +// MakeKetchup processes tomatoes into ketchup. func (c *Cannery) MakeKetchup(tomato int) { c.ketchup = tomato c.mediator.Notify("Cannery: Ketchup complete...") } -// GetKetchup returns ketchup. +// GetKetchup returns the amount of produced ketchup. func (c *Cannery) GetKetchup() int { return c.ketchup } -// Shop implements a Shop colleague. +// Shop represents a colleague that sells ketchup to customers. type Shop struct { mediator Mediator money float64 } -// SetMediator sets mediator. +// SetMediator assigns a mediator to the shop. func (s *Shop) SetMediator(mediator Mediator) { s.mediator = mediator } -// AddMoney adds money. +// AddMoney changes the shop's balance. func (s *Shop) AddMoney(m float64) { s.money += m } -// SellKetchup converts ketchup to money. +// SellKetchup converts ketchup inventory into money. func (s *Shop) SellKetchup(ketchup int) { s.money = float64(ketchup) * 54.75 } -// GetMoney returns money. +// GetMoney returns the shop's current balance. func (s *Shop) GetMoney() float64 { return s.money } diff --git a/Behavioral/Memento/memento.go b/Behavioral/Memento/memento.go index 3489630..ba9805e 100644 --- a/Behavioral/Memento/memento.go +++ b/Behavioral/Memento/memento.go @@ -1,32 +1,46 @@ // Package memento is an example of the Memento Pattern. +// +// In the following example, we are implementing an undo mechanism. +// Originator creates and restores its state, Memento stores snapshots, +// and Caretaker manages the saved states without examining them. +// +// This usage example demonstrates: +// 1. Originator that can save and restore its state +// 2. Memento that stores state snapshots (immutable) +// 3. Caretaker that holds mementos without accessing their contents +// +// The example is meant to show how state can be captured and restored +// without breaking encapsulation. package memento -// Originator implements a state master. +// Originator represents an object whose state needs to be saved and restored. type Originator struct { State string } -// CreateMemento returns state storage. +// CreateMemento saves the current state into a memento. func (o *Originator) CreateMemento() *Memento { return &Memento{state: o.State} } -// SetMemento sets old state. +// SetMemento restores a previously saved state from a memento. func (o *Originator) SetMemento(memento *Memento) { o.State = memento.GetState() } -// Memento implements storage for the state of Originator +// Memento stores a snapshot of the Originator's state. +// It provides restricted access - only Originator can read the state. type Memento struct { state string } -// GetState returns state. +// GetState returns the stored state (intended for Originator use only). func (m *Memento) GetState() string { return m.state } -// Caretaker keeps Memento until it is needed by Originator. +// Caretaker manages mementos without modifying or inspecting them. +// It's responsible for safekeeping and returning mementos to Originator. type Caretaker struct { Memento *Memento } diff --git a/Behavioral/Observer/observer.go b/Behavioral/Observer/observer.go index 2dc620a..f870615 100644 --- a/Behavioral/Observer/observer.go +++ b/Behavioral/Observer/observer.go @@ -1,16 +1,33 @@ // Package observer is an example of the Observer Pattern. -// Push model. +// Push model - publisher sends state to all observers. +// +// In the following example, we are implementing a notification system. +// Publisher maintains a list of observers and notifies them of state changes. +// Observers automatically update when publisher's state changes. +// +// This usage example demonstrates: +// 1. Publisher interface for attaching observers and sending notifications +// 2. Observer interface for receiving updates +// 3. Concrete publisher that tracks state and notifies all observers +// 4. Concrete observers that update their state when notified +// +// The example uses the push model where publisher sends the state +// directly to all observers during notification. package observer -// Publisher interface. +// Publisher defines the interface for managing observers and sending updates. type Publisher interface { + // Attach adds a new observer to the notification list. Attach(observer Observer) + // SetState changes the publisher's internal state. SetState(state string) + // Notify sends the current state to all attached observers. Notify() } -// Observer provides a subscriber interface. +// Observer defines the interface for receiving updates from publisher. type Observer interface { + // Update is called by publisher when state changes. Update(state string) } @@ -20,26 +37,26 @@ type ConcretePublisher struct { state string } -// NewPublisher is the Publisher constructor. +// NewPublisher creates and returns a new ConcretePublisher. func NewPublisher() Publisher { return &ConcretePublisher{} } -// Attach a Observer -func (s *ConcretePublisher) Attach(observer Observer) { - s.observers = append(s.observers, observer) +// Attach adds a new observer to the notification list. +func (p *ConcretePublisher) Attach(observer Observer) { + p.observers = append(p.observers, observer) } -// SetState sets new state -func (s *ConcretePublisher) SetState(state string) { - s.state = state +// SetState updates the publisher's internal state. +func (p *ConcretePublisher) SetState(state string) { + p.state = state } -// Notify sends notifications to subscribers. -// Push model. -func (s *ConcretePublisher) Notify() { - for _, observer := range s.observers { - observer.Update(s.state) +// Notify sends the current state to all attached observers. +// Push model - publisher pushes state to observers. +func (p *ConcretePublisher) Notify() { + for _, observer := range p.observers { + observer.Update(p.state) } } @@ -48,7 +65,7 @@ type ConcreteObserver struct { state string } -// Update set new state -func (s *ConcreteObserver) Update(state string) { - s.state = state +// Update sets the observer's state to match the publisher's state. +func (o *ConcreteObserver) Update(state string) { + o.state = state } diff --git a/Behavioral/README.md b/Behavioral/README.md index 627767c..ded6cfe 100644 --- a/Behavioral/README.md +++ b/Behavioral/README.md @@ -1,34 +1,33 @@ - ## Поведенческие паттерны (Behavioral) Поведенческие паттерны делятся на два типа: 1. Паттерны уровня класса -2. Паттерны уровня объекта. +2. Паттерны уровня объекта -Паттерны уровня класса описывают взаимодействия между классами и их подклассами. Такие отношения выражаются путем наследования и реализации классов. Тут базовый класс определяет интерфейс, а подклассы - реализацию. +Паттерны уровня класса описывают взаимодействие между классами и их подклассами. Такие отношения выражаются через наследование и реализацию интерфейсов. Базовый класс определяет интерфейс, а подклассы предоставляют конкретную реализацию. -Паттерны уровня объекта описывают взаимодействия между объектами. Такие отношения выражаются связями - ассоциацией, агрегацией и композицией. Тут структуры строятся путем объединения объектов некоторых классов. +Паттерны уровня объекта описывают взаимодействие между объектами. Такие отношения выражаются через связи: ассоциацию, агрегацию и композицию. Структуры строятся путем объединения объектов различных классов. -Ассоциация - отношение, когда объекты двух классов могут ссылаться один на другой. Например, свойство класса содержит экземпляр другого класса. +**Ассоциация** - отношение, при котором объекты двух классов могут ссылаться друг на друга. Например, свойство одного класса содержит экземпляр другого класса. -Агрегация – частная форма ассоциации. Агрегация применяется, когда один объект должен быть контейнером для других объектов и время существования этих объектов никак не зависит от времени существования объекта контейнера. Вообщем, если контейнер будет уничтожен, то входящие в него объекты не пострадают. Например, мы создали объект, а потом передали его в объект контейнер, каким-либо образом, можно в метод объекта контейнера передать или присвоить сразу свойству контейнера извне. Значит при удалении контейнера мы ни как не затронем наш созданный объект, который может взаимодействовать и с другими контейнерами. +**Агрегация** - частная форма ассоциации. Агрегация применяется, когда один объект выступает контейнером для других объектов, но время жизни этих объектов не зависит от времени жизни контейнера. Если контейнер будет уничтожен, входящие в него объекты продолжают существовать. Например, мы создаем объект и передаем его в контейнер через метод или присваиваем свойству. При удалении контейнера объект остается доступным и может использоваться другими контейнерами. -Композиция – Тоже самое, что и агрегация, но составные объекты не могут существовать отдельно от объекта контейнера и если контейнер будет уничтожен, то всё его содержимое будет уничтожено тоже. Например, мы создали объект в методе объекта контейнера и присвоили его свойству объекта контейнера. Из вне, о нашем созданном объекте никто не знает, значит, при удалении контейнера, созданный объект убудет удален так же, т.к. на него нет ссылки извне. +**Композиция** - более строгая форма агрегации. Составные объекты не могут существовать отдельно от контейнера. Если контейнер уничтожается, всё его содержимое уничтожается тоже. Например, объект создается внутри метода контейнера и присваивается его свойству. Извне об этом объекте никто не знает, поэтому при удалении контейнера объект становится недоступным и удаляется сборщиком мусора. К паттернам уровня класса относится только «Шаблонный метод». -Поведенческие паттерны описывают взаимодействие объектов и классов между собой и пытаются добиться наименьшей степени связанности компонентов системы друг с другом делая систему более гибкой. - -* [Цепочка ответственности (Chain Of Responsibility)](ChainOfResponsibility) -* [Команда (Command)](Command) -* [Итератор (Iterator)](Iterator) -* [Посредник (Mediator)](Mediator) -* [Хранитель (Memento)](Memento) -* [Наблюдатель (Observer)](Observer) -* [Состояние (State)](State) -* [Стратегия (Strategy)](Strategy) -* [Шаблонный метод (Template Method)](TemplateMethod) -* [Посетитель (Visitor)](Visitor) +Поведенческие паттерны описывают взаимодействие объектов и классов между собой, стремясь к минимальной связанности компонентов системы. Это делает систему более гибкой и легкой для расширения. + +* [Цепочка ответственности (Chain of Responsibility)](ChainOfResponsibility) +* [Команда (Command)](Command) +* [Итератор (Iterator)](Iterator) +* [Посредник (Mediator)](Mediator) +* [Хранитель (Memento)](Memento) +* [Наблюдатель (Observer)](Observer) +* [Состояние (State)](State) +* [Стратегия (Strategy)](Strategy) +* [Шаблонный метод (Template Method)](TemplateMethod) +* [Посетитель (Visitor)](Visitor) ## -~- THE END -~- \ No newline at end of file diff --git a/Behavioral/State/state.go b/Behavioral/State/state.go index 298cc8a..7b77c85 100644 --- a/Behavioral/State/state.go +++ b/Behavioral/State/state.go @@ -1,45 +1,58 @@ // Package state is an example of the State Pattern. +// +// In the following example, we are implementing a mobile phone alert system. +// The phone can be in different alert states (vibration, song) and its behavior +// changes depending on the current state. +// +// This usage example demonstrates: +// 1. State interface defining behavior common to all states +// 2. Context (MobileAlert) that maintains a reference to current state +// 3. Concrete states (Vibration, Song) with specific implementations +// 4. Dynamic state changes at runtime +// +// The example is meant to show how an object can alter its behavior +// when its internal state changes. package state -// MobileAlertStater provides a common interface for various states. +// MobileAlertStater defines the interface for all alert states. type MobileAlertStater interface { Alert() string } -// MobileAlert implements an alert depending on its state. +// MobileAlert represents the context that changes behavior based on state. type MobileAlert struct { state MobileAlertStater } -// Alert returns a alert string +// Alert delegates the alert behavior to the current state. func (a *MobileAlert) Alert() string { return a.state.Alert() } -// SetState changes state +// SetState changes the current alert state at runtime. func (a *MobileAlert) SetState(state MobileAlertStater) { a.state = state } -// NewMobileAlert is the MobileAlert constructor. +// NewMobileAlert creates a new alert with default vibration state. func NewMobileAlert() *MobileAlert { return &MobileAlert{state: &MobileAlertVibration{}} } -// MobileAlertVibration implements vibration alert +// MobileAlertVibration implements vibration alert behavior. type MobileAlertVibration struct { } -// Alert returns a alert string -func (a *MobileAlertVibration) Alert() string { +// Alert returns vibration notification string. +func (v *MobileAlertVibration) Alert() string { return "Vrrr... Brrr... Vrrr..." } -// MobileAlertSong implements beep alert +// MobileAlertSong implements song alert behavior. type MobileAlertSong struct { } -// Alert returns a alert string -func (a *MobileAlertSong) Alert() string { +// Alert returns song notification string. +func (s *MobileAlertSong) Alert() string { return "Белые розы, Белые розы. Беззащитны шипы..." } diff --git a/Behavioral/Strategy/strategy.go b/Behavioral/Strategy/strategy.go index b6b7a1a..0157ba1 100644 --- a/Behavioral/Strategy/strategy.go +++ b/Behavioral/Strategy/strategy.go @@ -1,64 +1,90 @@ -// Package strategy is an example of the Strategy Pattern. +// Package strategy provides implementations of the Strategy pattern +// for sorting algorithms. +// +// In the following example, we are implementing different sorting algorithms +// that can be interchanged at runtime. The Context delegates sorting to the +// currently selected strategy. +// +// This usage example demonstrates: +// 1. Strategy interface (Sorter) that all algorithms implement +// 2. Concrete strategies (BubbleSort, InsertionSort) with different algorithms +// 3. Context that maintains a reference to current strategy +// 4. Dynamic strategy switching at runtime +// +// The example is meant to show how algorithms can be selected and changed +// without modifying the client code. package strategy -// StrategySort provides an interface for sort algorithms. -type StrategySort interface { - Sort([]int) +// Sorter defines the interface for sorting algorithms. +// +// Example: +// +// sorter := &BubbleSort{} +// data := []int{3, 1, 4, 1, 5} +// sorter.Sort(data) +type Sorter interface { + Sort(data []int) } -// BubbleSort implements bubble sort algorithm. -type BubbleSort struct { -} +// BubbleSort implements the bubble sort algorithm. +// Time complexity: O(n²) average and worst case, O(n) best case. +type BubbleSort struct{} -// Sort sorts data. -func (s *BubbleSort) Sort(a []int) { - size := len(a) - if size < 2 { +// Sort performs an in-place bubble sort on the input slice. +func (b *BubbleSort) Sort(data []int) { + if len(data) < 2 { return } - for i := 0; i < size; i++ { - for j := size - 1; j >= i+1; j-- { - if a[j] < a[j-1] { - a[j], a[j-1] = a[j-1], a[j] + + swapped := true + for swapped { + swapped = false + for i := 0; i < len(data)-1; i++ { + if data[i] > data[i+1] { + data[i], data[i+1] = data[i+1], data[i] + swapped = true } } } } -// InsertionSort implements insertion sort algorithm. -type InsertionSort struct { -} +// InsertionSort implements the insertion sort algorithm. +// Time complexity: O(n²) average and worst case, O(n) best case. +type InsertionSort struct{} -// Sort sorts data. -func (s *InsertionSort) Sort(a []int) { - size := len(a) - if size < 2 { +// Sort performs an in-place insertion sort on the input slice. +func (i *InsertionSort) Sort(data []int) { + if len(data) < 2 { return } - for i := 1; i < size; i++ { - var j int - var buff = a[i] - for j = i - 1; j >= 0; j-- { - if a[j] < buff { - break - } - a[j+1] = a[j] + + for i := 1; i < len(data); i++ { + curr := data[i] + j := i - 1 + for j >= 0 && data[j] > curr { + data[j+1] = data[j] + j-- } - a[j+1] = buff + data[j+1] = curr } } -// Context provides a context for execution of a strategy. +// Context holds a sorting strategy and delegates sorting to it. +// It allows switching between different algorithms at runtime. type Context struct { - strategy StrategySort + strategy Sorter } -// Algorithm replaces strategies. -func (c *Context) Algorithm(a StrategySort) { - c.strategy = a +// SetStrategy replaces the current sorting strategy. +func (c *Context) SetStrategy(s Sorter) { + c.strategy = s } -// Sort sorts data according to the chosen strategy. -func (c *Context) Sort(s []int) { - c.strategy.Sort(s) +// Sort sorts data using the current strategy. +// Panics if no strategy is set. +func (c *Context) Sort(data []int) { + if c.strategy == nil { + panic("strategy: no strategy set") + } + c.strategy.Sort(data) } diff --git a/Behavioral/TemplateMethod/template_method.go b/Behavioral/TemplateMethod/template_method.go index fd0428a..2387636 100644 --- a/Behavioral/TemplateMethod/template_method.go +++ b/Behavioral/TemplateMethod/template_method.go @@ -1,53 +1,66 @@ // Package template_method is an example of the Template Method Pattern. +// +// In the following example, we are implementing a text formatter that wraps +// strings in different styles of quotation marks. The template method defines +// the skeleton of the algorithm (open + string + close), while subclasses +// provide the specific implementation for each quote style. +// +// This usage example demonstrates: +// 1. Template method (Quotes) that defines the algorithm structure +// 2. Interface for primitive operations (Open, Close) +// 3. Concrete implementations for different quote styles (French, German) +// // In fact, this pattern is based on Abstract Class and Polymorphism. -// But there’s nothing like that in Go, so the composition will be applied. +// But there's nothing like that in Go, so composition is applied instead. package template_method -// QuotesInterface provides an interface for setting different quotes. +// QuotesInterface defines the primitive operations that concrete types must implement. +// These operations are used by the template method to build the final result. type QuotesInterface interface { Open() string Close() string } -// Quotes implements a Template Method. +// Quotes implements the template method using composition. type Quotes struct { QuotesInterface } -// Quotes is the Template Method. +// Quotes is the template method that defines the algorithm skeleton. +// It calls primitive operations implemented by concrete types. func (q *Quotes) Quotes(str string) string { return q.Open() + str + q.Close() } -// NewQuotes is the Quotes constructor. +// NewQuotes creates a new Quotes with the provided quote style implementation. func NewQuotes(qt QuotesInterface) *Quotes { return &Quotes{qt} } -// FrenchQuotes implements wrapping the string in French quotes. +// FrenchQuotes implements string wrapping in French quotes. type FrenchQuotes struct { } -// Open sets opening quotes. -func (q *FrenchQuotes) Open() string { +// Open returns the French opening quotation mark. +func (f *FrenchQuotes) Open() string { return "«" } -// Close sets closing quotes. -func (q *FrenchQuotes) Close() string { +// Close returns the French closing quotation mark. +func (f *FrenchQuotes) Close() string { return "»" } -// GermanQuotes implements wrapping the string in German quotes. +// GermanQuotes implements string wrapping in German quotes. type GermanQuotes struct { } -// Open sets opening quotes. -func (q *GermanQuotes) Open() string { +// Open returns the German opening quotation mark. +func (g *GermanQuotes) Open() string { return "„" } -// Close sets closing quotes. -func (q *GermanQuotes) Close() string { +// Close returns the German closing quotation mark. +func (g *GermanQuotes) Close() string { return "“" } diff --git a/Behavioral/Visitor/visitor.go b/Behavioral/Visitor/visitor.go index 460f55a..0087e29 100644 --- a/Behavioral/Visitor/visitor.go +++ b/Behavioral/Visitor/visitor.go @@ -1,48 +1,62 @@ // Package visitor is an example of the Visitor Pattern. +// +// In the following example, we are implementing a city exploration system. +// A person (visitor) can visit different places (sushi bar, pizzeria, burger bar) +// and perform place-specific actions without modifying the places themselves. +// +// This usage example demonstrates: +// 1. Visitor interface with methods for each place type +// 2. Place interface that accepts visitors +// 3. Concrete visitor (People) that implements operations for all places +// 4. Concrete places with their specific behaviors +// 5. Collection (City) that can be visited as a whole +// +// The example is meant to show how to add new operations to existing +// object structures without modifying them. package visitor -// Visitor provides a visitor interface. +// Visitor defines operations to be performed on different types of places. type Visitor interface { VisitSushiBar(p *SushiBar) string VisitPizzeria(p *Pizzeria) string VisitBurgerBar(p *BurgerBar) string } -// Place provides an interface for place that the visitor should visit. +// Place defines an interface for elements that can be visited. type Place interface { Accept(v Visitor) string } -// People implements the Visitor interface. +// People implements the Visitor interface for a person exploring places. type People struct { } -// VisitSushiBar implements visit to SushiBar. +// VisitSushiBar implements visiting a sushi bar. func (v *People) VisitSushiBar(p *SushiBar) string { return p.BuySushi() } -// VisitPizzeria implements visit to Pizzeria. +// VisitPizzeria implements visiting a pizzeria. func (v *People) VisitPizzeria(p *Pizzeria) string { return p.BuyPizza() } -// VisitBurgerBar implements visit to BurgerBar. +// VisitBurgerBar implements visiting a burger bar. func (v *People) VisitBurgerBar(p *BurgerBar) string { return p.BuyBurger() } -// City implements a collection of places to visit. +// City represents a collection of places that can be visited. type City struct { places []Place } -// Add appends Place to the collection. +// Add appends a new place to the city. func (c *City) Add(p Place) { c.places = append(c.places, p) } -// Accept implements a visit to all places in the city. +// Accept makes the visitor visit all places in the city. func (c *City) Accept(v Visitor) string { var result string for _, p := range c.places { @@ -51,44 +65,44 @@ func (c *City) Accept(v Visitor) string { return result } -// SushiBar implements the Place interface. +// SushiBar represents a place that sells sushi. type SushiBar struct { } -// Accept implementation. +// Accept allows a visitor to visit the sushi bar. func (s *SushiBar) Accept(v Visitor) string { return v.VisitSushiBar(s) } -// BuySushi implementation. +// BuySushi returns the sushi bar's specific operation. func (s *SushiBar) BuySushi() string { return "Buy sushi..." } -// Pizzeria implements the Place interface. +// Pizzeria represents a place that sells pizza. type Pizzeria struct { } -// Accept implementation. +// Accept allows a visitor to visit the pizzeria. func (p *Pizzeria) Accept(v Visitor) string { return v.VisitPizzeria(p) } -// BuyPizza implementation. +// BuyPizza returns the pizzeria's specific operation. func (p *Pizzeria) BuyPizza() string { return "Buy pizza..." } -// BurgerBar implements the Place interface. +// BurgerBar represents a place that sells burgers. type BurgerBar struct { } -// Accept implementation. +// Accept allows a visitor to visit the burger bar. func (b *BurgerBar) Accept(v Visitor) string { return v.VisitBurgerBar(b) } -// BuyBurger implementation. +// BuyBurger returns the burger bar's specific operation. func (b *BurgerBar) BuyBurger() string { return "Buy burger..." } diff --git a/Creational/AbstractFactory/abstract_factory.go b/Creational/AbstractFactory/abstract_factory.go index 7e0cfc8..37d4f0c 100644 --- a/Creational/AbstractFactory/abstract_factory.go +++ b/Creational/AbstractFactory/abstract_factory.go @@ -1,70 +1,88 @@ // Package abstract_factory is an example of the Abstract Factory Pattern. +// +// In the following example, we are implementing a drink production system. +// CocaColaFactory creates a family of related products: CocaColaWater and +// CocaColaBottle. The factory ensures that products from the same family +// work together correctly. +// +// This usage example demonstrates: +// 1. Abstract factory interface for creating product families +// 2. Abstract product interfaces (Water, Bottle) that products must implement +// 3. Concrete factory (CocaColaFactory) that creates specific products +// 4. Concrete products (CocaColaWater, CocaColaBottle) that work together +// 5. Interaction between products (bottle receives water) +// +// The example is meant to show how to create families of related objects +// without specifying their concrete classes. package abstract_factory -// AbstractFactory provides an interface for creating families of related objects. +// AbstractFactory defines an interface for creating families of related products. type AbstractFactory interface { CreateWater(volume float64) AbstractWater CreateBottle(volume float64) AbstractBottle } -// AbstractWater provides a water interface. +// AbstractWater defines the interface for water/drink products. type AbstractWater interface { GetVolume() float64 } -// AbstractBottle provides a bottle interface. +// AbstractBottle defines the interface for bottle products. type AbstractBottle interface { - PourWater(water AbstractWater) // Bottle interacts with a water. + PourWater(water AbstractWater) // Bottle interacts with water GetBottleVolume() float64 GetWaterVolume() float64 } -// CocaColaFactory implements AbstractFactory interface. +// CocaColaFactory implements AbstractFactory for Coca-Cola products. type CocaColaFactory struct { } -// NewCocaColaFactory is the CocaColaFactory constructor. +// NewCocaColaFactory creates a new Coca-Cola factory. func NewCocaColaFactory() AbstractFactory { return &CocaColaFactory{} } -// CreateWater implementation. +// CreateWater creates a Coca-Cola water product. func (f *CocaColaFactory) CreateWater(volume float64) AbstractWater { return &CocaColaWater{volume: volume} } -// CreateBottle implementation. +// CreateBottle creates a Coca-Cola bottle product. func (f *CocaColaFactory) CreateBottle(volume float64) AbstractBottle { return &CocaColaBottle{volume: volume} } -// CocaColaWater implements AbstractWater. +// CocaColaWater implements AbstractWater for Coca-Cola. type CocaColaWater struct { - volume float64 // Volume of drink. + volume float64 } -// GetVolume returns volume of drink. +// GetVolume returns the volume of water in the container. func (w *CocaColaWater) GetVolume() float64 { return w.volume } -// CocaColaBottle implements AbstractBottle. +// CocaColaBottle implements AbstractBottle for Coca-Cola. type CocaColaBottle struct { - water AbstractWater // Bottle must contain a drink. - volume float64 // Volume of bottle. + water AbstractWater // Current water in the bottle + volume float64 // Maximum bottle capacity } -// PourWater pours water into a bottle. +// PourWater pours water into the bottle. func (b *CocaColaBottle) PourWater(water AbstractWater) { b.water = water } -// GetBottleVolume returns volume of bottle. +// GetBottleVolume returns the bottle's capacity. func (b *CocaColaBottle) GetBottleVolume() float64 { return b.volume } -// GetWaterVolume returns volume of water. +// GetWaterVolume returns the volume of water currently in the bottle. func (b *CocaColaBottle) GetWaterVolume() float64 { + if b.water == nil { + return 0 + } return b.water.GetVolume() } diff --git a/Creational/Builder/builder.go b/Creational/Builder/builder.go index 59f4302..62c1c77 100644 --- a/Creational/Builder/builder.go +++ b/Creational/Builder/builder.go @@ -1,51 +1,67 @@ // Package builder is an example of the Builder Pattern. +// +// In the following example, we are implementing a document builder. +// The Director defines the construction steps, while the ConcreteBuilder +// implements how to build each part. This separates the construction +// process from the representation. +// +// This usage example demonstrates: +// 1. Builder interface defining steps to build a product +// 2. Director that controls the construction process +// 3. ConcreteBuilder that implements the building steps +// 4. Product that is the final result of construction +// +// The example is meant to show how to construct complex objects step by step +// and create different representations using the same construction process. package builder -// Builder provides a builder interface. +// Builder defines the interface for creating parts of a product. type Builder interface { MakeHeader(str string) MakeBody(str string) MakeFooter(str string) } -// Director implements a manager +// Director orchestrates the construction process. +// It defines the order in which building steps are executed. type Director struct { builder Builder } -// Construct tells the builder what to do and in what order. +// Construct executes the building steps in a specific order. func (d *Director) Construct() { d.builder.MakeHeader("Header") d.builder.MakeBody("Body") d.builder.MakeFooter("Footer") } -// ConcreteBuilder implements Builder interface. +// ConcreteBuilder implements the Builder interface to construct +// and assemble parts of the product. type ConcreteBuilder struct { product *Product } -// MakeHeader builds a header of document.. +// MakeHeader adds a header section to the document. func (b *ConcreteBuilder) MakeHeader(str string) { b.product.Content += "
" + str + "
" } -// MakeBody builds a body of document. +// MakeBody adds a body section to the document. func (b *ConcreteBuilder) MakeBody(str string) { b.product.Content += "
" + str + "
" } -// MakeFooter builds a footer of document. +// MakeFooter adds a footer section to the document. func (b *ConcreteBuilder) MakeFooter(str string) { b.product.Content += "" } -// Product implementation. +// Product represents the complex object being built. type Product struct { Content string } -// Show returns product. +// Show returns the final product content. func (p *Product) Show() string { return p.Content } diff --git a/Creational/FactoryMethod/factory_method.go b/Creational/FactoryMethod/factory_method.go index 5e39889..55fbd3e 100644 --- a/Creational/FactoryMethod/factory_method.go +++ b/Creational/FactoryMethod/factory_method.go @@ -1,11 +1,25 @@ // Package factory_method is an example of the Factory Method pattern. +// +// In the following example, we are implementing a product creation system. +// The Creator defines a factory method that creates products based on an action. +// Concrete products implement a common interface, allowing them to be used +// interchangeably. +// +// This usage example demonstrates: +// 1. Creator interface with factory method for creating products +// 2. Product interface that all products must implement +// 3. ConcreteCreator that implements the factory method +// 4. Concrete products (A, B, C) with specific implementations +// +// The example is meant to show how to delegate object creation to subclasses +// and provide a way to create objects without specifying exact classes. package factory_method import ( "log" ) -// action helps clients to find out available actions. +// action represents the type of product to create. type action string const ( @@ -14,27 +28,27 @@ const ( C action = "C" ) -// Creator provides a factory interface. +// Creator defines the factory method interface. type Creator interface { CreateProduct(action action) Product // Factory Method } -// Product provides a product interface. -// All products returned by factory must provide a single interface. +// Product defines the interface for all products created by the factory. type Product interface { - Use() string // Every product should be usable + Use() string // Every product must be usable } -// ConcreteCreator implements Creator interface. +// ConcreteCreator implements the Creator interface. type ConcreteCreator struct{} -// NewCreator is the ConcreteCreator constructor. +// NewCreator creates a new ConcreteCreator. func NewCreator() Creator { return &ConcreteCreator{} } -// CreateProduct is a Factory Method. -func (p *ConcreteCreator) CreateProduct(action action) Product { +// CreateProduct is the factory method that creates products based on action. +// It returns a product matching the requested action or logs fatal error. +func (c *ConcreteCreator) CreateProduct(action action) Product { var product Product switch action { @@ -51,32 +65,32 @@ func (p *ConcreteCreator) CreateProduct(action action) Product { return product } -// ConcreteProductA implements product "A". +// ConcreteProductA implements Product for action A. type ConcreteProductA struct { action string } -// Use returns product action. +// Use returns the product's action identifier. func (p *ConcreteProductA) Use() string { return p.action } -// ConcreteProductB implements product "B". +// ConcreteProductB implements Product for action B. type ConcreteProductB struct { action string } -// Use returns product action. +// Use returns the product's action identifier. func (p *ConcreteProductB) Use() string { return p.action } -// ConcreteProductC implements product "C". +// ConcreteProductC implements Product for action C. type ConcreteProductC struct { action string } -// Use returns product action. +// Use returns the product's action identifier. func (p *ConcreteProductC) Use() string { return p.action } diff --git a/Creational/Prototype/prototype.go b/Creational/Prototype/prototype.go index a9dd13e..ac1ffdf 100644 --- a/Creational/Prototype/prototype.go +++ b/Creational/Prototype/prototype.go @@ -1,30 +1,42 @@ -// Package prototype is an example of the Singleton Pattern. +// Package prototype is an example of the Prototype Pattern. +// +// In the following example, we are implementing a cloning system. +// The Prototyper interface defines a method for cloning objects, +// allowing new objects to be created by copying existing ones. +// +// This usage example demonstrates: +// 1. Prototyper interface with Clone method +// 2. Concrete product that implements the cloning interface +// 3. Creating new objects by cloning existing ones +// +// The example is meant to show how to create new objects by copying +// existing instances, which can be more efficient than creating from scratch. package prototype -// Prototyper provides a cloning interface. +// Prototyper defines the interface for cloning objects. type Prototyper interface { Clone() Prototyper GetName() string } -// ConcreteProduct implements product "A" +// ConcreteProduct implements a product that can be cloned. type ConcreteProduct struct { - name string // Имя продукта + name string } -// NewConcreteProduct is the Prototyper constructor. +// NewConcreteProduct creates a new product with the given name. func NewConcreteProduct(name string) Prototyper { return &ConcreteProduct{ name: name, } } -// GetName returns product name +// GetName returns the product's name. func (p *ConcreteProduct) GetName() string { return p.name } -// Clone returns a cloned object. +// Clone creates and returns a copy of the product. func (p *ConcreteProduct) Clone() Prototyper { return &ConcreteProduct{p.name} } diff --git a/Creational/README.md b/Creational/README.md index 29ea31f..cd3b1ee 100644 --- a/Creational/README.md +++ b/Creational/README.md @@ -1,23 +1,22 @@ - ## Порождающие паттерны (Creational) Порождающие паттерны делятся на два типа: 1. Паттерны уровня класса -2. Паттерны уровня объекта. +2. Паттерны уровня объекта -Паттерны уровня класса изменяют класс создаваемого объекта с помощью наследования. +Паттерны уровня класса используют наследование, чтобы изменять класс создаваемого объекта. -Паттерны уровня объекта создают новые объекты с помощью других объектов. +Паттерны уровня объекта делегируют создание объектов другим объектам. К паттернам уровня класса относится только «Фабричный метод». -Порождающие паттерны отвечают за создание классов и объектов. Другими словами порождают классы и порождают объекты. +Порождающие паттерны отвечают за создание классов и объектов. Они абстрагируют процесс инстанцирования, позволяя сделать систему независимой от способа создания, композиции и представления объектов. * [Абстрактная фабрика (Abstract Factory)](AbstractFactory) * [Строитель (Builder)](Builder) -* [Фабричный метод (Factory Method)](FactoryMethod) -* [Прототип (Prototype)](Prototype) -* [Одиночка (Singleton)](Singleton) +* [Фабричный метод (Factory Method)](FactoryMethod) +* [Прототип (Prototype)](Prototype) +* [Одиночка (Singleton)](Singleton) ## -~- THE END -~- \ No newline at end of file diff --git a/Creational/Singleton/singleton.go b/Creational/Singleton/singleton.go index ed1300d..ba2e082 100644 --- a/Creational/Singleton/singleton.go +++ b/Creational/Singleton/singleton.go @@ -1,12 +1,25 @@ // Package singleton is an example of the Singleton Pattern. +// +// In the following example, we are implementing a singleton that ensures +// only one instance of a type is created. This is useful for shared resources +// like configuration, connection pools, or logging. +// +// This usage example demonstrates: +// 1. Private instance variable that holds the single instance +// 2. sync.Once to ensure thread-safe lazy initialization +// 3. GetInstance function that returns the singleton instance +// +// The example is meant to show how to guarantee that a type has only one +// instance and provide a global point of access to it. package singleton import ( "sync" ) -// Singleton implementation. +// Singleton represents a type that should have only one instance. type Singleton struct { + // Any fields can be added here } var ( @@ -14,7 +27,9 @@ var ( once sync.Once ) -// GetInstance returns singleton +// GetInstance returns the singleton instance. +// It creates the instance on the first call using sync.Once for +// thread-safe lazy initialization. func GetInstance() *Singleton { once.Do(func() { instance = &Singleton{} diff --git a/Structural/Adapter/adapter.go b/Structural/Adapter/adapter.go index 0b2c059..d28644a 100644 --- a/Structural/Adapter/adapter.go +++ b/Structural/Adapter/adapter.go @@ -1,31 +1,43 @@ // Package adapter is an example of the Adapter Pattern. +// +// In the following example, we are adapting an existing Adaptee to work with +// a Target interface that our system expects. The Adapter translates calls +// from Target interface methods to Adaptee methods. +// +// This usage example demonstrates: +// 1. Target interface that the client expects to work with +// 2. Adaptee that has a different interface +// 3. Adapter that bridges between Target and Adaptee +// +// The example is meant to show how to make incompatible interfaces work together +// without modifying existing code. package adapter -// Target provides an interface with which the system should work. +// Target defines the interface that the client uses. type Target interface { Request() string } -// Adaptee implements system to be adapted. +// Adaptee represents the existing system with an incompatible interface. type Adaptee struct { } -// NewAdapter is the Adapter constructor. +// NewAdapter creates a new adapter that wraps the adaptee. func NewAdapter(adaptee *Adaptee) Target { return &Adapter{adaptee} } -// SpecificRequest implementation. +// SpecificRequest is the adaptee's existing method. func (a *Adaptee) SpecificRequest() string { return "Request" } -// Adapter implements Target interface and is an adapter. +// Adapter adapts the Adaptee to the Target interface. type Adapter struct { *Adaptee } -// Request is an adaptive method. +// Request implements the Target interface by calling the adaptee's method. func (a *Adapter) Request() string { return a.SpecificRequest() } diff --git a/Structural/Bridge/bridge.go b/Structural/Bridge/bridge.go index c0faf4d..1d69c21 100644 --- a/Structural/Bridge/bridge.go +++ b/Structural/Bridge/bridge.go @@ -1,56 +1,70 @@ // Package bridge is an example of the Bridge Pattern. +// +// In the following example, we are implementing a car and engine system. +// The Bridge pattern decouples an abstraction (Car) from its implementation (Engine) +// so they can vary independently. Different cars can use different engines +// without modifying either hierarchy. +// +// This usage example demonstrates: +// 1. Abstraction (Car) that depends on implementation interface +// 2. Implementation interface (Enginer) that defines engine behavior +// 3. Concrete implementations (Suzuki, Honda, Lada engines) +// 4. Client can combine any car with any engine +// +// The example is meant to show how to separate abstraction from implementation +// and allow them to evolve independently. package bridge -// Carer provides car interface. +// Carer defines the abstraction interface for cars. type Carer interface { Rase() string } -// Enginer provides engine interface. +// Enginer defines the implementation interface for engines. type Enginer interface { GetSound() string } -// Car implementation. +// Car implements the abstraction and maintains a reference to an engine. type Car struct { engine Enginer } -// NewCar is the Car constructor. +// NewCar creates a new car with the specified engine. func NewCar(engine Enginer) Carer { return &Car{ engine: engine, } } -// Rase implementation. +// Rase returns the sound of the car's engine when started. func (c *Car) Rase() string { return c.engine.GetSound() } -// EngineSuzuki implements Suzuki engine. +// EngineSuzuki implements a Suzuki engine. type EngineSuzuki struct { } -// GetSound returns sound of the engine. +// GetSound returns the distinctive sound of a Suzuki engine. func (e *EngineSuzuki) GetSound() string { return "SssuuuuZzzuuuuKkiiiii" } -// EngineHonda implements Honda engine. +// EngineHonda implements a Honda engine. type EngineHonda struct { } -// GetSound returns sound of the engine. +// GetSound returns the distinctive sound of a Honda engine. func (e *EngineHonda) GetSound() string { return "HhoooNnnnnnnnnDddaaaaaaa" } -// EngineLada implements Lada engine. +// EngineLada implements a Lada engine. type EngineLada struct { } -// GetSound returns sound of the engine. +// GetSound returns the distinctive sound of a Lada engine. func (e *EngineLada) GetSound() string { return "PhhhhPhhhhPhPhPhPhPh" } diff --git a/Structural/Composite/composite.go b/Structural/Composite/composite.go index 5a91df4..94f64f2 100644 --- a/Structural/Composite/composite.go +++ b/Structural/Composite/composite.go @@ -1,36 +1,49 @@ // Package composite is an example of the Composite Pattern. +// +// In the following example, we are implementing a file system tree. +// The Composite pattern allows clients to treat individual objects (File) +// and compositions of objects (Directory) uniformly through a common interface. +// +// This usage example demonstrates: +// 1. Component interface that defines operations for both leaf and composite +// 2. Leaf (File) that represents end objects with no children +// 3. Composite (Directory) that stores child components +// 4. Uniform traversal and printing of tree structure +// +// The example is meant to show how to compose objects into tree structures +// and work with them as if they were individual objects. package composite -// Component provides an interface for branches and leaves of a tree. +// Component defines the interface for both files and directories. type Component interface { - Add(child Component) - Name() string - Child() []Component - Print(prefix string) string + Add(child Component) // Add a child component (no-op for files) + Name() string // Get component name + Child() []Component // Get child components (empty for files) + Print(prefix string) string // Print tree structure } -// Directory implements branches of a tree +// Directory implements a composite that can contain other components. type Directory struct { name string childs []Component } -// Add appends an element to the tree branch. +// Add appends a child component to this directory. func (d *Directory) Add(child Component) { d.childs = append(d.childs, child) } -// Name returns name of the Component. +// Name returns the directory name. func (d *Directory) Name() string { return d.name } -// Child returns child elements. +// Child returns all components inside this directory. func (d *Directory) Child() []Component { return d.childs } -// Print returns the branche in string representation. +// Print returns the directory and its contents as a formatted string. func (d *Directory) Print(prefix string) string { result := prefix + "/" + d.Name() + "\n" for _, val := range d.Child() { @@ -39,38 +52,39 @@ func (d *Directory) Print(prefix string) string { return result } -// File implements a leaves of a tree +// File implements a leaf that cannot have children. type File struct { name string } -// Add implementation. +// Add is a no-op for files (they cannot contain children). func (f *File) Add(child Component) { + // Files don't contain other components } -// Name returns name of the Component. +// Name returns the file name. func (f *File) Name() string { return f.name } -// Child implementation. +// Child returns an empty slice as files have no children. func (f *File) Child() []Component { return []Component{} } -// Print returns the leave in string representation. +// Print returns the file path as a formatted string. func (f *File) Print(prefix string) string { return prefix + "/" + f.Name() + "\n" } -// NewDirectory is constructor. +// NewDirectory creates a new directory with the given name. func NewDirectory(name string) *Directory { return &Directory{ name: name, } } -// NewFile is constructor. +// NewFile creates a new file with the given name. func NewFile(name string) *File { return &File{ name: name, diff --git a/Structural/Decorator/decorator.go b/Structural/Decorator/decorator.go index ed7a8da..5bd4566 100644 --- a/Structural/Decorator/decorator.go +++ b/Structural/Decorator/decorator.go @@ -1,26 +1,40 @@ // Package decorator is an example of the Decorator Pattern. +// +// In the following example, we are implementing a text decoration system. +// The Decorator pattern allows behavior to be added to an individual object +// dynamically without affecting the behavior of other objects from the same class. +// +// This usage example demonstrates: +// 1. Component interface that defines the base behavior +// 2. ConcreteComponent that provides the core functionality +// 3. Decorator that wraps a component and adds new behavior +// +// The example is meant to show how to attach additional responsibilities +// to an object dynamically. Decorators provide a flexible alternative to +// subclassing for extending functionality. package decorator -// Component provides an interface for a decorator and component. +// Component defines the interface for objects that can have responsibilities +// added to them dynamically. type Component interface { Operation() string } -// ConcreteComponent implements a component. +// ConcreteComponent implements the core Component interface. type ConcreteComponent struct { } -// Operation implementation. +// Operation returns the base string without any decoration. func (c *ConcreteComponent) Operation() string { return "I am component!" } -// ConcreteDecorator implements a decorator. +// ConcreteDecorator wraps a Component and adds additional behavior. type ConcreteDecorator struct { component Component } -// Operation wraps operation of component +// Operation decorates the component's result with HTML strong tags. func (d *ConcreteDecorator) Operation() string { return "" + d.component.Operation() + "" } diff --git a/Structural/Facade/facade.go b/Structural/Facade/facade.go index 062c5cc..a98e1cf 100644 --- a/Structural/Facade/facade.go +++ b/Structural/Facade/facade.go @@ -1,11 +1,25 @@ // Package facade is an example of the Facade Pattern. +// +// In the following example, we are implementing a simple life simulator. +// The Facade pattern provides a unified interface to a set of interfaces +// in a subsystem. Man facade defines a higher-level interface that makes +// the subsystem easier to use. +// +// This usage example demonstrates: +// 1. Complex subsystems (House, Tree, Child) with their own interfaces +// 2. Facade (Man) that provides a simple interface to the subsystems +// 3. Client code that only interacts with the facade +// +// The example is meant to show how to provide a simplified interface +// to a complex subsystem, reducing dependencies and making the system +// easier to use. package facade import ( "strings" ) -// NewMan creates man. +// NewMan creates a new Man facade with all subsystems initialized. func NewMan() *Man { return &Man{ house: &House{}, @@ -14,14 +28,16 @@ func NewMan() *Man { } } -// Man implements man and facade. +// Man implements the facade that provides a simple interface to +// the complex subsystems (House, Tree, Child). type Man struct { house *House tree *Tree child *Child } -// Todo returns that man must do. +// Todo returns a summary of everything the man needs to do in life. +// It coordinates the subsystems and combines their results. func (m *Man) Todo() string { result := []string{ m.house.Build(), @@ -31,29 +47,29 @@ func (m *Man) Todo() string { return strings.Join(result, "\n") } -// House implements a subsystem "House" +// House represents a subsystem for building-related operations. type House struct { } -// Build implementation. +// Build returns the house building action. func (h *House) Build() string { return "Build house" } -// Tree implements a subsystem "Tree" +// Tree represents a subsystem for tree-related operations. type Tree struct { } -// Grow implementation. +// Grow returns the tree growing action. func (t *Tree) Grow() string { return "Tree grow" } -// Child implements a subsystem "Child" +// Child represents a subsystem for child-related operations. type Child struct { } -// Born implementation. +// Born returns the child birth action. func (c *Child) Born() string { return "Child born" } diff --git a/Structural/Flyweight/flyweight.go b/Structural/Flyweight/flyweight.go index 64e1577..50e4dd6 100644 --- a/Structural/Flyweight/flyweight.go +++ b/Structural/Flyweight/flyweight.go @@ -1,20 +1,37 @@ // Package flyweight is an example of the Flyweight Pattern. +// +// In the following example, we are implementing an image caching system. +// The Flyweight pattern minimizes memory usage by sharing as much data as possible +// with similar objects. It separates intrinsic state (shared) from extrinsic +// state (context-specific). +// +// This usage example demonstrates: +// 1. Flyweight interface defining operations that use extrinsic state +// 2. Flyweight factory that creates and manages shared flyweights +// 3. Concrete flyweight storing intrinsic state (filename) +// 4. Extrinsic state (width, height, opacity) passed during operations +// +// The example is meant to show how to efficiently support large numbers +// of fine-grained objects by sharing common parts of state. package flyweight import "fmt" -// Flyweighter interface +// Flyweighter defines the interface for flyweight objects. type Flyweighter interface { + // Draw renders the image with provided extrinsic state. Draw(width, height int, opacity float64) string } -// FlyweightFactory implements a factory. -// If a suitable flyweighter is in pool, then returns it. +// FlyweightFactory creates and manages flyweight objects. +// It ensures that flyweights are shared properly by maintaining a pool. type FlyweightFactory struct { pool map[string]Flyweighter } -// GetFlyweight creates or returns a suitable Flyweighter by state. +// GetFlyweight returns a flyweight for the given filename. +// If a flyweight already exists in the pool, it returns the existing one. +// Otherwise, it creates a new flyweight, adds it to the pool, and returns it. func (f *FlyweightFactory) GetFlyweight(filename string) Flyweighter { if f.pool == nil { f.pool = make(map[string]Flyweighter) @@ -25,12 +42,16 @@ func (f *FlyweightFactory) GetFlyweight(filename string) Flyweighter { return f.pool[filename] } -// ConcreteFlyweight implements a Flyweighter interface. +// ConcreteFlyweight implements the Flyweighter interface. +// It stores intrinsic state (filename) that is shared across contexts. type ConcreteFlyweight struct { - filename string // internal state + filename string // internal state (shared) } -// Draw draws image. Args width, height and opacity is external state. +// Draw renders the image with the provided extrinsic state. +// Extrinsic state (width, height, opacity) is passed as parameters +// and depends on the specific context of use. func (f *ConcreteFlyweight) Draw(width, height int, opacity float64) string { - return fmt.Sprintf("draw image: %s, width: %d, height: %d, opacity: %.2f", f.filename, width, height, opacity) + return fmt.Sprintf("draw image: %s, width: %d, height: %d, opacity: %.2f", + f.filename, width, height, opacity) } diff --git a/Structural/Proxy/proxy.go b/Structural/Proxy/proxy.go index c391e1c..8fc328c 100644 --- a/Structural/Proxy/proxy.go +++ b/Structural/Proxy/proxy.go @@ -1,29 +1,48 @@ -// Package proxy is an example of the Adapter Pattern. +// Package proxy is an example of the Proxy Pattern. +// +// In the following example, we are implementing a message proxy. +// The Proxy pattern provides a surrogate or placeholder for another object +// to control access to it. The proxy forwards requests to the real subject +// and can add additional behavior before or after forwarding. +// +// This usage example demonstrates: +// 1. Subject interface common to both RealSubject and Proxy +// 2. RealSubject that performs the actual work +// 3. Proxy that controls access and can add behavior +// 4. Lazy initialization of the real subject +// +// The example is meant to show how to use a proxy for lazy loading, +// access control, logging, or adding extra functionality without +// modifying the real subject. package proxy -// Subject provides an interface for a real subject and its surrogate. +// Subject defines the common interface for RealSubject and Proxy. type Subject interface { Send() string } -// Proxy implements a surrogate. +// Proxy implements a surrogate that controls access to the RealSubject. +// It can add additional behavior before or after forwarding requests. type Proxy struct { realSubject Subject } -// Send sends a message +// Send forwards the request to the RealSubject, creating it if necessary, +// and decorates the result with HTML strong tags. func (p *Proxy) Send() string { + // Lazy initialization if p.realSubject == nil { p.realSubject = &RealSubject{} } + // Additional behavior (decoration) before/after forwarding return "" + p.realSubject.Send() + "" } -// RealSubject implements a real subject +// RealSubject implements the actual business logic. type RealSubject struct { } -// Send sends a message +// Send returns the real message from the subject. func (s *RealSubject) Send() string { - return "I’ll be back!" + return "I'll be back!" } diff --git a/Structural/README.md b/Structural/README.md index 0cfa265..248e8a2 100644 --- a/Structural/README.md +++ b/Structural/README.md @@ -1,31 +1,30 @@ - -### Структурные паттерны (Structural) +## Структурные паттерны (Structural) Структурные паттерны делятся на два типа: 1. Паттерны уровня класса -2. Паттерны уровня объекта. +2. Паттерны уровня объекта -Паттерны уровня класса описывают взаимодействия между классами и их подклассами. Такие отношения выражаются путем наследования и реализации классов. Тут базовый класс определяет интерфейс, а подклассы - реализацию. +Паттерны уровня класса описывают взаимодействие между классами и их подклассами. Такие отношения выражаются через наследование и реализацию интерфейсов. Базовый класс определяет интерфейс, а подклассы предоставляют конкретную реализацию. -Паттерны уровня объекта описывают взаимодействия между объектами. Такие отношения выражаются связями - ассоциацией, агрегацией и композицией. Тут структуры строятся путем объединения объектов некоторых классов. +Паттерны уровня объекта описывают взаимодействие между объектами. Такие отношения выражаются через связи: ассоциацию, агрегацию и композицию. Структуры строятся путем объединения объектов различных классов. -Ассоциация - отношение, когда объекты двух классов могут ссылаться один на другой. Например, свойство класса содержит экземпляр другого класса. +**Ассоциация** - отношение, при котором объекты двух классов могут ссылаться друг на друга. Например, свойство одного класса содержит экземпляр другого класса. -Агрегация – частная форма ассоциации. Агрегация применяется, когда один объект должен быть контейнером для других объектов и время существования этих объектов никак не зависит от времени существования объекта контейнера. Вообщем, если контейнер будет уничтожен, то входящие в него объекты не пострадают. Например, мы создали объект, а потом передали его в объект контейнер, каким-либо образом, можно в метод объекта контейнера передать или присвоить сразу свойству контейнера извне. Значит, при удалении контейнера мы ни как не затронем наш созданный объект, который может взаимодействовать и с другими контейнерами. +**Агрегация** - частная форма ассоциации. Агрегация применяется, когда один объект выступает контейнером для других объектов, но время жизни этих объектов не зависит от времени жизни контейнера. Если контейнер будет уничтожен, входящие в него объекты продолжают существовать. Например, мы создаем объект и передаем его в контейнер через метод или присваиваем свойству. При удалении контейнера объект остается доступным и может использоваться другими контейнерами. -Композиция – Тоже самое, что и агрегация, но составные объекты не могут существовать отдельно от объекта контейнера и если контейнер будет уничтожен, то всё его содержимое будет уничтожено тоже. Например, мы создали объект в методе объекта контейнера и присвоили его свойству объекта контейнера. Из вне, о нашем созданном объекте никто не знает, значит, при удалении контейнера, созданный объект будет удален так же, т.к. на него нет ссылки извне. +**Композиция** - более строгая форма агрегации. Составные объекты не могут существовать отдельно от контейнера. Если контейнер уничтожается, всё его содержимое уничтожается тоже. Например, объект создается внутри метода контейнера и присваивается его свойству. Извне об этом объекте никто не знает, поэтому при удалении контейнера объект становится недоступным и удаляется сборщиком мусора. -К паттернам уровня класса относится только «Адаптер». Смысл его работы в том, что если у вас есть класс и его интерфейс не совместим с библиотеками вашей системы, то что бы разрешить этот конфликт, мы не изменяем код этого класса, а пишем для него адаптер. +К паттернам уровня класса относится только «Адаптер». Его смысл в том, что если у вас есть класс с несовместимым интерфейсом, вы не изменяете код этого класса, а создаете адаптер, который приводит интерфейс к нужному виду. -Все структурные паттерны отвечают за создание правильной структуры системы, в которой без труда смогут взаимодействовать между собой уже имеющиеся классы и объекты. +Структурные паттерны отвечают за построение гибких и эффективных структур системы, позволяя существующим классам и объектам без труда взаимодействовать друг с другом. -* [Адаптер (Adapter)](Adapter) -* [Мост (Bridge)](Bridge) -* [Компоновщик (Composite)](Composite) -* [Декоратор (Decorator)](Decorator) -* [Фасад (Facade)](Facade) -* [Приспособленец (Flyweight)](Flyweight) -* [Заместитель (Proxy)](Proxy) +* [Адаптер (Adapter)](Adapter) +* [Мост (Bridge)](Bridge) +* [Компоновщик (Composite)](Composite) +* [Декоратор (Decorator)](Decorator) +* [Фасад (Facade)](Facade) +* [Приспособленец (Flyweight)](Flyweight) +* [Заместитель (Proxy)](Proxy) -## -~- THE END -~- +## -~- THE END -~- \ No newline at end of file diff --git a/Unsorted/README.md b/Unsorted/README.md index 7fe2068..b15a303 100644 --- a/Unsorted/README.md +++ b/Unsorted/README.md @@ -2,5 +2,5 @@ ### Неотсортированные и наброски * [Спецификация (Specification)](Specification) - +* [Пул воркеров (Worker Pool)](WorkerPool) ## -~- THE END -~- diff --git a/Unsorted/Specification/specification.go b/Unsorted/Specification/specification.go index 984a9b2..78df15f 100644 --- a/Unsorted/Specification/specification.go +++ b/Unsorted/Specification/specification.go @@ -1,30 +1,37 @@ -// Pattern Specification +// Package specification provides an implementation of the Specification pattern +// for business rules validation. // -// In the following example, we are retrieving invoices and sending them to a collection agency if +// In the following example, we are retrieving invoices and sending them to a +// collection agency if: // 1. they are overdue, // 2. notices have been sent, and // 3. they are not already with the collection agency. +// // This example is meant to show the end result of how the logic is 'chained' together. +// The Specification pattern allows you to encapsulate business rules in a +// reusable and combinable way. Rules can be chained using AND, OR, and NOT +// operations to create complex validation logic. // -// This usage example assumes a previously defined OverdueSpecification class -// that is satisfied when an invoice's due date is 30 days or older, -// a NoticeSentSpecification class that is satisfied when three notices -// have been sent to the customer, and an InCollectionSpecification class -// that is satisfied when an invoice has already been sent to the collection -// agency. The implementation of these classes isn't important here. - +// This usage example demonstrates: +// 1. Specification interface with logical combinators (And, Or, Not) +// 2. BaseSpecification providing default combinator implementations +// 3. Composite specifications (And, Or, Not) for combining rules +// 4. Concrete specifications (OverDue, NoticeSent, InCollection) +// 5. Method chaining for readable business rules +// +// The example is meant to show how to build complex business rules from +// simple, reusable components. package specification -// Data for analysis +// Invoice represents a customer invoice with data needed for business rules. type Invoice struct { - Day int - Notice int - IsSent bool + Day int // Days overdue + Notice int // Number of notices sent + IsSent bool // Whether already sent to collection } -///// - -// Invoice Specification Interface +// Specification defines the interface for business rules that can be +// evaluated against an Invoice and combined with other specifications. type Specification interface { IsSatisfiedBy(Invoice) bool And(Specification) Specification @@ -33,137 +40,132 @@ type Specification interface { Relate(Specification) } -///// - -// Invoice BaseSpecification +// BaseSpecification provides default implementations for logical combinators. +// It should be embedded in all concrete specifications. type BaseSpecification struct { Specification } -// Check specification -func (self *BaseSpecification) IsSatisfiedBy(elm Invoice) bool { +// IsSatisfiedBy provides a default implementation that returns false. +// Concrete specifications should override this method. +func (b *BaseSpecification) IsSatisfiedBy(elm Invoice) bool { return false } -// Condition AND -func (self *BaseSpecification) And(spec Specification) Specification { - a := &AndSpecification{ - self.Specification, spec, +// And creates a new specification that requires both conditions to be true. +func (b *BaseSpecification) And(spec Specification) Specification { + and := &AndSpecification{ + Specification: b.Specification, + compare: spec, } - a.Relate(a) - return a + and.Relate(and) + return and } -// Condition OR -func (self *BaseSpecification) Or(spec Specification) Specification { - a := &OrSpecification{ - self.Specification, spec, +// Or creates a new specification that requires at least one condition to be true. +func (b *BaseSpecification) Or(spec Specification) Specification { + or := &OrSpecification{ + Specification: b.Specification, + compare: spec, } - a.Relate(a) - return a + or.Relate(or) + return or } -// Condition NOT -func (self *BaseSpecification) Not() Specification { - a := &NotSpecification{ - self.Specification, +// Not creates a new specification that requires the condition to be false. +func (b *BaseSpecification) Not() Specification { + not := &NotSpecification{ + Specification: b.Specification, } - a.Relate(a) - return a + not.Relate(not) + return not } -// Relate to specification -func (self *BaseSpecification) Relate(spec Specification) { - self.Specification = spec +// Relate sets the embedded specification reference. +// This internal method helps composite specifications delegate correctly. +func (b *BaseSpecification) Relate(spec Specification) { + b.Specification = spec } -///// - -// AndSpecification +// AndSpecification combines two specifications with logical AND. type AndSpecification struct { Specification compare Specification } -// Check specification -func (self *AndSpecification) IsSatisfiedBy(elm Invoice) bool { - return self.Specification.IsSatisfiedBy(elm) && self.compare.IsSatisfiedBy(elm) +// IsSatisfiedBy returns true only if both specifications are satisfied. +func (a *AndSpecification) IsSatisfiedBy(elm Invoice) bool { + return a.Specification.IsSatisfiedBy(elm) && a.compare.IsSatisfiedBy(elm) } -///// - -// OrSpecification +// OrSpecification combines two specifications with logical OR. type OrSpecification struct { Specification compare Specification } -// Check specification -func (self *OrSpecification) IsSatisfiedBy(elm Invoice) bool { - return self.Specification.IsSatisfiedBy(elm) || self.compare.IsSatisfiedBy(elm) +// IsSatisfiedBy returns true if either specification is satisfied. +func (o *OrSpecification) IsSatisfiedBy(elm Invoice) bool { + return o.Specification.IsSatisfiedBy(elm) || o.compare.IsSatisfiedBy(elm) } -///// - -// NotSpecification +// NotSpecification inverts a specification with logical NOT. type NotSpecification struct { Specification } -// Check specification -func (self *NotSpecification) IsSatisfiedBy(elm Invoice) bool { - return !self.Specification.IsSatisfiedBy(elm) +// IsSatisfiedBy returns true if the wrapped specification is not satisfied. +func (n *NotSpecification) IsSatisfiedBy(elm Invoice) bool { + return !n.Specification.IsSatisfiedBy(elm) } -///// - -// Invoice's due date is 30 days or older +// OverDueSpecification is satisfied when an invoice is 30+ days overdue. type OverDueSpecification struct { Specification } -// Check specification -func (self *OverDueSpecification) IsSatisfiedBy(elm Invoice) bool { +// IsSatisfiedBy returns true if the invoice is 30 days or more overdue. +func (o *OverDueSpecification) IsSatisfiedBy(elm Invoice) bool { return elm.Day >= 30 } -// Constructor +// NewOverDueSpecification creates a new specification for overdue invoices. func NewOverDueSpecification() Specification { - a := &OverDueSpecification{&BaseSpecification{}} - a.Relate(a) - return a + spec := &OverDueSpecification{&BaseSpecification{}} + spec.Relate(spec) + return spec } -// Three notices have been sent to the customer +// NoticeSentSpecification is satisfied when 3+ notices have been sent. type NoticeSentSpecification struct { Specification } -// Check specification -func (self *NoticeSentSpecification) IsSatisfiedBy(elm Invoice) bool { +// IsSatisfiedBy returns true if the invoice has 3 or more notices sent. +func (n *NoticeSentSpecification) IsSatisfiedBy(elm Invoice) bool { return elm.Notice >= 3 } -// Constructor +// NewNoticeSentSpecification creates a new specification for notice count. func NewNoticeSentSpecification() Specification { - a := &NoticeSentSpecification{&BaseSpecification{}} - a.Relate(a) - return a + spec := &NoticeSentSpecification{&BaseSpecification{}} + spec.Relate(spec) + return spec } -// Invoice has already been sent to the collection agency. +// InCollectionSpecification is satisfied when invoice is NOT yet in collection. type InCollectionSpecification struct { Specification } -// Check specification -func (self *InCollectionSpecification) IsSatisfiedBy(elm Invoice) bool { +// IsSatisfiedBy returns true if the invoice has NOT been sent to collection. +func (i *InCollectionSpecification) IsSatisfiedBy(elm Invoice) bool { return !elm.IsSent } -// Constructor +// NewInCollectionSpecification creates a new specification for collection status. func NewInCollectionSpecification() Specification { - a := &InCollectionSpecification{&BaseSpecification{}} - a.Relate(a) - return a + spec := &InCollectionSpecification{&BaseSpecification{}} + spec.Relate(spec) + return spec } diff --git a/Unsorted/WorkerPool/README.md b/Unsorted/WorkerPool/README.md new file mode 100644 index 0000000..8f1436f --- /dev/null +++ b/Unsorted/WorkerPool/README.md @@ -0,0 +1,18 @@ +## Пул воркеров (Worker Pool) + +Паттерн Worker Pool относится к группе паттернов конкурентного выполнения. + +Часто в приложениях требуется выполнить множество однотипных задач, и последовательная обработка занимает слишком много времени. Например, нужно обработать тысячи HTTP-запросов, файлов или сообщений из очереди. В таких случаях следует использовать паттерн Worker Pool. + +Смысл работы этого паттерна в том, что мы создаём фиксированное количество воркеров (горутин), которые постоянно ожидают поступления задач. Когда задача появляется, свободный воркер немедленно приступает к её выполнению. Другими словами, Worker Pool распределяет нагрузку и ограничивает количество одновременно выполняемых операций. + +Требуется для реализации: + +1. Очередь задач — канал, через который поступают задания; +2. Пул воркеров — фиксированное количество горутин, выполняющих задачи; +3. Механизм завершения — способ корректно остановить все воркеры; +4. Сбор результатов — опциональный канал для получения результатов работы. + +[!] В описании паттерна применяются общие понятия. Применимо к языку Go, это горутины и каналы для организации конкурентного выполнения. + +## -~- THE END -~- \ No newline at end of file diff --git a/Unsorted/WorkerPool/worker_pool.go b/Unsorted/WorkerPool/worker_pool.go new file mode 100644 index 0000000..2ad2af5 --- /dev/null +++ b/Unsorted/WorkerPool/worker_pool.go @@ -0,0 +1,99 @@ +// Package workerpool provides an implementation of the Worker Pool pattern +// for concurrent task processing. +// +// In the following example, we are processing a large number of tasks +// concurrently using a fixed pool of workers. This approach allows us to +// control the degree of parallelism and prevent resource exhaustion. +// +// This usage example demonstrates: +// 1. Creating a pool with one worker per CPU core +// 2. Distributing 1000 tasks through a channel +// 3. Collecting results from all workers +// 4. Graceful shutdown using context +// +// The workers simulate work with a one-second delay and return the square +// of each input value. This example is meant to show the end result of how +// the tasks are distributed and results are collected. +package workerpool + +import ( + "context" + "fmt" + "runtime" + "sync" + "time" +) + +// WorkerPool demonstrates a basic worker pool implementation. +// It creates a pool of workers, distributes 1000 tasks, and collects results. +// +// The function uses: +// - context.WithCancel for graceful shutdown +// - tasks channel for distributing work +// - results channel for collecting output +// - sync.WaitGroup for coordinating worker completion +func WorkerPool() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + tasks := make(chan int) + results := make(chan int) + wg := &sync.WaitGroup{} + + // Start workers (one per CPU core) + for i := 0; i < runtime.NumCPU(); i++ { + wg.Add(1) + go func(workerID int) { + defer wg.Done() + worker(ctx, workerID, tasks, results) + }(i) + } + + // Send tasks (producer) + go func() { + defer close(tasks) + for i := 0; i < 1000; i++ { + tasks <- i + } + }() + + // Close results when all workers are done (collector coordinator) + go func() { + wg.Wait() + close(results) + }() + + // Collect and print results (consumer) + for result := range results { + fmt.Printf("Result: %d\n", result) + } +} + +// worker processes tasks from tasks channel and sends results to results channel. +// It runs until context is cancelled or tasks channel is closed. +// +// Each worker: +// - Receives a task from the tasks channel +// - Simulates work with a one-second delay +// - Sends the result (input squared) to the results channel +// - Exits when context is done or tasks channel is closed +// +// The worker ID is used only for logging to demonstrate distribution. +func worker(ctx context.Context, id int, tasks <-chan int, results chan<- int) { + for { + select { + case <-ctx.Done(): + // Context cancelled - shut down gracefully + return + case val, ok := <-tasks: + if !ok { + // Tasks channel closed - no more work + return + } + fmt.Printf("Worker %d received %d\n", id, val) + time.Sleep(time.Second) + results <- val * val + fmt.Printf("Worker %d processed %d\n", id, val) + } + } +} diff --git a/Unsorted/WorkerPool/worker_pool_test.go b/Unsorted/WorkerPool/worker_pool_test.go new file mode 100644 index 0000000..05bbf05 --- /dev/null +++ b/Unsorted/WorkerPool/worker_pool_test.go @@ -0,0 +1,196 @@ +package workerpool + +import ( + "context" + "sync" + "testing" + "time" +) + +func TestWorkerPool(t *testing.T) { + // Create context with timeout to prevent hanging tests + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + tasks := make(chan int, 10) + results := make(chan int, 10) + wg := &sync.WaitGroup{} + + // Start 2 workers for the test + numWorkers := 2 + for i := 0; i < numWorkers; i++ { + wg.Add(1) + go func(workerID int) { + defer wg.Done() + worker(ctx, workerID, tasks, results) + }(i) + } + + // Send 5 tasks + go func() { + for i := 1; i <= 5; i++ { + tasks <- i + } + close(tasks) + }() + + // Wait for workers to finish in a separate goroutine + go func() { + wg.Wait() + close(results) + }() + + // Collect results + received := make(map[int]bool) + for result := range results { + received[result] = true + } + + // Verify we received all squares of numbers 1 through 5 + expected := map[int]bool{ + 1: true, // 1*1 + 4: true, // 2*2 + 9: true, // 3*3 + 16: true, // 4*4 + 25: true, // 5*5 + } + + for exp := range expected { + if !received[exp] { + t.Errorf("Expected result %d not found in results", exp) + } + } + + if len(received) != len(expected) { + t.Errorf("Expected %d results, got %d", len(expected), len(received)) + } +} + +func TestWorkerPoolCancellation(t *testing.T) { + // Context is cancelled immediately + ctx, cancel := context.WithCancel(context.Background()) + cancel() // Cancel before starting + + tasks := make(chan int, 10) + wg := &sync.WaitGroup{} + + workerStarted := make(chan bool) + workerDone := make(chan bool) + + // Start one worker + wg.Add(1) + go func() { + workerStarted <- true + defer func() { + wg.Done() + workerDone <- true + }() + + for { + select { + case <-ctx.Done(): + return + case _, ok := <-tasks: + if !ok { + return + } + // Should never reach here as context is already cancelled + t.Error("Worker received task after cancellation") + } + } + }() + + <-workerStarted // Wait for worker to start + + // Try to send a task + select { + case tasks <- 42: + // If sent, give some time for processing + time.Sleep(10 * time.Millisecond) + default: + // Channel might be closed + } + + close(tasks) + wg.Wait() + + // Verify worker exited + select { + case <-workerDone: + // Good, worker exited + case <-time.After(time.Second): + t.Error("Worker did not exit after cancellation") + } +} + +func TestWorkerPoolClosedTasks(t *testing.T) { + ctx := context.Background() + tasks := make(chan int) + results := make(chan int, 5) + wg := &sync.WaitGroup{} + + // Start worker + wg.Add(1) + go func() { + defer wg.Done() + worker(ctx, 1, tasks, results) + }() + + // Send one task and close + go func() { + tasks <- 7 + close(tasks) + }() + + // Wait for result + select { + case res := <-results: + if res != 49 { // 7*7 + t.Errorf("Expected 49, got %d", res) + } + case <-time.After(2 * time.Second): + t.Error("Timeout waiting for result") + } + + wg.Wait() +} + +func TestWorkerPoolManyTasks(t *testing.T) { + ctx := context.Background() + tasks := make(chan int, 100) + results := make(chan int, 100) + wg := &sync.WaitGroup{} + + // Start 4 workers + for i := 0; i < 4; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + worker(ctx, id, tasks, results) + }(i) + } + + // Send 20 tasks + go func() { + for i := 0; i < 20; i++ { + tasks <- i + } + close(tasks) + }() + + // Close results after all workers are done + go func() { + wg.Wait() + close(results) + }() + + // Count results + count := 0 + for range results { + count++ + } + + if count != 20 { + t.Errorf("Expected 20 results, got %d", count) + } +}