Добавить в корзинуПозвонить
Найти в Дзене

Тестируем реализацию шаблона проектирования singleton c Google Mock

Согласно Википедии «синглтон» - это шаблон, ограничивающий создание экземпляра класса одним единственным экземпляром. Он описан в книге о паттернах проектирования «Банды четырёх», посвещенной способам решения повторяющихся проблем в объектно-ориентированном программировании. Паттерн используется когда в системе необходимо обеспечить существование ровно одного объекта. Шаблон «синглтон» позволяет: Пусть «синглтон» нужен для того чтобы гарантировать наличие только одного объекта Logger, записывающего сообщения в std::cout. class Logger { public:
virtual void Log(const std::string& message) {
std::cout << message << std::endl;
} }; Одна из реализаций «синглтона» может выглядеть следующим образом: Для того чтобы гарантировать существование единственности объекта класса LoggerSingleton помечаем конструктор копирования и оператор присваивания удаленными, а конструктор по умолчанию делаем приватным. Таким образом, объект LoggerSingleton можно создать лишь внутри статического метода Get

Согласно Википедии «синглтон» - это шаблон, ограничивающий создание экземпляра класса одним единственным экземпляром. Он описан в книге о паттернах проектирования «Банды четырёх», посвещенной способам решения повторяющихся проблем в объектно-ориентированном программировании. Паттерн используется когда в системе необходимо обеспечить существование ровно одного объекта.

Шаблон «синглтон» позволяет:

  • Гарантировать наличие только одного экземпляра
  • Обеспечить лёгкий доступ к этому экземпляру
  • Контролировать создание экземпляров (например, скрывать конструкторы класса)

Пусть «синглтон» нужен для того чтобы гарантировать наличие только одного объекта Logger, записывающего сообщения в std::cout.

class Logger {

public:
virtual void Log(const std::string& message) {
std::cout << message << std::endl;
}

};

Одна из реализаций «синглтона» может выглядеть следующим образом:

-2

Для того чтобы гарантировать существование единственности объекта класса LoggerSingleton помечаем конструктор копирования и оператор присваивания удаленными, а конструктор по умолчанию делаем приватным. Таким образом, объект LoggerSingleton можно создать лишь внутри статического метода GetInstance, что мы и делаем.

Такая реализация имеет следующие преимущества:

  • Если «синглтон» должен работать в многопоточной среде, то начиная со стандарта C++11, инициализация локальных статических переменных гарантированно потокобезопасна. Если два потока одновременно вызовут GetInstance() в первый раз, компилятор сам добавит необходимые блокировки, чтобы объект создался только один раз. Таким образом, не нужно вручную использовать std::mutex или std::call_once для переменной instance. Однако, доступ к переменной logger_ все еще требует применения мьютекса.
  • В отличие от реализации через динамическую память (new), где нужно думать, кто и когда вызовет delete, здесь деструктор LoggerSingleton будет вызван автоматически при выходе из программы.

Основным недостатком реализации является трудность тестирования, так как объект класса Loger жестко зашит в класс LoggerSingleton и его невозможно подменить на этапе тестирования.

Чтобы исправить этот недостаток используем метод внедрения зависимостей, основанных на применении абстракций, который был описан в одной из предыдущих статей.

Введем абстрактный класс

-3

и создадим мок, наследуемый от него

-4
-5

Теперь можно внедрять зависимость при помощи вызова метода SetLogger. Зависимость хранится в LoggerSingleton при помощи умного указателя std::shared_ptr.

Обратите внимание, метод Log также изменился. Теперь логируем только если logger_ не нулевой. Так сделано потому что синглтон хранит состояние, которое сохраняется между вызовами GetInstance. Для корректного тестирования нам нужно уметь очищать это состояние. Это можно сделать вызовом SetLogger(nullptr).

-6

В первом тесте проверяем что функция Log была вызвана 1 раз с корректным значением аргумента.

Во втором тесте мы не можем проверить факт того что Log не была вызвана ни одного раза, так как мы не внедряли объект класса MockLogger в качестве зависимости. Поэтому проверяем что вызов Log не приводит к генерации исключения.

Третий тест проверяет факт того, что переменная instance существует в единственном экземпляре. Таким образом мы доказываем что «синглтон» работает корректно.