单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。
Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?
请用“事实、参考资料或具体专业知识”来支持您的回答
单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。
Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?
请用“事实、参考资料或具体专业知识”来支持您的回答
当前回答
这并不是说单体本身是坏的,而是GoF设计模式是坏的。唯一真正有效的论点是,GoF设计模型不适合测试,尤其是在并行运行测试的情况下。
只要在代码中应用以下方法,使用类的单个实例就是有效的构造:
确保将用作单例的类实现了一个接口。这允许使用相同的接口实现存根或模拟确保Singleton是线程安全的。这是给的。单例应该是简单的,而不是过于复杂。在应用程序的运行时,如果需要将单例传递给给定对象,请使用构建该对象的类工厂,并让类工厂将单例实例传递给需要它的类。在测试期间,为了确保确定性行为,将单例类创建为单独的实例,作为实际类本身或实现其行为的存根/模拟,并将其原样传递给需要它的类。不要使用在测试期间创建需要单例的被测对象的类因子,因为它将传递其单个全局实例,这违背了目的。
我们已经在我们的解决方案中使用了Singleton,并取得了巨大的成功,这些成功是可测试的,确保了并行测试运行流中的确定性行为。
其他回答
单身者解决了一个(而且只有一个)问题。
资源争夺。
如果你有一些资源
(1) 只能有一个实例,并且
(2) 您需要管理单个实例,
你需要一个单身汉。
例子不多。日志文件是最大的文件。您不想只放弃一个日志文件。您希望正确刷新、同步和关闭它。这是必须管理的单个共享资源的示例。
你很少需要单身汉。他们之所以不好,是因为他们觉得自己是一个全球性的人,而且他们是GoF设计模式书的全额付费成员。
当你认为你需要一个全局,你可能犯了一个可怕的设计错误。
单线态的问题是范围增加,因此耦合的问题。不可否认,在某些情况下,您确实需要访问单个实例,并且可以通过其他方式实现。
我现在更喜欢围绕控制反转(IoC)容器进行设计,并允许容器控制生命周期。这为依赖于实例的类提供了好处,使它们不知道存在单个实例的事实。将来可以更改单例的生存期。我最近遇到的一个例子是从单线程到多线程的简单调整。
FWIW,如果你尝试单元测试它时它是一个PIA,那么当你尝试调试、修复或增强它时,它就会变成PIA。
它模糊了关注点的分离。
假设您有一个单例,您可以从类中的任何位置调用此实例。您的类不再像它应该的那样纯粹。您的类现在将不再对其成员及其显式接收的成员进行操作。这会造成混乱,因为类的用户不知道该类所需的足够信息是什么。封装的整个思想是向用户隐藏方法的方式,但如果在方法内部使用了单例,则必须知道单例的状态才能正确使用该方法。这是反OOP。
还有一件关于单身汉的事,还没有人说过。
在大多数情况下,“singletony”是某个类的实现细节,而不是其接口的特征。控制容器的反转可能会对类用户隐藏此特性;您只需要将类标记为singleton(例如,在Java中使用@singleton注释),就可以了;IoCC将完成剩下的工作。您不需要提供对单例实例的全局访问,因为访问已经由IoCC管理。因此,IoC Singleton没有任何问题。
与IoC Singleton相反的GoF Singleton应该通过getInstance()方法在接口中暴露“singletony”,因此他们会受到上面所说的一切影响。
因为它们基本上是面向对象的全局变量,所以通常可以用这样的方式设计类,这样就不需要它们了。