Daily bit(e) C++. Паттерн Моносостояние (Monostate)
Добавлено 7 августа 2026 в 07:19
Daily bit(e) C++ 61, альтернатива паттерну Singleton, паттерн Monostate (моносостояние)

Паттерн 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;
Моносостояние (со всеми недостатками глобального состояния) может лучше подходить для тестирования.
#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_;
