← Назад к вопросам

Когда стоит использовать паттерн Singleton?

1.3 Junior🔥 171 комментариев
#ООП и проектирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерн Singleton: Когда его использовать

Singleton — это паттерн проектирования, который гарантирует, что класс имеет только один экземпляр и предоставляет глобальную точку доступа к этому экземпляру. Это один из наиболее часто используемых (и часто критикуемых) паттернов в разработке. Давайте разберёмся, когда его действительно стоит использовать.

Когда Singleton полезен

1. Глобальная конфигурация приложения

class AppConfig {
private:
    static AppConfig* instance;
    AppConfig() { /* Инициализация */ }
    
public:
    static AppConfig* getInstance() {
        if (instance == nullptr) {
            instance = new AppConfig();
        }
        return instance;
    }
    
    std::string getDatabaseUrl() const;
    int getPort() const;
    bool isDebugMode() const;
};

AppConfig* AppConfig::instance = nullptr;

// Использование
std::string dbUrl = AppConfig::getInstance()->getDatabaseUrl();

2. Логирование

Централизованный логгер, используемый по всему приложению:

class Logger {
private:
    static Logger* instance;
    std::ofstream logFile;
    
    Logger() { logFile.open("app.log", std::ios::app); }
    
public:
    static Logger* getInstance() {
        if (instance == nullptr) {
            instance = new Logger();
        }
        return instance;
    }
    
    void log(const std::string& message) {
        logFile << "[" << getCurrentTime() << "] " << message << std::endl;
        logFile.flush();
    }
};

Logger* Logger::instance = nullptr;

// Использование
Logger::getInstance()->log("Application started");

3. Пулы ресурсов (Database Connection Pool)

class ConnectionPool {
private:
    static ConnectionPool* instance;
    std::queue<std::shared_ptr<Connection>> availableConnections;
    std::mutex mutex;
    static const int MAX_CONNECTIONS = 10;
    
    ConnectionPool() {
        for (int i = 0; i < MAX_CONNECTIONS; ++i) {
            availableConnections.push(std::make_shared<Connection>());
        }
    }
    
public:
    static ConnectionPool* getInstance() {
        if (instance == nullptr) {
            instance = new ConnectionPool();
        }
        return instance;
    }
    
    std::shared_ptr<Connection> getConnection() {
        std::lock_guard<std::mutex> lock(mutex);
        if (availableConnections.empty()) {
            return nullptr;  // Нет свободных соединений
        }
        auto conn = availableConnections.front();
        availableConnections.pop();
        return conn;
    }
    
    void releaseConnection(std::shared_ptr<Connection> conn) {
        std::lock_guard<std::mutex> lock(mutex);
        availableConnections.push(conn);
    }
};

ConnectionPool* ConnectionPool::instance = nullptr;

4. Кэш приложения

class Cache {
private:
    static Cache* instance;
    std::unordered_map<std::string, std::string> data;
    std::mutex mutex;
    
    Cache() { /* Инициализация */ }
    
public:
    static Cache* getInstance() {
        if (instance == nullptr) {
            instance = new Cache();
        }
        return instance;
    }
    
    void put(const std::string& key, const std::string& value) {
        std::lock_guard<std::mutex> lock(mutex);
        data[key] = value;
    }
    
    std::string get(const std::string& key) {
        std::lock_guard<std::mutex> lock(mutex);
        return data.count(key) ? data[key] : "";
    }
};

Cache* Cache::instance = nullptr;

Потокобезопасный Singleton (Modern C++)

В современном C++11+ рекомендуется использовать статическую переменную функции для гарантии потокобезопасности:

class ThreadSafeSingleton {
private:
    ThreadSafeSingleton() { /* Инициализация */ }
    
public:
    static ThreadSafeSingleton& getInstance() {
        static ThreadSafeSingleton instance;  // Инициализируется один раз
        return instance;
    }
    
    // Запрещаем копирование
    ThreadSafeSingleton(const ThreadSafeSingleton&) = delete;
    ThreadSafeSingleton& operator=(const ThreadSafeSingleton&) = delete;
};

// Использование
ThreadSafeSingleton& singleton = ThreadSafeSingleton::getInstance();

Когда НЕ использовать Singleton

  • Для тестирования — сложно мокировать Singleton
  • Для бизнес-логики — может скрывать зависимости
  • Когда можно использовать DI — внедрение зависимостей часто лучше
  • Для множественных экземпляров — если нужны разные конфигурации

Альтернативы

Dependency Injection:

class Application {
private:
    std::shared_ptr<Logger> logger;
    std::shared_ptr<AppConfig> config;
    
public:
    Application(std::shared_ptr<Logger> l, std::shared_ptr<AppConfig> c)
        : logger(l), config(c) {}
    
    void run() {
        logger->log("Started");
    }
};

Статические переменные:

void logMessage(const std::string& msg) {
    static std::ofstream logFile("app.log", std::ios::app);
    logFile << msg << std::endl;
}

Выводы

Используйте Singleton когда:

  • Нужна именно одна глобальная точка доступа (логирование, конфиг)
  • Ресурс должен быть инициализирован один раз и переиспользован
  • Нужна синхронизация доступа к ресурсу

НЕ используйте Singleton когда:

  • Можно использовать dependency injection
  • Нужна гибкость для тестирования
  • Требуется несколько различных конфигураций

Singleton — это инструмент для специфических задач. Правильно применённый, он упрощает архитектуру. Неправильно — усложняет тестирование и создаёт скрытые зависимости.

Когда стоит использовать паттерн Singleton? | PrepBro