滥用单例类的最常见例子
什么时候不应该使用单例类,尽管这样做可能很诱人?如果我们列出了最常见的"单神经炎"实例,我们应该避免这些情况,那就太好了。
解决方案
我知道许多人回答"我们有多个时",等等。
由于原始海报想要列出我们不应该使用Singletons的情况列表(而不是最主要的原因),因此我将介绍一下:
每当我们在使用它时,都是因为不允许使用全局变量!
我有一个初级工程师使用Singleton的次数,因为他们知道我不接受代码审查中的全局变量。当我指出他们所做的全部只是用Singleton模式代替全局变量时,他们似乎常常感到震惊,而他们仍然只有全局变量!
不要将单例用于可能演变为可乘资源的事物。
这听起来可能很愚蠢,但是如果我们声明某项为单例,那么我们将做出非常有力的声明,说它绝对是唯一的。我们正在围绕它构建代码,越来越多。并且当我们在数千行代码之后发现根本不是单例时,我们面前的工作量很大,因为所有其他对象都希望WizBang类的"神圣"对象是单例。 。
典型示例:"此应用程序只有一个数据库连接,因此是单例。"馊主意。我们将来可能希望建立多个连接。最好创建一个数据库连接池,并仅用一个实例填充它。行为类似于Singleton,但是所有其他代码将具有可扩展的代码以访问池。
编辑:我了解理论上我们可以将一个单例扩展到几个对象。但是,没有实际的生命周期(如池/拆池),这意味着没有对已分发对象的真正所有权,即现在的多单身必须是无状态的,才能由不同的方法和线程同时使用。
好吧,单例在大多数情况下只是使事情变得静态。因此,我们实际上是在使数据全局化,并且我们都知道全局变量是错误的,或者我们正在编写静态方法,现在不是很面向对象吗?
这是史蒂夫·耶格(Steve Yegge)更加详细地解释了为什么单例不好的原因。基本上,我们几乎不应该在所有情况下都使用单例,我们真的不知道永远不会在一个以上的地方使用单例。
几年前,我感到很大的愧than(很遗憾,从那时起我就学到了我的经验)。
发生的是,我参加了一个桌面应用程序项目,该项目已从VB6转换为.Net,真是一团糟。诸如40页(打印的)功能之类的东西,没有真正的类结构。我建立了一个类来封装对数据库的访问。还不是真正的数据层,还只是真正的数据层可以使用的基类。在某个地方,我有了一个聪明的主意,使这门课成为单身人士。它可以运行一年左右,然后我们还需要为该应用程序构建一个Web界面。单身最终成为数据库的巨大瓶颈,因为所有Web用户都必须共享相同的连接。再次...吸取的教训。
回顾过去,这实际上可能是一小会儿的正确选择,因为它迫使其他开发人员在使用它时要更加自律,并使他们意识到以前在VB6世界中不是问题的范围问题。但是我应该在几周后将它改回去,然后再围绕它进行过多的构建。
使其成为噩梦的一件事就是它是否包含可修改的全局状态。我参与了一个项目,在该项目中,到处都使用Singleton来处理应该以完全不同的方式解决的问题(传递策略等)。在某些情况下,"去单顿化"是对部分内容的重大改写。系统。我会说,在大多数情况下,当人们使用Singleton时,b / c错了,一开始它看起来不错,但特别是在测试中就变成了问题。
有时,我们假设只有一件事情,那么我们发现是错误的。
示例,一个数据库类。我们假设我们只会连接到应用程序的数据库。
// Its our database! We'll never need another class Database { };
可是等等!你的老板说,连接到其他人的数据库。假设我们要向网站添加phpbb,并想戳其数据库以集成其某些功能。我们应该创建一个新的单例还是另一个数据库实例?大多数人都认为最好使用相同类的新实例,而无需重复代码。
你宁愿有
Database ourDb; Database otherDb;
比复制过去的数据库并使:
// Copy-pasted from our home-grown database. class OtherGuysDatabase { };
这里的滑坡是我们可能会停止考虑制作新的类实例,而开始考虑可以在每个实例中使用一种类型。
当我们在同一个JVM中运行多个应用程序时。
单例是整个JVM中的单例,而不仅仅是单个应用程序。即使多个线程或者应用程序似乎正在创建一个新的单例对象,但如果它们在相同的JVM中运行,它们都将使用相同的对象。
单例实际上总是一个坏主意,并且通常是无用/多余的,因为它们只是对体面模式的非常有限的简化。
查找依赖注入的工作方式。它解决了相同的问题,但是以一种更有用的方式解决了-实际上,我们发现它适用于设计的更多部分。
尽管我们可以在那里找到DI库,但是我们也可以自己滚动一个基本库,这很容易。
我试图只有一个单例来反转控制/服务定位器对象。
IService service = IoC.GetImplementationOf<IService>();
这是我的朋友亚历克斯·米勒(Alex Miller)的一句话:它并没有确切地列举"何时不应该使用单例",而是一篇综合性的优秀文章,并指出,在极少数情况下,即使有,也应该只使用单例。 。
就连接而言(例如),我们不希望使连接本身成为单例,我们可能需要四个连接,或者可能需要多次破坏并重新创建连接,这是有道理的。
但是,为什么不通过单个界面(即连接管理器)访问所有连接?