Паттерны проектирования в Go с примерами для разработчиков

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Паттерны проектирования в Go (Golang) с примерами кода

Паттерны проектирования — это проверенные решения типовых задач разработки. В Go, с его лаконичным синтаксисом и отсутствием классического ООП, многие паттерны обретают уникальную реализацию. В этой статье мы разберём три группы паттернов: порождающие, структурные и поведенческие — с практическими примерами на Golang.

Материал будет полезен как новичкам, так и опытным Go-разработчикам, стремящимся писать чистый, переиспользуемый и тестируемый код.

1. Порождающие паттерны (Creational Patterns)

Порождающие паттерны управляют процессом создания объектов. В Go, где нет конструкторов и наследования, их реализация особенно изящна.

1.1. Одиночка (Singleton)

Гарантирует, что у типа есть только один экземпляр, и предоставляет глобальную точку доступа к нему. В Go часто реализуется через sync.Once.

package singleton

import (
    "sync"
)

type Database struct {
    connection string
}

var (
    instance *Database
    once     sync.Once
)

func GetDatabase() *Database {
    once.Do(func() {
        instance = &Database{connection: "postgres://user:pass@localhost/db"}
    })
    return instance
}

Когда использовать: работа с пулом соединений, кэшем, логгером.

1.2. Фабричный метод (Factory Method)

Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. В Go фабричный метод — это просто функция, возвращающая интерфейс.

package factory

import "fmt"

// Product interface
type INotification interface {
    Send(message string) string
}

// Concrete products
type Email struct{}

func (e *Email) Send(msg string) string {
    return fmt.Sprintf("Email sent: %s", msg)
}

type SMS struct{}

func (s *SMS) Send(msg string) string {
    return fmt.Sprintf("SMS sent: %s", msg)
}

// Factory function
func CreateNotification(notifType string) INotification {
    switch notifType {
    case "email":
        return &Email{}
    case "sms":
        return &SMS{}
    default:
        return nil
    }
}

Когда использовать: когда тип создаваемого объекта неизвестен заранее.

1.3. Строитель (Builder)

Отделяет конструирование сложного объекта от его представления. В Go строитель — это структура с методами-сеттерами, возвращающими самого себя для цепочечного вызова.

package builder

type Pizza struct {
    Size     string
    Cheese   bool
    Pepperoni bool
}

type PizzaBuilder struct {
    pizza Pizza
}

func NewPizzaBuilder() *PizzaBuilder {
    return &PizzaBuilder{}
}

func (b *PizzaBuilder) SetSize(size string) *PizzaBuilder {
    b.pizza.Size = size
    return b
}

func (b *PizzaBuilder) AddCheese() *PizzaBuilder {
    b.pizza.Cheese = true
    return b
}

func (b *PizzaBuilder) AddPepperoni() *PizzaBuilder {
    b.pizza.Pepperoni = true
    return b
}

func (b *PizzaBuilder) Build() Pizza {
    return b.pizza
}

Когда использовать: когда объект имеет много опциональных параметров.

2. Структурные паттерны (Structural Patterns)

Структурные паттерны определяют способы сборки объектов и классов в более крупные структуры, сохраняя гибкость и эффективность.

2.1. Адаптер (Adapter)

Позволяет объектам с несовместимыми интерфейсами работать вместе. В Go для этого используются интерфейсы и структуры-обёртки.

package adapter

import "fmt"

// Target interface
type USB interface {
    ConnectWithUSB() string
}

// Adaptee (несовместимый класс)
type MemoryCard struct{}

func (m *MemoryCard) ReadFromCard() string {
    return "Data read from memory card"
}

// Adapter
type CardReader struct {
    card *MemoryCard
}

func NewCardReader(card *MemoryCard) *CardReader {
    return &CardReader{card: card}
}

func (r *CardReader) ConnectWithUSB() string {
    return fmt.Sprintf("Adapter: %s via USB", r.card.ReadFromCard())
}

Когда использовать: интеграция с legacy-кодом или сторонними библиотеками.

2.2. Декоратор (Decorator)

Динамически добавляет объекту новую функциональность, оборачивая его в полезные обёртки. В Go декораторы реализуются через композицию интерфейсов.

package decorator

import "fmt"

type INotifier interface {
    Send(message string) string
}

type Notifier struct{}

func (n *Notifier) Send(msg string) string {
    return fmt.Sprintf("Sending: %s", msg)
}

// Decorator 1
type SMSNotifier struct {
    notifier INotifier
}

func (s *SMSNotifier) Send(msg string) string {
    return s.notifier.Send(msg) + " via SMS"
}

// Decorator 2
type SlackNotifier struct {
    notifier INotifier
}

func (s *SlackNotifier) Send(msg string) string {
    return s.notifier.Send(msg) + " via Slack"
}

Когда использовать: когда нужно добавлять обязанности динамически и в разных комбинациях.

3. Поведенческие паттерны (Behavioral Patterns)

Поведенческие паттерны решают задачи эффективного взаимодействия между объектами.

3.1. Наблюдатель (Observer)

Создаёт механизм подписки, позволяющий одним объектам следить и реагировать на события, происходящие в других объектах. В Go часто используют каналы.

package observer

import "fmt"

type Subject struct {
    observers []chan string
}

func (s *Subject) Attach(observer chan string) {
    s.observers = append(s.observers, observer)
}

func (s *Subject) Notify(msg string) {
    for _, observer := range s.observers {
        observer <- msg
    }
}

// Использование
func ExampleObserver() {
    subject := &Subject{}
    obs1 := make(chan string, 1)
    obs2 := make(chan string, 1)

    subject.Attach(obs1)
    subject.Attach(obs2)

    subject.Notify("Event happened!")

    fmt.Println(<-obs1) // Event happened!
    fmt.Println(<-obs2) // Event happened!
}

Когда использовать: системы событий, уведомления, обновление UI.

3.2. Стратегия (Strategy)

Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. В Go стратегия — это просто интерфейс с одним методом.

<                

Блоги

Рекомендаций по книгам