一年前曾经非常开心的修改了QQ签名,“酷爱单例模式”!
经典设计模式书的第一讲,这是个如此神奇的模式,比C里的全局变量看起来更有过之而无不及,在任何地方,只要引用了库名称,你就能获得全局访问点,随时修改随时读取,岂不爽哉?
于是,在一段时间内,我把我非常重要的几个实体类都用单例模式实现了,任何地方都可访问,解决了好多大难题!
但,越到后来,越隐隐约约的发现,单例是个笑面杀手!
对程序架构而言,单例意味着没有隐藏,插入到程序的任何组件都可以随时修改它,这客观上违背了面向对象的最小公开法则,程序健壮性安全性骤降。
对程序扩展性而言,单例意味着很难被继承重写! 当你在一个单例中尝试覆盖它的某些功能时,编译器会报错,这时候你哭去吧。或者,你会奇怪的发现,咦?怎么还是基类的功能!
对程序逻辑而言,单例,顾名思义,仅有其一,但你的程序在发展着,你确定只有一个实例即可?某天突然发现,业务变了,需要扩展,那些数不清的用单例调用的代码怎么处理?尤其是已经发布到顾客手中的代码?
对效率而言,每次访问它的时候,都会查看是否被创建(多增加了一次判断),这对于一些需要高性能的程序是非常不合适的。
对多线程而言,在多线程环境下,实现单例是需要技巧的。否则,单例不单!
对对象生命周期而言,单例只有自己持有真正的引用,,如何销毁何时销毁都是问题,可能还会造成指针悬挂。
对开发者而言,一个类不能new! 这还会带来更多的疑问和混淆。
于是,我便站在了这样的十字路口,要么花大力气重写核心代码,要不延续单例模式的老路,再痛苦几年。
痛定思痛, 我决定改掉那个模式。 大概,单例是个真正需求较窄的设计模式,仅仅适合设计配置参数上管理类,或者调试输出类(大家司空见惯的Log),或者是一些全局访问但功能相对单一的功能,千万别尝试将单例包装到复杂的数据实体上,这样做只是饮鸩止渴,哪天回过头来,就像操作系统的注册表一样,又臭又长,带来的危险比带来的好处还多得多!
有一期“程序员”杂志刊登了一篇采访 GoF(设计模式作者)的文章,他们计划对<设计模式>进行修订,其中还特别提到要剔除“单例模式”,认为单例模式很容易在系统中产生“代码的臭味”,期待新版的设计模式早点出来。
共勉。