Daily bit(e) C++. Паттерн Моносостояние (Monostate)

Добавлено 7 августа 2026 в 07:19

Daily bit(e) C++ 61, альтернатива паттерну Singleton, паттерн Monostate (моносостояние)

Daily bit(e) C++

Паттерн Monostate (не путать с std::monostate) – это паттерн, преследующий цели, схожие с Singleton.

В то время как Singleton допускает только один экземпляр, паттерн Monostate позволяет создавать экземпляры много раз, обеспечивая при этом наличие только одного экземпляра его внутреннего состояния.

#include <cstdint>
#include <mutex>
#include <string>

struct MonoConfig 
{
  MonoConfig() 
  {
    // Обеспечить единственную инициализацию вне статической цепочки.
    // Если нам не нужна многопоточная безопасность,
    // мы можем использовать логический флаг.
    std::call_once(flag_, populate_config);
  }

  // Интерфейс для доступа к моносостоянию
  uint64_t get_id() const 
  {
    return field1;
  }
  const std::string& get_label() const
  {
    return field2; 
  }

private:
  static std::once_flag flag_;
  static void populate_config()
  {
    // прочитать поля из источника конфигурации
    field1 = UINT64_C(42);
    field2 = "Hello World";
  }
  static uint64_t field1;
  static std::string field2;
};

// Все статические члены оставлены инициализированными по умолчанию
std::once_flag MonoConfig::flag_;
uint64_t MonoConfig::field1;
std::string MonoConfig::field2;

// Создать экземпляр объекта моносостояния
MonoConfig config;
// для доступа к глобальному состоянию
config.get_label();

// Создание дополнительных экземпляров не выполняет никаких действий.
// Обратите внимание, что при хранении в качестве члена он всё равно
// будет занимать минимум 1 байт, если мы не используем [[no_unique_address]].
MonoConfig a, b, c, d, e, f, g, i, j, k;

Пример на Compiler Explorer

Моносостояние (со всеми недостатками глобального состояния) может лучше подходить для тестирования.

#include <memory>

// В сочетании с паттерном PIMPL мы можем 
// имитировать/подделывать глобальное состояние:
struct ImplIface {};
struct Actual : ImplIface 
{
  static std::unique_ptr<ImplIface> make()
  {
    return std::make_unique<Actual>();
  }
};
struct Testing : ImplIface
{
  static std::unique_ptr<ImplIface> make()
  {
    return std::make_unique<Testing>();
  }
};

// Переключение активного типа в зависимости от тестирования/релиза
using ActiveType = Testing;

struct MonoPIMPL 
{
  MonoPIMPL()
  {
    std::call_once(flag_, [] { impl_ = ActiveType::make(); });
  }
  // раскрыть ImplIface, как любой другой PIMPL
private:
  static std::once_flag flag_;
  static std::unique_ptr<ImplIface> impl_;
};

std::once_flag MonoPIMPL::flag_;
std::unique_ptr<ImplIface> MonoPIMPL::impl_;

Пример на Compiler Explorer

Теги

C++ / CppDaily bit(e) C++Паттерны проектирования / Design Patterns