Предположим, у нас есть классический одноэлементный анти-шаблон, который отвечает за три вещи:
class Singleton {
public:
// Globally accessible instance
static Singleton & instance();
// Public interface
void do_something();
private:
// Lifetime management
Singleton();
~Singleton();
}
и класс, который зависит от этого:
class Dependent {
public:
Dependent() : s(Singleton::instance()) {}
void do_something_else();
private:
Singleton & s;
};
Теперь мы хотели бынаписать модульный тест для синглтона:
void test_singleton() {
Singleton s; // Problem 1
s.do_something();
assert(/* some post-condition */);
}
и для зависимого класса:
struct StubSingleton : Singleton // Problem 2
{
int did_something;
StubSingleton : did_something(0) {}
void do_something() {++did_something;}
};
void test_dependent() {
StubSingleton s; // Problem 1
Dependent d(s); // Problem 3
d.do_something_else();
assert(s.did_something == 1);
}
Мы видим, что нужно преодолеть три проблемы:
- Мы не можем создавать и уничтожать экземпляры во время тестов;
- Мы не можем определить наши собственные подклассы для проверки того, как используется интерфейс;
- Мы не можем предоставить нашу собственную зависимость отзависимый класс.
Самый простой способ преодолеть эти проблемы - это реорганизовать синглтон-класс:
- Сделать конструктор и деструктор общедоступными, сняв с себя ответственность за управление временем жизни.the class;
- Сделайте интерфейс абстрактным, что позволит нам определить наши собственные реализации;
- Удалите глобальный экземпляр и измените зависимые классы, чтобы получить его зависимость по ссылке.
Итак, теперь наши классы выглядят так:
class Singleton {
public:
virtual ~Singleton() {}
virtual void do_something() = 0;
};
class RealSingleton : public Singleton
{
void do_something();
};
class Dependent {
public:
explicit Dependent(Singleton & s) : s(s) {}
void do_something_else();
private:
Singleton & s;
};
Теперь этот класс легко тестировать и почти так же легко использовать в производственной среде (вам просто нужно создать экземпляр RealSingleton
и передать ссылкитуда, где они нужны).Единственная проблема в том, что вы больше не можете называть это синглтоном.