Ответ
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), так как это делает код более модульным, тестируемым и гибким.