Singleton (Одиночка)

Паттерн Singleton гарантирует, что у класса есть только один экземпляр и предоставляет глобальную точку доступа к нему. Полезен для управления ресурсами: логгеры, менеджеры конфигурации, доступ к БД.

Когда применять Singleton

  • Нужен единый объект, управляющий глобальным состоянием.
  • Доступ к ресурсу должен быть централизован и контролируем.
  • Создание дополнительных экземпляров приводит к ошибкам.

PlantUML-диаграмма

UML диаграмма Singleton

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 простой, но мощный паттерн. Используйте его внимательно: он удобен для центрального управления состоянием, но может создавать жёсткие зависимости и сложности с тестированием.