单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。
Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?
请用“事实、参考资料或具体专业知识”来支持您的回答
单例模式是GoF模式书中一个完全付费的成员,但最近它似乎被开发人员世界孤立了。我仍然使用了相当多的单线程,特别是对于工厂类,虽然你必须对多线程问题(像任何类一样)小心一点,但我不明白为什么它们如此糟糕。
Stack Overflow似乎特别假设每个人都认为Singleton是邪恶的。为什么?
请用“事实、参考资料或具体专业知识”来支持您的回答
当前回答
单线态的问题是范围增加,因此耦合的问题。不可否认,在某些情况下,您确实需要访问单个实例,并且可以通过其他方式实现。
我现在更喜欢围绕控制反转(IoC)容器进行设计,并允许容器控制生命周期。这为依赖于实例的类提供了好处,使它们不知道存在单个实例的事实。将来可以更改单例的生存期。我最近遇到的一个例子是从单线程到多线程的简单调整。
FWIW,如果你尝试单元测试它时它是一个PIA,那么当你尝试调试、修复或增强它时,它就会变成PIA。
其他回答
当几个人(或团队)达成类似或相同的解决方案时,就会出现一种模式。许多人仍然使用原始形式的单件或使用工厂模板(Alexandrescu的《现代C++设计》中有很好的讨论)。并发性和管理对象生存期的困难是主要障碍,前者很容易按照您的建议进行管理。
和所有的选择一样,辛格尔顿也有其沉浮的部分。我认为它们可以适度使用,特别是对于在应用程序寿命期内仍然存在的对象。事实上,它们类似于(而且很可能是)全球性的,这可能引发了纯粹主义者。
它模糊了关注点的分离。
假设您有一个单例,您可以从类中的任何位置调用此实例。您的类不再像它应该的那样纯粹。您的类现在将不再对其成员及其显式接收的成员进行操作。这会造成混乱,因为类的用户不知道该类所需的足够信息是什么。封装的整个思想是向用户隐藏方法的方式,但如果在方法内部使用了单例,则必须知道单例的状态才能正确使用该方法。这是反OOP。
参见Wikipedia Singleton_pattern
一些人还认为它是一种反模式,他们认为它被过度使用,在实际上不需要类的唯一实例的情况下引入了不必要的限制。[1][2][3][4]
参考文献(仅文章中的相关参考文献)
^亚历克斯·米勒。我讨厌的模式#1:辛格尔顿,2007年7月^Scott Densmore。为什么单身汉是邪恶的,2004年5月^史蒂夫·耶格。单身汉被认为愚蠢,2004年9月^J.B.Rainsberger,IBM。2001年7月,明智地使用你的单身
作者的一些对位词:
如果你将来需要让班级不再单身,你就会陷入困境一点也不——我当时使用的是一个数据库连接单例,我想将其转换为一个连接池。请记住,每个单例都是通过标准方法访问的:
我的类别实例
这类似于工厂方法的签名。我所做的只是更新实例方法以返回池中的下一个连接,无需进行其他更改。如果我们没有使用单例的话,那就更难了。
单身汉只是花式的全球明星这一点无可厚非,但所有静态字段和方法也是如此——从类而不是实例访问的任何东西本质上都是全局的,我看不出静态字段的使用有多大阻碍?
并不是说单身汉很好,只是反驳了这里的一些“传统智慧”。
摘自Brian Button:
它们通常被用作全局实例,为什么如此糟糕?因为您将应用程序的依赖项隐藏在代码中,而不是通过接口公开它们。让一些东西全球化以避免传播是一种代码气味。他们违反了单一责任原则:因为他们控制自己的创作和生命周期。它们固有地导致代码紧密耦合。这使得在许多情况下,在测试中伪造它们相当困难。它们在应用程序的整个生命周期中携带状态。另一个对测试的打击,因为您可能会遇到一种需要订购测试的情况,这对于单元测试来说是一个很大的禁忌。为什么?因为每个单元测试都应该彼此独立。