[关闭]
@XingdingCAO 2017-11-25T09:52:28.000000Z 字数 1590 阅读 2328

慎用Singleton

Java constructor


参考:
1. 单例的灾难:http://blog.csdn.net/caowenbin/article/details/38389271
2. Root Cause of Singletons:http://misko.hevery.com/2008/08/25/root-cause-of-singletons/

Singleton这一设计模式出现的时间很早,1995年出版的《设计模式》就将其作为典范的设计模式之一,而且在2003年出版的《Effective Java》中也有讨论。当时的人们通过各种途径掌握了这一设计模式,便开始大量的应用。但是,好景不长,过了一段时间,就有人开始察觉到这一模式带来的弊病。

设计初衷

我们使用单例设计模式的目标就是不管任何时刻,内存中都只能存在一个实例,任何操作都发生在这个实例上;以此来避免重复创建高代价的对象或达成其他的目的。

特点

为了得到单例,我们不再使用构造方法得到实例,而是通过getInstance()的静态方法获得实例的引用。

全局的耦合

我们知道全局耦合,也叫外部耦合,是一种不太好的耦合,我们应当重构代码将外部数据的共享改为参数的传递来减低耦合度。但是,单例的特点就决定了具有状态的单例离不开全局耦合。

如果单例拥有着各种状态,那么它的状态也就是全局的状态,我们在使用这种单例时还必须注意线程安全,加上各种互斥锁以保护状态的一致性。

如果说单例的创建的耗费过大值得同步带来的性能损耗,那么单例给软件设计带来的影响就是灾难性的了。

曾几何时,看到过一个项目的代码,发现里面有大量的单例存在,在新功能的实现时,很多getInstance()的调用,假设这些单例的实现都是正确无误的,当看到大量getInstance()时,还是一个头两个大,耦合度太高,内聚性降低,整体的结构不清晰,到处是错综复杂的依赖,如果整理出类图,应该有剪不断理还乱的箭头线。这就是单例对设计的破坏,也可以认为是用单例代替设计。

上面的吐槽来自于单例的灾难这篇博客。单例的实现十分简单,但是也容易让人放弃思考,只要是能用单例的,统统用上,然后到处getInstance()去解决依赖问题。

不仅国内出现了这种问题,连Google的工程师也未能幸免。上面这篇博客写在2014年,早在2008年,Root Cause of Singletons这篇博客(作者为Google工程师)就警示大家不要滥用单例:

Most developers agree that global state is bad, but they love their Singletons.

Google的工程师都有一定的水平,然而他们也没能避免对单例的滥用,即使再优雅地使用单例,也难逃全局的耦合。

叨叨

以上都是对我这样的新手的警示,我们学会了如何避免代码级别的全局耦合,却又可能栽在对象的全局耦合上。还是因为对软件设计的认识不够深刻,没有把握真正的设计原则。像我这样没怎么历练过的菜鸟,在学会几种设计模式之后就到处“套公式”,得到的软件肯定是不伦不类的;还是需要不断实践,不断思考,方能运用于无形之中。

如何正确地使用单例?

在上面提到了,单例模式的邪恶之处在于其全局的耦合,那么如果单例是不可变的,单例也就失去了全局的状态,也就失去了全局耦合。所以,单例的正确使用方法就是不变类的单例。

但是,我们也应该再三确认是否不变类单例值得被运用:这个类确实只能存在一个实例吗?而且值得这个类失去良好的继承性吗?值得在内存中长时间占用吗?

如果以上问题有一个的回答是否定的,那么你就不应该在使用单例了。

例外

还有一些例子是实践证明还可以接受的可变类单例,如:日志服务。

日志服务是单例的,并且还拥有着不同的状态,但是我们仍然可以接受。因为,日志服务并不会影响软件的信息流动,也就是说不管是否使用日志服务,业务逻辑是不变的。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注