Паттерн Singleton гарантирует, что у класса есть только один экземпляр и предоставляет глобальную точку доступа к нему. Полезен для управления ресурсами: логгеры, менеджеры конфигурации, доступ к БД.
Когда применять Singleton
- Нужен единый объект, управляющий глобальным состоянием.
- Доступ к ресурсу должен быть централизован и контролируем.
- Создание дополнительных экземпляров приводит к ошибкам.
PlantUML-диаграмма
public sealed class Logger
{
private static readonly Lazy<Logger> _instance =
new Lazy<Logger>(() => new Logger());
private Logger() { }
public static Logger Instance => _instance.Value;
public void Log(string message)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {message}");
}
}
type Logger struct{}
var (
instance *Logger
once sync.Once
)
// sync.Once гарантирует, что инициализация выполнится ровно один раз,
// даже если Instance вызовут одновременно из нескольких горутин.
func Instance() *Logger {
once.Do(func() { instance = &Logger{} })
return instance
}
func (l *Logger) Log(message string) {
fmt.Printf("[%s] %s\n", time.Now().Format("15:04:05"), message)
}
class Logger:
# Модуль в Python сам по себе одиночка, поэтому чаще просто создают
# объект на уровне модуля. Здесь — явный вариант через __new__.
_instance: "Logger | None" = None
def __new__(cls) -> "Logger":
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def log(self, message: str) -> None:
print(f"[{datetime.now():%H:%M:%S}] {message}")
export class Logger {
private static instance: Logger | null = null;
private constructor() {}
static get(): Logger {
return (Logger.instance ??= new Logger());
}
log(message: string): void {
console.log(`[${new Date().toLocaleTimeString()}] ${message}`);
}
}
public final class Logger {
private Logger() { }
// Holder-идиома: вложенный класс загружается при первом обращении,
// и потокобезопасность инициализации обеспечивает сама JVM.
private static final class Holder {
private static final Logger INSTANCE = new Logger();
}
public static Logger getInstance() {
return Holder.INSTANCE;
}
public void log(String message) {
System.out.printf("[%tT] %s%n", LocalTime.now(), message);
}
}
// В Kotlin одиночка встроен в язык: object создаётся лениво и потокобезопасно,
// писать двойную проверку блокировки не нужно.
object Logger {
fun log(message: String) {
val now = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
println("[$now] $message")
}
}
Плюсы и минусы
| Плюсы | Минусы |
|---|---|
| Гарантирует единственность объекта | Скрывает зависимости, ухудшает тестируемость |
| Глобальная точка доступа | Может привести к «глобальному состоянию» |
| Удобно для управления ресурсами | Нарушает принцип единой ответственности (SRP) |
Заключение
Singleton простой, но мощный паттерн. Используйте его внимательно: он удобен для центрального управления состоянием, но может создавать жёсткие зависимости и сложности с тестированием.