Порождающие паттерны
Singleton (Одиночка)
Singleton — это порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к этому экземпляру.
Проблема
Singleton решает две проблемы одновременно, нарушая принцип единственной ответственности:
- Гарантирует наличие единственного экземпляра класса — когда нужен строгий контроль над количеством экземпляров
- Предоставляет глобальную точку доступа — когда нужен глобальный доступ к экземпляру
Решение
Все реализации паттерна Singleton сводятся к тому, чтобы скрыть конструктор по умолчанию и создать публичный статический метод, который и будет контролировать жизненный цикл единственного экземпляра.
Структура
Singleton
├── instance: Singleton
├── Singleton() // приватный конструктор
└── getInstance(): Singleton
Пример реализации
JavaScript (ES6+)
class DatabaseConnection {
static instance = null;
constructor() {
if (DatabaseConnection.instance) {
return DatabaseConnection.instance;
}
// Инициализация подключения к базе данных
this.connectionString = 'mongodb://localhost:27017/mydb';
this.isConnected = false;
DatabaseConnection.instance = this;
}
static getInstance() {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
connect() {
if (!this.isConnected) {
console.log('Подключение к базе данных...');
this.isConnected = true;
}
return this;
}
disconnect() {
this.isConnected = false;
console.log('Отключение от базы данных');
}
}
// Использование
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true - это один и тот же объект
TypeScript
JavaScript (ES6+)
class Logger {
private static instance: Logger;
private logs: string[] = [];
private constructor() {}
public static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
public log(message: string): void {
const timestamp = new Date().toISOString();
this.logs.push(`[${timestamp}] ${message}`);
console.log(`[${timestamp}] ${message}`);
}
public getLogs(): string[] {
return [...this.logs];
}
}
// Использование
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
logger1.log('Первое сообщение');
logger2.log('Второе сообщение');
console.log(logger1 === logger2); // true
Когда использовать
Используйте паттерн Singleton, когда:
- В системе должен быть единственный экземпляр класса
- Нужна глобальная точка доступа к этому экземпляру
- Создание объекта дорого (например, подключение к базе данных)
- Нужно контролировать доступ к общему ресурсу
Преимущества
- Гарантирует единственный экземпляр класса
- Предоставляет глобальную точку доступа
- Ленивая инициализация (создание только при необходимости)
- Контроль над жизненным циклом объекта
Недостатки
- Нарушает принцип единственной ответственности
- Скрывает зависимости в коде
- Усложняет тестирование
- Может стать антипаттерном при неправильном использовании
- Проблемы с многопоточностью (если не реализован правильно)
Альтернативы
- Dependency Injection — передача зависимостей через конструктор
- Service Locator — централизованный реестр сервисов
- Factory Method — для создания объектов с контролем жизненного цикла
Антипаттерн?
Singleton часто считается антипаттерном, потому что:
- Создает скрытые зависимости
- Усложняет тестирование
- Нарушает принципы SOLID
- Может привести к проблемам в многопоточной среде
Используйте Singleton осторожно и только когда действительно нужен единственный экземпляр объекта!
Помните: Singleton — это мощный паттерн, но используйте его разумно!