Что такое паттерн Singleton (Одиночка)?

Ответ

Singleton (Одиночка) — это порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к этому экземпляру.

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

Пример на Go (потокобезопасный):

package main

import (
    "fmt"
    "sync"
)

// Структура, которую мы хотим сделать синглтоном
// (например, подключение к БД или конфигурация)
type DatabaseConnection struct {
    connectionString string
}

func (db *DatabaseConnection) Query(q string) {
    fmt.Printf("Executing query '%s' using connection '%s'n", q, db.connectionString)
}

var (
    instance *DatabaseConnection
    once     sync.Once
)

// GetInstance предоставляет глобальную точку доступа
func GetInstance() *DatabaseConnection {
    // once.Do выполнит переданную функцию только один раз
    // при первом вызове GetInstance.
    once.Do(func() {
        fmt.Println("Creating database connection...")
        instance = &DatabaseConnection{connectionString: "user:pass@tcp(127.0.0.1:3306)/db"}
    })
    return instance
}

func main() {
    // Все вызовы будут возвращать один и тот же экземпляр
    conn1 := GetInstance()
    conn1.Query("SELECT * FROM users")

    conn2 := GetInstance()
    conn2.Query("SELECT * FROM products")
}
// Вывод:
// Creating database connection...
// Executing query 'SELECT * FROM users' using connection 'user:pass@tcp(127.0.0.1:3306)/db'
// Executing query 'SELECT * FROM products' using connection 'user:pass@tcp(127.0.0.1:3306)/db'

Преимущества:

  • Гарантия единственного экземпляра: Полезно для ресурсов, которые должны быть уникальными (пул соединений, логгер, конфигурация).
  • Ленивая инициализация (Lazy Initialization): Ресурс создается только тогда, когда он действительно нужен в первый раз.
  • Глобальный доступ: Легко получить доступ к экземпляру из любой части программы.

Недостатки и критика:

Singleton часто считают анти-паттерном в современной разработке:

  • Глобальное состояние: Вносит в программу глобальное состояние, что затрудняет отслеживание зависимостей и отладку.
  • Сложность тестирования: Модули, использующие Singleton, становятся тесно связанными с ним. Сложно подменить синглтон на mock-объект в unit-тестах.
  • Нарушение принципа единственной ответственности (SRP): Класс-синглтон отвечает не только за свою бизнес-логику, но и за контроль количества своих экземпляров.
  • Скрытые зависимости: Зависимость от синглтона не видна в сигнатуре функции или конструкторе, что делает код менее прозрачным.

Альтернатива: В большинстве случаев предпочтительнее использовать Внедрение зависимостей (Dependency Injection), так как это делает код более модульным, тестируемым и гибким.