Ответ
В C++ проектах я применял Singleton крайне редко и осознанно, только для объектов, которые действительно должны существовать в единственном экземпляре на протяжении всей жизни программы и быть доступными из разных частей кода. Основные сценарии:
- Логирование (Logger): Глобальный логгер, в который пишут все модули.
- Доступ к конфигурации приложения: Единый объект, загружающий настройки из файла.
- Менеджер ресурсов: Например, кэш текстур или пул соединений с базой данных.
Реализация в современном C++ (потокобезопасная, с отложенной инициализацией):
class DatabaseConnectionPool {
private:
DatabaseConnectionPool() { /* установка соединений */ } // Приватный конструктор
~DatabaseConnectionPool() = default;
// Запрещаем копирование и присваивание
DatabaseConnectionPool(const DatabaseConnectionPool&) = delete;
DatabaseConnectionPool& operator=(const DatabaseConnectionPool&) = delete;
public:
static DatabaseConnectionPool& getInstance() {
// Потокобезопасно начиная с C++11
static DatabaseConnectionPool instance;
return instance;
}
Connection getConnection() { /* ... */ }
void returnConnection(Connection conn) { /* ... */ }
};
// Использование
auto& pool = DatabaseConnectionPool::getInstance();
auto conn = pool.getConnection();
Важно понимать недостатки: Singleton создает скрытые глобальные зависимости, что усложняет модульное тестирование. Часто лучшей альтернативой является передача зависимости через конструктор (Dependency Injection). Я использую Singleton только тогда, когда его преимущества перевешивают эти недостатки, и объект по своей природе является истинно глобальным.
Ответ 18+ 🔞
Давай разберем этот ваш синглтон, ребята. Ну что за мода пошла — на каждом углу его суют, как будто панацея от всех бед. А потом сидишь и думаешь, э бошка думай, откуда у тебя в тестах эти скрытые зависимости вылезают, которые нихуя не замокать.
Я его применял, конечно, но редко и с оглядкой, будто по охуенно тонкому льду идешь. Только для таких сущностей, которые реально должны быть в одном экземпляре на всю жизнь программы и быть доступными отовсюду. Иначе — зачем? Чтобы потом волнение ебать испытывать при рефакторинге?
Основные сценарии, где он хоть как-то оправдан:
- Логирование (Logger): Ну тут без вариантов. Один глобальный логгер, в который все модули пишут. Иначе начнется пиздопроебибна с дублированием логов и разными форматами.
- Доступ к конфигурации приложения: Один объект, который разок прочитал
config.jsonи дальше раздает настройки. Удобно, но тут уже подозрение ебать чувствую — а не передать ли конфиг явно? - Менеджер ресурсов: Ну, типа кэш текстур или, как в примере ниже, пул соединений к базе. Он один на всю программу, и логично, чтобы все брали из одной бочки.
Смотри, как это сейчас модно делать на C++ (чтоб потокобезопасно и без костылей):
class DatabaseConnectionPool {
private:
DatabaseConnectionPool() { /* установка соединений */ } // Спрятали конструктор, хитрая жопа
~DatabaseConnectionPool() = default;
// Запрещаем копирование и присваивание — чтобы не вышло два пула
DatabaseConnectionPool(const DatabaseConnectionPool&) = delete;
DatabaseConnectionPool& operator=(const DatabaseConnectionPool&) = delete;
public:
// Вот тут вся магия
static DatabaseConnectionPool& getInstance() {
// Начиная с C++11 `static` внутри функции потокобезопасен. Красота.
static DatabaseConnectionPool instance;
return instance;
}
Connection getConnection() { /* ... */ }
void returnConnection(Connection conn) { /* ... */ }
};
// Использование — просто и понятно
auto& pool = DatabaseConnectionPool::getInstance();
auto conn = pool.getConnection();
Выглядит-то красиво, ёпта. Но важно понимать недостатки, а их овердохуища: Эта штука создаёт те самые скрытые глобальные зависимости. Ты в одном модуле вызываешь getInstance(), и теперь твой модуль намертво привязан к этой конкретной реализации. Как его тестировать изолированно? Да нихуя не как. Придётся или городить костыли, или мокать глобальное состояние — а это уже, простите, мудя полная.
Часто гораздо лучше просто передать зависимость через конструктор (этот ваш Dependency Injection). Так код становится чище, прозрачнее и тестируемее. Я синглтон юзаю только тогда, когда его плюшки реально перевешивают все эти головные боли, и объект по своей природе — истинно глобальный, как тот же логгер. Во всех остальных случаях — да похуй, не надо.