Синглтоны похожи на коммунизм: они оба прекрасно звучат на бумаге, но на практике порождают проблемы.
В одноэлементном шаблоне непропорционально большое внимание уделяется простоте доступа к объектам. Он полностью избегает контекста, требуя, чтобы каждый потребитель использовал объект в области видимости домена приложения, не оставляя никаких вариантов для различных реализаций. Он встраивает знания об инфраструктуре в ваши классы (призыв к GetInstance()
), добавляя ровно нулевую выразительную силу. Это фактически снижает вашу выразительную силу, потому что вы не можете изменить реализацию, используемую одним классом, не изменив ее для всех . Вы просто не можете добавить разовые функции.
Кроме того, когда класс Foo
зависит от Logger.GetInstance()
, Foo
он эффективно скрывает свои зависимости от потребителей. Это означает, что вы не сможете полностью понять Foo
или использовать его с уверенностью, если не прочитаете его источник и не обнаружите тот факт, от которого он зависит Logger
. Если у вас нет исходного кода, это ограничивает ваше понимание и эффективное использование кода, от которого вы зависите.
Шаблон синглтона, реализованный с помощью статических свойств / методов, представляет собой не более чем хакерство по реализации инфраструктуры. Он ограничивает вас множеством способов, не предлагая заметных преимуществ по сравнению с альтернативами. Вы можете использовать его как хотите, но, поскольку есть жизнеспособные альтернативы, которые способствуют лучшему дизайну, это никогда не должно быть рекомендуемой практикой.
Другие очень хорошо объяснили проблему синглтонов в целом. Я просто хотел бы добавить примечание о конкретном случае Logger. Я согласен с вами, что обычно не проблема получить доступ к Регистратору (или корневому регистратору, если быть точным) как синглтону, через статический
getInstance()
илиgetRootLogger()
метод. (если вы не хотите видеть, что регистрируется тестируемым вами классом - но по своему опыту я вряд ли могу вспомнить такие случаи, когда это было необходимо. Опять же, для других это может быть более насущной проблемой).IMO обычно одноэлементный регистратор не вызывает беспокойства, поскольку он не содержит никакого состояния, относящегося к тестируемому классу. То есть состояние регистратора (и его возможные изменения) никак не влияют на состояние тестируемого класса. Так что это не усложняет ваши модульные тесты.
Альтернативой было бы внедрение регистратора через конструктор в (почти) каждый класс в вашем приложении. Для согласованности интерфейсов его следует вводить, даже если рассматриваемый класс в настоящее время ничего не регистрирует - альтернативой может быть то, что когда вы обнаружите в какой-то момент, что теперь вам нужно что-то регистрировать из этого класса, вам понадобится регистратор, таким образом вам нужно добавить параметр конструктора для DI, нарушив весь клиентский код. Мне не нравятся оба этих варианта, и я чувствую, что использование DI для регистрации просто усложнило бы мне жизнь, чтобы соответствовать теоретическому правилу, без какой-либо конкретной выгоды.
Итак, мой итог: класс, который используется (почти) повсеместно, но не содержит состояния, относящегося к вашему приложению, может быть безопасно реализован как Singleton .
источник
В основном, но не только о тестах. Синглтоны были популярны, потому что их было легко потреблять, но у них есть ряд недостатков.
DI дает вам простое использование ваших зависимых классов - просто поместите его в аргументы конструктора, и система предоставит его вам, при этом обеспечивая гибкость тестирования и построения.
источник
Примерно единственный раз, когда вы должны когда-либо использовать Singleton вместо Dependency Injection, - это если Singleton представляет неизменяемое значение, такое как List.Empty или подобное (при условии неизменяемых списков).
Внутренняя проверка для синглтона должна быть такой: «Было бы хорошо, если бы это была глобальная переменная, а не синглтон?» В противном случае вы используете шаблон Singleton, чтобы скрыть глобальную переменную, и вам следует рассмотреть другой подход.
источник
Только что прочитал статью о Monostate - это отличная альтернатива Singleton, но у нее есть некоторые странные свойства:
Разве это не страшно - потому что Mapper действительно зависит от соединения с базой данных для выполнения save () - но если другой сопоставитель был создан ранее - он может пропустить этот шаг при получении его зависимостей. В то время как аккуратно, это также немного беспорядочно, не так ли?
источник
Существуют и другие альтернативы шаблонам Singleton: Proxy и MonoState.
http://www.objectmentor.com/resources/articles/SingletonAndMonostate.pdf
Как можно использовать шаблон прокси для замены синглтона?
источник