Домашняя страница Паттерн Singleton в C++. Неуверенное Да, или категорическое Нет? Часть 2
Публикация
Отменить

Паттерн Singleton в C++. Неуверенное Да, или категорическое Нет? Часть 2

Все вышеописанное — это особенности объектного программирования в С++ и против них не пойти… Если только не использовать объекта вообще. А что? Мы же хотим сделать статическую функциональность, зачем тут объект? Нам не нужно наследование от этого объекта, нам не нужны коллекции этих объектов, а нужна только функциональность. Заменим класс синглетон следующим кодом:

Луч света в темном царстве…

Все вышеописанное

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 вставок или указателей на функции.

Дело не в борьбе С и С++, а в том, что для решения определенных задач лучше подходят парадигмы и средства одного языка, а для других — другого. Важно уметь видеть достоинства и недостатки разных вариантов решения и выбирать тот из них, который наиболее подходит.

Публикация защищена лицензией CC BY 4.0 .

Паттерн Singleton в C++. Неуверенное Да, или категорическое Нет? Часть 1

Розетка-потайной тройник!