Singleton действительно антипаттерн в enterprize приложения?
От: mr.sashich  
Дата: 09.08.07 13:30
Оценка:
Я понимаю, что тема возможно избита, но все же у кого какие мысли?
Есть критерии использования этого паттерна, которые выверены на практике.
Только не надо копировать описание синопсиса этого паттерна из "Розовой книжки по Java".
Это будет лишнее =)
Re: Singleton действительно антипаттерн в enterprize приложе
От: Курилка Россия http://kirya.narod.ru/
Дата: 09.08.07 13:33
Оценка: :)
Здравствуйте, mr.sashich, Вы писали:

MS>Я понимаю, что тема возможно избита, но все же у кого какие мысли?

Почему обязательно enterprise? Он по любому антипаттерн, т.к. заведомо повышает связность приложения.
Почему Singleton антипаттерн
От: IB Австрия http://rsdn.ru
Дата: 09.08.07 13:54
Оценка: 311 (24) +3
#Имя: FAQ.design.singleton_antipattern
Здравствуйте, mr.sashich, Вы писали:

MS>Я понимаю, что тема возможно избита, но все же у кого какие мысли?

Итого дискуссий по синглтону:

"главная проблема синглтона в том, что это первый паттерн описанный в GoF" (c) MaximVK. На него набрасываются и не замечают его недостатков, из коих:

1. Синглтон нарушает SRP (Single Responsibility Principle) — класс синглтона, помимо того чтобы выполнять свои непосредственные обязанности, занимается еще и контролированием количества своих экземпляров.
2. Глобальное состояние. Про вред глобальных переменных вроде бы уже все знают, но тут та же самая проблема. Когда мы получаем доступ к экземпляру класса, мы не знаем текущее состояние этого класса, и кто и когда его менял, и это состояние может быть вовсе не таким, как ожидается. Иными словами, корректность работы с синглтоном зависит от порядка обращений к нему, что вызывает неявную зависимость подсистем друг от друга и, как следствие, серьезно усложняет разработку.
3. Зависимость обычного класса от синглтона не видна в публичном контракте класса. Так как обычно экземпляр синглтона не передается в параметрах метода, а получается напрямую, через GetInstance(), то для выявления зависимости класса от синглтона надо залезть в тело каждого метода — просто просмотреть публичный контракт объекта недостаточно.
4. Наличие синглтона понижает тестируемость приложения в целом и классов, которые используют синглтон, в частности. Во-первых, вместо синглтона нельзя подпихнуть Mock-объект, а во-вторых, если синглтон имеет интерфейс для изменения своего состояния, то тесты начинают зависеть друг от друга.
Говоря же проще — синглтон повышает связность, и все вышеперечисленное, в том или ином виде, есть следствие повышения связности.

Естественно, можно акккуратненько пройти по граблям и использовать синглетон, но (цитата из доки к пикоконтейнеру) "Overuse makes for bad solutions. At the enterprise level, it makes for very very bad solutions"...
Тем более, что при тщательном рассмотрении вопроса, использования синглтона, как правило, можно легко избежать. А если можно легко избежать, значит это нужно сделать, чтобы удержать себя от излишнего соблазна "оверюза"... Например, для контроля количества экземпляров объекта вполне можно (и нужно) использовать различного рода фабрики.
Наибольшая же опасность, как было сказано, подстерегает при попытке построить на основе сиглтонов всю архитектуру приложения, такому подходу существует масса замечательных альтернатив. Например, IoC контейнеры — там проблема контроля создания сервисов решается естественным образом, так как они, по сути, являются "фабриками на стероидах" =). Другой альтернативой являются Service Locator-ы, из известных вариантов этого подхода — паттерн IServiceProvider.

Добавлено в FAQ. ДХ
... << RSDN@Home 1.2.0 alpha rev. 673>>
Мы уже победили, просто это еще не так заметно...
Re[2]: Singleton действительно антипаттерн в enterprize прил
От: IT Россия linq2db.com
Дата: 09.08.07 23:17
Оценка: :)
Здравствуйте, IB, Вы писали:

IB>Другой альтернативой являются Service Locator-ы, из известных вариантов этого подхода — паттерн IServiceProvider.


А сервис у кого запрашивать?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Singleton действительно антипаттерн в enterprize прил
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.08.07 02:27
Оценка: +2 :)
Здравствуйте, IT, Вы писали:
IT>А сервис у кого запрашивать?
У другого сервиса.
Сервиспровайдера тебе засунут через IoC. Вся идея в том, что тебе не надо делать в каждом классе по восемнацать IoC-входов: для логгирования, для транзакций, для коммуникаций и т.п. Ты делаешь одну дырку, в которую тебе суют IServiceProvider и ты окучиваешь его по мере необходимости.

Лично меня всегда такие схемы напрягали, потому что совершенно не видно, кто кого использует, и откуда что берется. Прямо как в анекдоте:
— Деньги где берешь?
— В тумбочке.
— А в тумбочке они откуда?
— Жена кладет
Ну и так далее. Но, наверное, есть какой-то способ расследовать такие штуки.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Singleton действительно антипаттерн в enterprize прил
От: mr.sashich  
Дата: 10.08.07 05:13
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, IT, Вы писали:

IT>>А сервис у кого запрашивать?
S>У другого сервиса.
S>Сервиспровайдера тебе засунут через IoC. Вся идея в том, что тебе не надо делать в каждом классе по восемнацать IoC-входов: для логгирования, для транзакций, для коммуникаций и т.п. Ты делаешь одну дырку, в которую тебе суют IServiceProvider и ты окучиваешь его по мере необходимости.

S>Лично меня всегда такие схемы напрягали, потому что совершенно не видно, кто кого использует, и откуда что берется. Прямо как в анекдоте:

S>- Деньги где берешь?
S>- В тумбочке.
S>- А в тумбочке они откуда?
S>- Жена кладет
S>Ну и так далее. Но, наверное, есть какой-то способ расследовать такие штуки.

А если IoC контейнер не используется как подсунуть одно "окно" не используя сингелтон?
Re[5]: Singleton действительно антипаттерн в enterprize прил
От: IB Австрия http://rsdn.ru
Дата: 10.08.07 07:39
Оценка:
Здравствуйте, mr.sashich, Вы писали:

MS>А если IoC контейнер не используется как подсунуть одно "окно" не используя сингелтон?

Через GetService(...). Сервисы образуют иерархию, каждый дочерний сервис имеет ссылку на родителя, и если при вызове GetService искомый сервис на данном уровне не нашелся, вызов делегируется выше по иерархии, и так до тех пор пока не доберется до корневого сервис-локатора.
В этом смысле синглтон еще большая гадость, так как позволяет добраться до корневого сервиса в обход иерархии.

Почитай подробнее про IServiceProvider, например здесь: Lightweight Containers and Plugin Architectures: Dependency Injection and Dynamic Service Locators in .NET
... << RSDN@Home 1.2.0 alpha rev. 673>>
Мы уже победили, просто это еще не так заметно...
Re[2]: Singleton действительно антипаттерн в enterprize прил
От: adontz Грузия http://adontz.wordpress.com/
Дата: 10.08.07 13:58
Оценка: 7 (1) +1 :)
Здравствуйте, IB, Вы писали:

IB>"главная проблема синглтона в том, что это первый паттерн описанный в GoF" (c) MaximVK. На него набрасываются и не замечают его недостатков, из коих:


Это не недостатки синглтона ни в коем случае, не надо воодить народ к заблуждение.

IB>1. Синглтон нарушает SRP (Single Responsibility Principle) — класс синглтона, помимо того чтобы выполнять свои непосредственные обязанности, занимается еще и контролированием количества своих экземпляров.


Это глупость, причём документально подтверждённая GoF. Действительно, в GoF синглтон описан как
class Singleton {
public:
    static Singleton* Instance();
protected:
    Singleton();
private:
    static Singleton* _instance;
}

и в таком виде описанная тобой проблема существует. Однако, не стоит заниматься буквоедством, тем более там, где это нелепо выглядит. Пример из книжки всегда прост и передаёт лишь суть и никогда не является объектом для дословного копирования. Всякий нормальный программист отделяет задачу контроля количества копий и пишет нечто вроде
template<T>
class singleton
{
    public:
        static const T * get_instance();
    private:
        static T * _instance;
}

либо
class Singleton<T> where T: new
{
    private static T _instance = default(T);
    public static T Instance { get; }
}
// или даже
class Singleton<T> where T: new, ISingleton
{
    private static T _instance = default(T);
    public static T Instance { get; }
}

либо как-то ещё, смотря от языка.

IB>2. Глобальное состояние. Про вред глобальных переменных вроде бы уже все знают, но тут та же самая проблема. Когда мы получаем доступ к экземпляру класса, мы не знаем текущее состояние этого класса, и кто и когда его менял, и это состояние может быть вовсе не таким, как ожидается. Иными словами, корректность работы с синглтоном зависит от порядка обращений к нему, что вызывает неявную зависимость подсистем друг от друга и, как следствие, серьезно усложняет разработку.


Singleton это stateless объект. Всякая другая его реализация ошибочна. Я тебя тут вообще не понял. Ты сперва сделал синглотон statefull объектом, а потом начал рассказывать какой синглотон плохой. Но это не синглтон плохой, это ты плохой, что сделал его statefull. Не надо путать тёплое с мягким.

IB>3. Зависимость обычного класса от синглтона не видна в публичном контракте класса. Так как обычно экземпляр синглтона не передается в параметрах метода, а получается напрямую, через GetInstance(), то для выявления зависимости класса от синглтона надо залезть в тело каждого метода — просто просмотреть публичный контракт объекта недостаточно.


Опять таки, ты сперва делаешь неверное предположение, а потом исходя из него строишь какие-то домыслы о недостатках синглтона. Кто сказал что зависимость обычного класса от синглтона должна быть видна? Вот у меня DAL зависит от системы журналирования, но из внешнего интерфейса DAL это не видно. И что? Я не вижу тут абсолютно никакой проблемы, тем более не вижу проблемы в синглтоне через который ведётся журналирования. Такого рода зависимости видны в настройках проекта и этого боле чем достаточно.

IB>4. Наличие синглтона понижает тестируемость приложения в целом и классов, которые используют синглтон, в частности. Во-первых, вместо синглтона нельзя подпихнуть Mock-объект, а во-вторых, если синглтон имеет интерфейс для изменения своего состояния, то тесты начинают зависеть друг от друга.


Во-первых, подкинуть Mock объект запросто можно (смотри второй пример на C#), во-вторых наличие состояний у синглтона это зло, причём зло в мозгах программиста, а не в синглтоне.

IB>Говоря же проще — синглтон повышает связность, и все вышеперечисленное, в том или ином виде, есть следствие повышения связности.


Да вообще зависимость одних модулей от других повышает связность

IB>Естественно, можно акккуратненько пройти по граблям и использовать синглетон, но (цитата из доки к пикоконтейнеру) "Overuse makes for bad solutions. At the enterprise level, it makes for very very bad solutions"...


Граблей никаких нет, если есть элементарное понимание области применения синглтона.

IB>Наибольшая же опасность, как было сказано, подстерегает при попытке построить на основе сиглтонов всю архитектуру приложения, такому подходу существует масса замечательных альтернатив. Например, IoC контейнеры — там проблема контроля создания сервисов решается естественным образом, так как они, по сути, являются "фабриками на стероидах" =). Другой альтернативой являются Service Locator-ы, из известных вариантов этого подхода — паттерн IServiceProvider.


Объясни мне доступно чем
GetService(typeof(MyService))
принципиальное лучше чем
Singleton<MyService>.Instance
если у нас всего один сервис.
Аргументы за сервисы, которые ты приводишь, актуальны, когда есть потребность в Chain of Responsibility, но это совсем другая задача.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[3]: Singleton действительно антипаттерн в enterprize прил
От: Cyberax Марс  
Дата: 10.08.07 14:07
Оценка: +6
Здравствуйте, adontz, Вы писали:

A>Singleton это stateless объект. Всякая другая его реализация ошибочна. Я тебя тут вообще не понял. Ты сперва сделал синглотон statefull объектом, а потом начал рассказывать какой синглотон плохой. Но это не синглтон плохой, это ты плохой, что сделал его statefull. Не надо путать тёплое с мягким.

Можно вопрос? А какой смысл в stateless-синглтоне? Чем он лучше класса со статическими методами? Тем более, что в статических методах нам еще компилятор по рукам надает, если мы добавим нестатическое поле в класс.
Sapienti sat!
Re[4]: Singleton действительно антипаттерн в enterprize прил
От: IT Россия linq2db.com
Дата: 10.08.07 14:10
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

IT>>А сервис у кого запрашивать?

S>У другого сервиса.
S>Сервиспровайдера тебе засунут через IoC. Вся идея в том, что тебе не надо делать в каждом классе по восемнацать IoC-входов: для логгирования, для транзакций, для коммуникаций и т.п. Ты делаешь одну дырку, в которую тебе суют IServiceProvider и ты окучиваешь его по мере необходимости.

Дырка может и одна, да только вынуть через неё всё что нужно не всегда получается. Иногда приходится через неё вытягивать что-то, у чего уже можно вытянуть то, что надо. А иногда таких приседаний надо сделать 2-3. Мэджик ещё тот получается. Даже не знаю что хуже, синглетон или вот такой сервис-провайдер.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Singleton действительно антипаттерн в enterprize прил
От: IT Россия linq2db.com
Дата: 10.08.07 14:10
Оценка: :))
Здравствуйте, adontz, Вы писали:

A>Объясни мне доступно чем
GetService(typeof(MyService))
принципиальное лучше чем
Singleton<MyService>.Instance
если у нас всего один сервис.


Щас тебя порвут.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Singleton действительно антипаттерн в enterprize прил
От: Курилка Россия http://kirya.narod.ru/
Дата: 10.08.07 14:11
Оценка:
Здравствуйте, adontz, Вы писали:

A>Singleton это stateless объект. Всякая другая его реализация ошибочна. Я тебя тут вообще не понял. Ты сперва сделал синглотон statefull объектом, а потом начал рассказывать какой синглотон плохой. Но это не синглтон плохой, это ты плохой, что сделал его statefull. Не надо путать тёплое с мягким.


Довольно странное утверждение...
Какой смысл вообще в stateless синглтоне? Чем он отличается от статик-методов?
Имхо смысл синглтона в возможности ленивой инициализации (i.e. statefull) и управления числом экземпляров (для stateless тоже не вижу смысла).
Плюс, объясни, если не сложно, как в
Singleton<MyService>.Instance

ты подсунешь мок? Или Instance — property, которая реализует фабрику?
Re[5]: Singleton действительно антипаттерн в enterprize прил
От: IB Австрия http://rsdn.ru
Дата: 10.08.07 14:48
Оценка:
Здравствуйте, IT, Вы писали:

IT>Дырка может и одна, да только вынуть через неё всё что нужно не всегда получается.

Ну это уж, извиняюсь, особенности реализации конкретной дырки..
Понятно, что кривой реализацией можно самую красивую идею испохабить, хотя конечно ISP не самый очевидный паттерн, со своими нюансами...
... << RSDN@Home 1.2.0 alpha rev. 673>>
Мы уже победили, просто это еще не так заметно...
Re[4]: Singleton действительно антипаттерн в enterprize прил
От: adontz Грузия http://adontz.wordpress.com/
Дата: 10.08.07 16:57
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Можно вопрос? А какой смысл в stateless-синглтоне? Чем он лучше класса со статическими методами? Тем более, что в статических методах нам еще компилятор по рукам надает, если мы добавим нестатическое поле в класс.


Синглтон лучше класса со статическими методами двумя вещами: отложенной инициализацией и возможностью реализовать интерфейс.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[5]: Singleton действительно антипаттерн в enterprize прил
От: Cyberax Марс  
Дата: 10.08.07 17:05
Оценка:
Здравствуйте, adontz, Вы писали:

C>>Можно вопрос? А какой смысл в stateless-синглтоне? Чем он лучше класса со статическими методами? Тем более, что в статических методах нам еще компилятор по рукам надает, если мы добавим нестатическое поле в класс.

A>Синглтон лучше класса со статическими методами двумя вещами: отложенной инициализацией и возможностью реализовать интерфейс.
Что можно инициализировать в stateless-синглтоне?

Интерфейс — это уже интереснее. Но я слабо представляю когда это нужно.
Sapienti sat!
Re[4]: Singleton действительно антипаттерн в enterprize прил
От: adontz Грузия http://adontz.wordpress.com/
Дата: 10.08.07 17:05
Оценка: -1
Здравствуйте, Курилка, Вы писали:

К>Довольно странное утверждение...

К>Какой смысл вообще в stateless синглтоне? Чем он отличается от статик-методов?

Уже писал: отложенная инициализация + реализация интерфейса.

К>Или Instance — property, которая реализует фабрику?


Как вариант. Сделать
Singleton<IMyService>.Instance;

и возвращать разные реализации IMyService в зависимости от того находимся ли мы в режиме тестирования или нет.
Учитывая специфику задачи (нам нужен compile-time полиморфизм) я бы предпочёл в 90% случаев ограничиться условной компиляцией.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[6]: Singleton действительно антипаттерн в enterprize прил
От: adontz Грузия http://adontz.wordpress.com/
Дата: 10.08.07 17:19
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Что можно инициализировать в stateless-синглтоне?

C>Интерфейс — это уже интереснее. Но я слабо представляю когда это нужно.

Зависит от синглтона. Возьмём конкретный, близкий почти всем, пример — журналирование.
Вызов Logger.Log(string message) очевидно не меняет состояния Logger, потому что у него нет никакого состояния. В терминах Си++ это
class Logger
{
    public:
        void Log(string message) const;
}

С другой стороны в процессе инициализации я могу захотеть открыть файл, сокет, соединение с БД и т.д.
Что касается интерфейса, в зависимости от настроек приложения, системы, аппаратной конфигурации могут возвращаться разные версии синглтона. То есть для
interface ILogger
{
    public:
        virtual void Log(string message) const = 0;
}

Могут быть возвращены LoggerRuRu, LoggerEnUs, LoggerOptimizedForPentium4, LoggerWithSelfTrace и т.п. Естественно, что для того кто использует синглтон это должно быть скрыто. Интерфейсы тут помогают.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[7]: Singleton действительно антипаттерн в enterprize прил
От: Курилка Россия http://kirya.narod.ru/
Дата: 10.08.07 17:24
Оценка:
Здравствуйте, adontz, Вы писали:

[cut]
A>Могут быть возвращены LoggerRuRu, LoggerEnUs, LoggerOptimizedForPentium4, LoggerWithSelfTrace и т.п. Естественно, что для того кто использует синглтон это должно быть скрыто. Интерфейсы тут помогают.

Т.е. реализуем фабрику вместе с синглтоном и приписываем её свойства синглтону, не знаю что и сказать
Re[5]: Singleton действительно антипаттерн в enterprize прил
От: Курилка Россия http://kirya.narod.ru/
Дата: 10.08.07 17:26
Оценка:
Здравствуйте, adontz, Вы писали:

A>Здравствуйте, Курилка, Вы писали:


К>>Довольно странное утверждение...

К>>Какой смысл вообще в stateless синглтоне? Чем он отличается от статик-методов?

A>Уже писал: отложенная инициализация + реализация интерфейса.


Инициализация чего? Объект-то у нас stateless.
Про интерфейсы — рядом

К>>Или Instance — property, которая реализует фабрику?


A>Как вариант. Сделать

A>
A>Singleton<IMyService>.Instance;
A>

Что сделать? Ты в ответ на приведённый мной код его продублировал и что ты хотел этим показать?

A>и возвращать разные реализации IMyService в зависимости от того находимся ли мы в режиме тестирования или нет.

A>Учитывая специфику задачи (нам нужен compile-time полиморфизм) я бы предпочёл в 90% случаев ограничиться условной компиляцией.

Понятно — подменяем понятия...
Re[7]: Singleton действительно антипаттерн в enterprize прил
От: Cyberax Марс  
Дата: 10.08.07 17:37
Оценка: 1 (1) +2
Здравствуйте, adontz, Вы писали:

A>С другой стороны в процессе инициализации я могу захотеть открыть файл, сокет, соединение с БД и т.д.

Пардон, это уже не stateless. А самый настоящий stateful.

И получаются все те же проблемы. Например, если ты захочешь, чтобы половина программы у тебя использовала кодировку RU_UTF-8, а другая половина — EN_US, то у тебя будут все проблемы синглтонов.
Sapienti sat!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.