Все вышеописанное — это особенности объектного программирования в С++ и против них не пойти… Если только не использовать объекта вообще. А что? Мы же хотим сделать статическую функциональность, зачем тут объект? Нам не нужно наследование от этого объекта, нам не нужны коллекции этих объектов, а нужна только функциональность. Заменим класс синглетон следующим кодом:
Луч света в темном царстве…
Все вышеописанное
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Manager.h
/////////////////////////////
namespace Manager
{
bool Create();
void Destroy();
void Do();
}
// Manager.cpp
/////////////////////////////
#include "Manager.h"
namespace Manager
{
{
const int ARRAY_SIZE = 10;
int g_valueArray[ ARRAY_SIZE ];
int g_value = 0;
}
//
bool Create()
{
...
}
void Destroy()
{
...
}
void Do()
{
g_value = rand() %10;
}
}
То есть, мы объявили и реализовали необходимые нам функции, объединив их общим пространством имен. Все недостатки 1.1., 1.2., 1.3. мы обошли, т.к. мы не использовали класса. Мы получили что хотели быстроту в работе, а так же возможность использовать эту функциональность из любого места, достаточно просто написать Manager::Do().
Кроме того, внешнему миру дается только то, что ему нужно, а все детали реализации, внутренние константы (такие как ARRAY_SIZE), внутренние переменные, внутренние структуры будут определяться только в одном файле (*.cpp) и при их изменение будет перекомпилирован только один файл. Если данному коду нужна дополнительная функциональность, то она тоже будет добавляться в файл *.cpp, а не *.h, поэтому все файлы, которые будут использовать наш менеджер (подключать файл Meneger.h) будут зависеть только от его интерфейса, а не от других файлов.
Если проект должен быть кросплатформенным, то мы так же легко сможем этого добиться. Достаточно сделать разные файлы реализации при том же едином интерфейсе.
1
2
3
4
5
6
7
8
9
10
11
// Manager.h
/////////////////////////////
namespace Manager
{
bool Create();
void Destroy();
void Do();
}
// Manager_Win32.cpp
// Manager_PS3.cpp
Понятно, что на одной платформе будет использоваться один файл, а на другой — другой. Заметьте, что количество файлов необходимое для данного варианта 3, а не 5, как это было в случае с классами.
Кроме того, всегда можно вынести общую для системы функциональность в отдельные файлы, и подключать их для всех платформ, однако эта функциональность будет видима только системе, и поэтому при изменение каких то деталей в ней будет произведена перекомпиляция только системы, а код который ее использует будет нетронут. Пример:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Manager.h
/////////////////////////////
namespace Manager
{
bool Create();
void Destroy();
void Do();
}
// Manager_Internal.h
/////////////////////////////
namespace Manager
{
struct SomeManagerStruct
{
int val;
};
enum SomeManagerEnum
{
};
}
// Manager_Win32.cpp
/////////////////////////////
#include "Manager.h"
#include "Manager_Internal.h"
namespace Manager
{
SomeManagerStruct g_struct;
void Do()
{
g_struct.val = rand()%10;
}
}
// Manager_PS3.cpp
/////////////////////////////
#include "Manager.h"
#include "Manager_Internal.h"
...
Теперь файл Manager.h будет использовать сторонний код, а все внутренние детали системы определенные в Manager_Internal.h, останутся сокрыты в ней и не будут видимы извне.
Выводы:
В общем использовать Singleton в языке C++ решение не благородное, сопряженное с потерями как в производительности, так и в компиляции.
Комментарии
mrdekk, 13 окт. 2010, 00:57
Развивая тему известного холивара С vs C++, можно тогда вообще от объектно-ориентированности отказаться? Зачем все это надо если есть структуры+переменные+пространство имен?
Все нагромождения языка С++ зачем? И как быть если платформенность проявляется уже в объявлении интерфейса?
FiloXSee, 13 окт. 2010, 13:58
Немного не корректное замечание. В статье указывается на несостоятельность ООП для паттерна Синглетон. Этот паттерн создает статическую функциональность, поэтому незачем использовать класс для этого.
Но если мы начинаем работать с объектами, которых может быть множество, они могут конкретизировать друг друга (т.е. удобно использовать наследование) и обрабовывать иерархии и коллекции, то от ООП никуда не деться. Иначе придется писать кучу switch вставок или указателей на функции.
Дело не в борьбе С и С++, а в том, что для решения определенных задач лучше подходят парадигмы и средства одного языка, а для других — другого. Важно уметь видеть достоинства и недостатки разных вариантов решения и выбирать тот из них, который наиболее подходит.