原文(Stackoverflow Reviews):http://stackoverflow.com/questions/1020312/are-singletons-really-that-bad/1020384#1020384
单例模式糟透了。它糟糕的地方在于它替你干的两件事情,而每件事情都有大约95%的概率会把事情搞砸(这意味着单例模式平均有99.75%的概率会让你抓狂 ;))
根据《设计模式》的简介,一个单例是一种带有下面两个属性的数据结构:1. 为一个对象提供全局访问
2. 仅允许同一时间存在一个对象
我们普遍认为第一个属性是蛋疼的东西,全局访问太讨人厌了。第二个属性还要更微妙些,但单例模式普遍上并不是一种合理的方法来限制仅有一个对象存在。如果你只需要一个对象实例,那就只实例化一个对象,让单例模式玩蛋去吧。而且“只需要一个对象实例”常常似是而非,比如早晚你总会需要更多的Logger对象,更多的数据库,或者为你每个unit test重新创建资源,这就意味着我们必须得能随心所欲地创建对象。在我们了解它可能产生的后果之前,单例模式就僵化了代码,使代码丧失了灵活性。
单例模式隐藏了依赖关系并增加了耦合度(每个类可能都潜在的依赖一个单例,意味这些类不能被其他工程复用除非他们准备复用全部的单例),而且因为依赖关系是隐形的(例如作为函数的参数),所以我们很难注意到,常常在不经意间就创建了这些单例。单例就像本地变量一样方便,导致了我们热衷于到处使用它们,然后准备移除单例模式时就悲剧了。你不是挂在混乱如麻的代码上,就是挂在意大利面条式盘曲蜿蜒的依赖图上。也许你侥幸坚持下来了,然后你那失控的依赖关系就会导致单例们开始互相调用,形成循环依赖,当你试图实例化其中一个单例时,Oh no。
还有一点,单例模式会把你的unit test搞砸。(你怎么测试一个调用单例的函数?我们不想让单例模式干扰调试,但怎么能做得到?)【注:同感。有时单例会莫名其妙地实例化两次,messing everything up。】
单例模式真特么地糟透了。如果你想要全局访问,那就用全局变量代替单例吧。有时,在极少少少少少数的情况下一个有多个实例的类是错的,而且没有workaround(关于这种情形唯一一个我能想到不怎么实际的例子是用类表示某种硬件时。你只有一个GPU,所以当你要将它映射到代码里时只能有一个实例只能存在)。如果你发现你置身于这种情形时(再强调一遍,这是一个多个实例会导致严重错误的情形,而不是一个你想不到多个实例有什么用的情形),那就启用实例模式,但不要让对象全局可见。
每个属性在罕见的情形下可能都有用,但我真的想不到两个属性会同时有用的情形。不幸的是,很多人认为“单例模式是OOP兼容的全局变量。”它们并不是。单例模式仍然有全局变量的缺点,甚至还带有其他更多完全无关的问题。单例模式真不比普通的全局变量好多少。