Паттерны проектирования в Go с примерами: от А до Я

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

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

Начать курс

Паттерны проектирования в Go с примерами: от А до Я

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

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

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

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

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

package main

import (
    "fmt"
    "sync"
)

type Database struct {
    connection string
}

var (
    instance *Database
    once     sync.Once
)

func GetDatabaseInstance() *Database {
    once.Do(func() {
        instance = &Database{connection: "postgres://localhost:5432/mydb"}
        fmt.Println("Создано единственное подключение к БД")
    })
    return instance
}

func main() {
    db1 := GetDatabaseInstance()
    db2 := GetDatabaseInstance()
    fmt.Println(db1 == db2) // true
}

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

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

package main

import "fmt"

// Интерфейс продукта
type Transport interface {
    Deliver() string
}

// Конкретные продукты
type Truck struct{}
func (t Truck) Deliver() string { return "Доставка по суше" }

type Ship struct{}
func (s Ship) Deliver() string { return "Доставка по морю" }

// Фабрика
type LogisticsFactory interface {
    CreateTransport() Transport
}

type RoadLogistics struct{}
func (r RoadLogistics) CreateTransport() Transport { return Truck{} }

type SeaLogistics struct{}
func (s SeaLogistics) CreateTransport() Transport { return Ship{} }

func main() {
    var factory LogisticsFactory = RoadLogistics{}
    transport := factory.CreateTransport()
    fmt.Println(transport.Deliver()) // Доставка по суше
}

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

Структурные паттерны помогают организовать классы и объекты в более крупные структуры.

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

Адаптер позволяет объектам с несовместимыми интерфейсами работать вместе. В Go это решается через обертку.

package main

import "fmt"

// Старый интерфейс
type OldPrinter interface {
    PrintOld(data string)
}

type LegacyPrinter struct{}
func (l LegacyPrinter) PrintOld(data string) {
    fmt.Println("Legacy:", data)
}

// Новый интерфейс
type NewPrinter interface {
    PrintNew(data string)
}

// Адаптер
type PrinterAdapter struct {
    OldPrinter OldPrinter
}

func (a PrinterAdapter) PrintNew(data string) {
    a.OldPrinter.PrintOld(data)
}

func main() {
    legacy := LegacyPrinter{}
    adapter := PrinterAdapter{OldPrinter: legacy}
    adapter.PrintNew("Hello, Go!") // Legacy: Hello, Go!
}

2.2. Composite (Компоновщик)

Компоновщик позволяет обрабатывать группы объектов как единый объект. Идеально подходит для древовидных структур.

package main

import "fmt"

// Интерфейс компонента
type Graphic interface {
    Draw() string
}

// Листовой элемент
type Circle struct{}
func (c Circle) Draw() string { return "Рисуем круг" }

// Контейнер
type CompoundGraphic struct {
    children []Graphic
}

func (cg *CompoundGraphic) Add(g Graphic) {
    cg.children = append(cg.children, g)
}

func (cg *CompoundGraphic) Draw() string {
    result := "Составная фигура:\n"
    for _, child := range cg.children {
        result += "  - " + child.Draw() + "\n"
    }
    return result
}

func main() {
    circle1 := Circle{}
    circle2 := Circle{}
    
    group := &CompoundGraphic{}
    group.Add(circle1)
    group.Add(circle2)
    
    fmt.Println(group.Draw())
}

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

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

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

Стратегия позволяет менять алгоритм на лету. В Go это реализуется через интерфейсы.

package main

import "fmt"

// Интерфейс стратегии
type PaymentStrategy interface {
    Pay(amount float64) string
}

// Конкретные стратегии
type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Оплачено %.2f кредитной картой", amount)
}

type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Оплачено %.2f через PayPal", amount)
}

// Контекст
type ShoppingCart struct {
    payment PaymentStrategy
}

func (s *ShoppingCart) SetPayment(p PaymentStrategy) {
    s.payment = p
}

func (s ShoppingCart) Checkout(amount float64) {
    fmt.Println(s.payment.Pay(amount))
}

func main() {
    cart := ShoppingCart{}
    cart.SetPayment(CreditCard{})
    cart.Checkout(100.50)
    
    cart.SetPayment(PayPal{})
    cart.Checkout(200.75)
}

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

Наблюдатель уведомляет подписчиков об изменениях. В Go удобно использовать каналы.

package main

import "fmt"

// Интерфейс наблюдателя
type Observer interface {
    Update(message string)
}

// Конкретный наблюдатель
type User struct {
    name string
}

func (u User) Update(message string) {
    fmt.Printf("Пользователь %s получил: %s\n", u.name, message)
}

// Издатель
type NewsPublisher struct {
    observers []Observer
}

func (np *NewsPublisher) Subscribe(o Observer) {
    np.observers = append(np.observers, o)
}

func (np *NewsPublisher) NotifyAll(message string) {
    for _, observer := range np.observers {
        observer.Update(message)
    }
}

func main() {
    publisher := &NewsPublisher{}
    
    user1 := User{name: "Анна"}
    user2 := User{name: "Иван"}
    
    publisher.Subscribe(user1)
    publisher.Subscribe(user2)
    
    publisher.NotifyAll("Новая статья по Go!")
}

Заключение

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

Блоги

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