Re[5]: Singleton
От: MaximVK Россия  
Дата: 01.07.07 22:24
Оценка: +4
Здравствуйте, Пётр Седов, Вы писали:

Петр, у тебя какая-то странная логика. Твой вопрос был какой?

Всё-таки, чем опасен singleton?

Еще раз и по порядку.
1. SRP. От того, что в Scala есть "определения объектов", а C++ — подумать только! — статические члены, синглетон перестает нарушать SRP?
2. С тем, что синглетон увеличивает связность, я вижу ты согласился. Этим он и опасен. Точка! Если тебе хочется поговорить на тему "Сильная связность — это хорошо" так и скажи.
3. Testability. Опять не понял. От того, что есть классы, которые плохо поддаются тестированию следует, что синглетон не влияет отрицательно на тестируемость? Или может влияние чудесным образом пропадает от того, что юнит-тесты не всегда эффективны? Или понижение тестируемости приложения — это хорошо? Кстати, речь идет не тестируемости синглетона, с этим как правило проблем нет, а приложении в целом, и классов, которые его используют, в особенности.
4. По поводу нагнетатия сложности. Ты не поверишь, но все эти компонентные модели и сервис провайдеры как раз и придуманы для борьбы со сложностью.
5. Пример с выводом отладочной информации — крайне неудачен. А что если тебе понадобиться поменять логику вывода? А если она должна быть разной для разных классов? А если тебе надо поменять форматирование? И к тому же, протаптывание интерфейса логгера во всех методах — не единственная альтернатива.
6. По поводу double checking locking — это ирония была.

Ну и еще раз о глобальных объектах и глобальных переменных. Их основная проблема — именно в глобальности и способности менять свое состояние. Вычеркни одно из этих условий — и все станет значительно проще. Локальные переменные большой проблемы не представляют и глобальные константы — тоже.
Re[2]: подскажите паттерн №2
От: WolfHound  
Дата: 02.07.07 09:20
Оценка:
Здравствуйте, Пётр Седов, Вы писали:

ПС>Пуристы, наверное. Они любят догмы.

Не пуристы, а практики.
На моей памяти небыло еще ни одного синглетона который бы не создал проблем.
Из-за одних больше, из-за других меньше(ибо быстро убрали). Но проблемы были из-за всех.

ПС>На самом деле singleton – вполне нормальное решение. Несколько примеров:

Бу-га-га...

ПС>* Windows-реестр. Это глобальный объект в рамках компьютера. Доступен всем. Используется, к примеру, для регистрации COM-компонентов.

Мелкософты (в не официальных разговорах) сами признают тот факт что это была ошибка.
И я с ними согласен.

ПС>* Heap (распределитель памяти). Это глобальный объект в рамках процесса. Доступен всем (malloc/free, new/delete). Кстати, помимо C/C++ heap-а, имеется глобальный process heap от Windows (WinAPI-шная функция GetProcessHeap возвращает handle этого heap-а).

Хип это вобще интимное дело рантайма. В коде ничего про хип быть не должно. Совсем.
Есть функция создать объект (ака конструктор), а где он будет создан и когда убит проблемы исключительно рантайма.

ПС>* Очередь оконных сообщений. Это глобальный объект в рамках потока (thread). Доступна всем. Кстати, вначале поток не имеет очереди. Windows создаёт очередь неявно, когда поток первый раз вызывает user-функцию (то есть WinAPI-шную функцию из user32.dll).

Тоже плохое решение.
Есть множество случаев когда может быть выгодно много очередей сообщений для одного потока.
Посмотри например на сингулярити (исследовательсякая ОС от мелкософт)... как ты думаешь почему там нет очереди сообщений потока?
... << RSDN@Home 1.2.0 alpha rev. 673>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[5]: Singleton
От: AndrewJD США  
Дата: 02.07.07 12:52
Оценка: +1
Здравствуйте, Пётр Седов, Вы писали:

ПС>
ПС>class Clock
ПС>{
ПС>public:
ПС>  static void Init();
ПС>  static double Time();
ПС>};

ПС>


ПС>У низкой связности (coupling) тоже есть недостатки:

ПС>* Куча интерфейс-классов (как MemManager здесь
Автор: Пётр Седов
Дата: 28.06.07
) усложняет код.

Однако MemManager это уже довольно крайний случай, вряд ли кому нужен менджер памяти отличный от CRT.

ПС>* Снижается эффективность программы (из-за косвенности).

ИМО, это никогда не будет узким местом, поэтому экономить пару тактов проца бессмысленно.

ПС>Во-первых, не всё можно тестировать (например, как тестировать Clock?).

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

ПС>Во-вторых, если нужно тестировать сам глобальный объект и он реализован без GetInstance (который на самом деле не нужен), то можно закрыть глаза на требование единственности экземпляра и тестировать независимых близнецов singleton-а

Обычно проблема не с тестом синглетона, а с тестами кода его использующего.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[6]: Singleton
От: Пётр Седов Россия  
Дата: 02.07.07 17:06
Оценка:
Здравствуйте, AndrewJD, Вы писали:

AJD>Однако MemManager это уже довольно крайний случай, вряд ли кому нужен менджер памяти отличный от CRT.

Да, во многих случаях использование C/C++ heap-а (singleton в рамках процесса) годится. Многие программисты используют malloc/free, или глобальные new/delete, или STL-контейнеры с allocator-ом по умолчанию. При этом мало кого волнует, что нарушается SRP (?), повышается связность, понижается тестируемость (так как это скорее теоретические проблемы). Программы просто работают.
Но иногда стандартный heap не годится, нужно что-то побыстрее. Тогда разумно от него отказаться и использовать распределитель памяти, заточенный под конкретные нужды. Я таким постоянно пользуюсь. Кстати, это не heap, а арена, и она всё равно singleton .

ПС>>Во-первых, не всё можно тестировать (например, как тестировать Clock?).

AJD>Зато очень хочется тестировать код который исползует этот Clock, причем хочется контролировать значения которые этот Clock возвращает.
Тогда этот код должен использовать Clock не напрямую, а косвенно (через прослойку). Тогда можно подсунуть этому коду mock-часы. Но unit test-ы не всегда возможны/нужны, в этих случаях можно обойтись кодом попроще (с использованием singleton-а).
Пётр Седов (ушёл с RSDN)
Re[7]: Singleton
От: AndrewJD США  
Дата: 02.07.07 17:52
Оценка:
Здравствуйте, Пётр Седов, Вы писали:

ПС>Тогда этот код должен использовать Clock не напрямую, а косвенно (через прослойку). Тогда можно подсунуть этому коду mock-часы.

Если есть прослойка, то зачем синглетон?

ПС>Но unit test-ы не всегда возможны/нужны, в этих случаях можно обойтись кодом попроще (с использованием singleton-а).

Отказ от синглетонов повышает возможность использования unit test. Разве это не достаточная причина?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[3]: Singleton
От: Пётр Седов Россия  
Дата: 02.07.07 18:19
Оценка:
Здравствуйте, WolfHound, Вы писали:

ПС>>Пуристы, наверное. Они любят догмы.

WH>Не пуристы, а практики.
То есть singleton-ы используются исключительно теоретиками ?

WH>На моей памяти небыло еще ни одного синглетона который бы не создал проблем.

WH>Из-за одних больше, из-за других меньше(ибо быстро убрали). Но проблемы были из-за всех.
Странно . Меня часто singleton-ы устраивают. Когда перестают устраивать, заменяю чем-нибудь.

ПС>>На самом деле singleton – вполне нормальное решение. Несколько примеров:

WH>Бу-га-га...
Это серьёзный аргумент .

ПС>>* Windows-реестр. Это глобальный объект в рамках компьютера. Доступен всем. Используется, к примеру, для регистрации COM-компонентов.

WH>Мелкософты (в не официальных разговорах) сами признают тот факт что это была ошибка.
Можно ссылки на эти «неофициальные разговоры»? Или хотя бы аргументацию. И что именно Microsoft «признаёт»:
* Реестр – неудачное решение.
* Регистрировать COM-компоненты в реестре – неудачное решение.
?
Для регистрации COM-компонентов, по-моему, нужно какое-то глобальное хранилище. Не реестр, так что-нибудь другое. Но всё равно singleton в рамках Windows.

ПС>>* Heap (распределитель памяти). Это глобальный объект в рамках процесса.

WH>Хип это вобще интимное дело рантайма. В коде ничего про хип быть не должно. Совсем.
WH>Есть функция создать объект (ака конструктор), а где он будет создан и когда убит проблемы исключительно рантайма.
И что, использовать стандартный heap – грех? Подходит – используйте (это удобно, так как он доступен в любой точке кода). Не подходит – не используйте.
Вон STL-контейнеры (включая std::string) по умолчанию используют стандартный heap. Это часто подходит для решения задачи.

ПС>>* Очередь оконных сообщений. Это глобальный объект в рамках потока (thread).

WH>Тоже плохое решение.
Может, это и плохое решение, но оно работает на практике. Также стоит упомянуть asynchronous procedure call (APC) очередь (доступную через WinAPI-шную функцию QueueUserAPC). Это singleton в рамках потока. Доступна всем, у кого есть соответствующие права.

WH>Есть множество случаев когда может быть выгодно много очередей сообщений для одного потока.

Я не утверждал, что очередь оконных сообщений потока хорошо подходит для всех задач. Но для некоторых задач (пользовательский интерфейс) подходит.

WH>Посмотри например на сингулярити (исследовательсякая ОС от мелкософт)... как ты думаешь почему там нет очереди сообщений потока?

Не знаю. Безопасность?
Пётр Седов (ушёл с RSDN)
Re[8]: Singleton
От: Пётр Седов Россия  
Дата: 02.07.07 18:42
Оценка:
Здравствуйте, AndrewJD, Вы писали:

ПС>>Тогда этот код должен использовать Clock не напрямую, а косвенно (через прослойку). Тогда можно подсунуть этому коду mock-часы.

AJD>Если есть прослойка, то зачем синглетон?
Чтобы в любой точке кода можно было узнать настоящее (не mock) время.

ПС>>Но unit test-ы не всегда возможны/нужны, в этих случаях можно обойтись кодом попроще (с использованием singleton-а).

AJD>Отказ от синглетонов повышает возможность использования unit test. Разве это не достаточная причина?
Например, std::string использует стандартный heap (то есть singleton). При этом вполне можно писать unit test-ы, проверяющие std::string. Например:
std::string s1 = "abc";
std::string s2 = "xyz";
std::string s3 = s1 + s2;
assert(s3 == "abcxyz");
Пётр Седов (ушёл с RSDN)
Re[4]: Singleton
От: WolfHound  
Дата: 02.07.07 20:04
Оценка:
Здравствуйте, Пётр Седов, Вы писали:

ПС>Странно . Меня часто singleton-ы устраивают. Когда перестают устраивать, заменяю чем-нибудь.

Просто еще шишек не набил.
А вот когда синглетон придется выдерать из десятка метров исходников подвязаных на этот синглетон...

ПС>Для регистрации COM-компонентов, по-моему, нужно какое-то глобальное хранилище. Не реестр, так что-нибудь другое. Но всё равно singleton в рамках Windows.

А кто сказал что у винды хороший дизайн?

ПС>И что, использовать стандартный heap – грех? Подходит – используйте (это удобно, так как он доступен в любой точке кода). Не подходит – не используйте.

ПС>Вон STL-контейнеры (включая std::string) по умолчанию используют стандартный heap. Это часто подходит для решения задачи.
Для тебя мир ограничен С++? Сочувствую.
Я считаю что менеджер памяти программиста заботить вобще не должен.
1)Ибо программисту есть чем заняться кроме как памятью рулить.
2)Единственный способ безопасно и оптимально рулить памятью это статический анализ кода. Для языков типа С++ это конечно работать не будет. Ибо для этого нужно точно знать что происходит. В случе с С++ это не возможно.

ПС>Может, это и плохое решение, но оно работает на практике.

Малоли что работает на практике... вон С++ тоже работает на практике... правда плохо... слишком много телодвижений на пустом месте...

ПС>Также стоит упомянуть asynchronous procedure call (APC) очередь (доступную через WinAPI-шную функцию QueueUserAPC). Это singleton в рамках потока. Доступна всем, у кого есть соответствующие права.

Костыль. Служит исключительно для объезда других кривостей дизайна.
Я не могу представить зачем при хоть скольнибудь продуманном дизайне это может понадобиться.

ПС>Я не утверждал, что очередь оконных сообщений потока хорошо подходит для всех задач. Но для некоторых задач (пользовательский интерфейс) подходит.

Очередь сообщений для ГУИ? Привязанная к потоку? Хорошее решение? Ну-ну.

ПС>Не знаю. Безопасность?

Нет.
Банальное отделение мух от котлет.
Потоки отдельно. Очереди сообщений отдельно.
Таким образом каждый занимается своим делом.
И я в одном потоке могу работать с любым колличеством очередей сообщений.
... << RSDN@Home 1.2.0 alpha rev. 673>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[9]: Singleton
От: AndrewJD США  
Дата: 03.07.07 08:18
Оценка:
Здравствуйте, Пётр Седов, Вы писали:

AJD>>Если есть прослойка, то зачем синглетон?

ПС>Чтобы в любой точке кода можно было узнать настоящее (не mock) время.
А зачем ей быть синглетоном, с глобальной точкой доступа? . Это простойка вполне себе може пользоваться глобальными функциями, но она не должна быть доступна из произвольного места.

AJD>>Отказ от синглетонов повышает возможность использования unit test. Разве это не достаточная причина?

ПС>Например, std::string использует стандартный heap (то есть singleton). При этом вполне можно писать unit test-ы.
ИМО, heap это вырожденый случай. Давай возьмем какой-нибудь другой синглетон который реализует логику приложения, а не функционирования рантайма.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[5]: Singleton
От: Ужасть бухгалтера  
Дата: 08.07.07 13:52
Оценка:
ПС>>Странно . Меня часто singleton-ы устраивают. Когда перестают устраивать, заменяю чем-нибудь.
WH>Просто еще шишек не набил.
WH>А вот когда синглетон придется выдерать из десятка метров исходников подвязаных на этот синглетон...

Извиняюсь, что вмешиваюсь... А можно привести пример замены синглтона на альтернативное решение? Просто интересно.
Re[6]: Singleton
От: Пётр Седов Россия  
Дата: 08.07.07 19:09
Оценка: 6 (1)
Здравствуйте, Ужасть бухгалтера, Вы писали:

УБ>Извиняюсь, что вмешиваюсь...

Это форум, а не личная переписка .

УБ>А можно привести пример замены синглтона на альтернативное решение?

Пример в этой же ветке здесь
Автор: Пётр Седов
Дата: 28.06.07
:

int GetNumFromEdit(HWND hEdit)
{
  ...
  char* pBuf = new char[Len + 1];
  ...
  delete[] pBuf;
  ...
}

...
class MemManager
{
public:
  virtual void* AllocBlock(int Size) = 0;
  virtual void FreeBlock(void* pMem, int Size) = 0;
};

int GetNumFromEdit2(HWND hEdit, MemManager* pMemManager)
{
  ...
  char* pBuf = static_cast<char*>(pMemManager->AllocBlock((Len + 1) * sizeof(char)));
  ...
  pMemManager->FreeBlock(pBuf, (Len + 1) * sizeof(char));
  ...
}

Функция GetNumFromEdit использует глобальный C/C++ heap. Функция GetNumFromEdit2 принимает указатель на объект, реализующий интерфейс MemManager, поэтому ей можно подсунуть любой распределитель памяти (например, thread-local heap).
Пётр Седов (ушёл с RSDN)
Re[7]: Singleton
От: Ужасть бухгалтера  
Дата: 08.07.07 20:27
Оценка: 1 (1) +1
УБ>>А можно привести пример замены синглтона на альтернативное решение?
ПС>Пример в этой же ветке здесь
Автор: Пётр Седов
Дата: 28.06.07
:

ПС>Функция GetNumFromEdit использует глобальный C/C++ heap. Функция GetNumFromEdit2 принимает указатель на объект, реализующий интерфейс MemManager, поэтому ей можно подсунуть любой распределитель памяти (например, thread-local heap).

Понятно. Вместо синглтона подсовываем параметр. Но ведь, чтобы вместо синглтона передать параметр, надо сначала узнать этот параметр Откуда его получит вызывающая функция? Например, как быть с соединением с БД? Пусть библиотека имеет несколько точек входа. Ни в одной нельзя быть уверенным, что она будет вызвана первой. Но как только потребуется соединение с БД, надо либо подключиться (если нет подключения), либо взять существующее подключение.

Если возложить обязанность создать и передать подключение на пользователя библиотеки, то пользователь должен будет знать о внутренних деталях функционирования библиотеки. Реально же ему эти подробности могут быть вообще не интересны: БД там используется или файлы, например.

Реализовать подключение в каждой точке входа? А если могут использоваться несколько точек входа одновременно? Например, сначала выводится блок новостей, а потом блок анонсов.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.