Фото від Rodion Kutsaiev на Unsplash
Що таке патерн Singleton?
За своєю суттю, патерн Singleton — це шаблон проектування, який гарантує, що клас має лише один екземпляр протягом всього життєвого циклу додатка, при цьому надаючи глобальну точку доступу до цього екземпляра.
Звичайні випадки використання
Патерн Singleton корисний у таких сценаріях:
- Управління конфігурацією: коли потрібно забезпечити єдино правильні налаштування конфігурації для всього додатка.
- Сервіси логування: для підтримки централізованої системи логування.
- Управління кешем: для реалізації механізмів кешування на рівні всього додатка.
- Фіксована кількість екземплярів: ще один випадок, коли патерн Singleton підходить, це коли потрібно обмежити кількість створених екземплярів.
Основні компоненти реалізації
Патерн Singleton складається з трьох основних елементів:
- Приватний конструктор, який забороняє безпосереднє створення екземпляра.
- Приватний статичний екземпляр класу.
- Публічний статичний метод, який повертає екземпляр.
Ось як виглядає базова реалізація на TypeScript і Java:
class Singleton {
private static instance: Singleton;
private constructor() {}
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
public doSomething() {
// Тут знаходиться цікава реалізація
}
}
public class Singleton {
// Приватний статичний екземпляр
private static Singleton instance;
// Приватний конструктор забороняє створення екземпляра
private Singleton() {}
// Публічний статичний метод доступу
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void doSomething() {
System.out.println("Doing something...");
}
}
Переваги, недоліки та проблеми
Переваги
- Глобальний доступ: надає єдину точку доступу.
- Спільний стан: ідеально підходить для спільного використання тільки для читання стану між компонентами додатка.
- Ледача ініціалізація: екземпляр створюється тільки за потреби.
- Контроль ресурсів: гарантує створення лише одного екземпляра, що економить ресурси системи.
Недоліки
- Проблеми з глобальним станом: у випадках, коли стан управляється класом singleton, стає важче зрозуміти та відлагоджувати код, оскільки будь-яка частина додатка може змінювати спільний стан.
- Проблеми з тестуванням: використання singleton може ускладнити юніт-тестування, оскільки спільний стан змінюється між тестами, що може викликати залежність від порядку виконання тестів і непередбачувану взаємодію.
- Проблеми з паралелізмом: у багатопоточних середовищах необхідно бути обережним.
- Сховані залежності: залежності через Singleton можуть бути менш очевидними, ніж через явні параметри.
- Порушення принципів: Singleton часто порушує принципи єдиної відповідальності (виконує як основну функціональність, так і управління екземплярами) і інверсії залежностей.
- Проблеми з паралелізмом: у розподілених системах або багатокорпусних середовищах підтримка "єдиного" екземпляра може бути складною або неможливою.
- Обмеження наслідування і розширення: Приватні конструктори та статичні методи часто ускладнюють розширення функціональності Singleton.
- Альтернативи часто кращі: Фреймворки ін'єкції залежностей та інші патерни зазвичай надають переваги Singleton з меншими недоліками.
Кращі практики та типові помилки
Те, що слід робити:
- Робіть конструктор приватним, щоб заборонити пряме створення екземпляра.
- Розгляньте ледачу ініціалізацію для кращої продуктивності.
- Реалізуйте захист від багатопоточності, якщо це необхідно.
Те, що не слід робити:
- Не використовуйте Singleton як глобальну змінну.
- Уникайте створення непотрібних Singleton.
- Уникайте створення Singleton, коли стан може бути оновлений з різних джерел.
Переконайтесь, що захист від багатопоточності реалізовано в умовах одночасного доступу.
Сучасні альтернативи
Як розвивається програмна інженерія, з'явилися кращі альтернативи патерну Singleton, які стали популярнішими або набули більшого визнання:
- Ін'єкція залежностей (Dependency Injection): Ін'єкція залежностей через конструктор/властивість чітко демонструє намір і значно полегшує тестування за допомогою мок-реалізацій. Крім того, реєстрація сервісів як одиничних екземплярів у контейнері DI має свої переваги, оскільки він управляє їх життєвим циклом і надає екземпляр за запитом.
- Статичні фабричні методи (Static Factory Methods): Статичні фабричні методи, які контролюють створення екземплярів, є більш гнучкими, ніж жорстка реалізація Singleton, і можуть розвиватися без порушення існуючого коду та вибірково повертати спільні екземпляри на основі параметрів.
- Модулі (Module, ES6, Python): Використання імпорту спільних сервісів з модульної системи в мовах, де це доступно, оскільки вона сама керує ініціалізацією і це ясніше, ніж традиційний патерн Singleton.
Висновок
Патерн Singleton, хоча й простий і корисний, має свої недоліки, особливо при тестуванні, спільному використанні стану та в розподілених середовищах. Завжди враховуйте можливості, які надає ваша мова програмування/фреймворк/середовище, та перевіряйте, чи можуть інші патерни або варіанти краще відповідати потребам вашої системи.
Чи була ця стаття корисною? Слідкуйте за мною, щоб отримувати більше інформації про патерни програмування та найкращі практики розробки.
Перекладено з: Understanding the Singleton Pattern: When One Instance Rules Them All