大多数做软件设计的人都学习过设计模式,而看过《设计模式》那本书的人一定对单例模式有印象。在众多的设计模式中,单例模式显得很特别,清晰又简单,容易被人记住,所以使用的也相当多。然而最近在一个C++的新项目中,发现了非常多的地方用了单例模式,几乎到了滥用的地步,带来的不好的地方也显现了出来。本文总结一下单例模式的害处,与大家分享,也提醒一些初学设计模式的朋友:设计模式有限制,用错了场景依然不是好的设计。
害处一,隐式依赖引起的结构不清晰
有的时候,你并不知道要使用的那个类A是单例。当你读类B代码的时候,你可能会先看它的头文件,或VC类视图里的内容,从这里你无法知道B类和A类的关系,因为B类在实现的时候才使用A类的那个所谓“GetInstance”之类的函数,读不到这一行,就不会知道B类对依赖A类的依赖关系。
害处二,多个实例的限制
有人可能会说了,单例就是防止多实例的,这算什么害处呢?确实,但实际情况是,许多被定义成单例的类,并不是非得单例不可。有的人使用单例,只是为了在引用它的时候方便,把单例当个全局变量一样,拿过来就用,从而免去设置实例间引用关系的代码。可是随着项目的发展,麻烦可能会不期而至。比如:需要一个新的A类的实例来支持一个新的功能场景,或者B类需要在A类的实例和另一个类的实例中选择一个来实现功能。
害处三,单例类的扩展限制
单例类没办法通过继承的方式扩展。因为那个GetInstance静态函数没办法生成子类的实例。如果你想给单例类扩展点接口,只有直接修改那个类了。想利用它来生成一个更丰富的子类是不可能的。
害处四,程序结束时的内存泄漏
有些人认为程序都结束了,漏一点内存没问题。但我不这么认为。没有正确析构的实例,漏的可能不只是内存,还有其它资源未释放的危险。我所读到的设计模式那本书上并没有详细描述实例释放的问题,所以我看到很多人写的单例代码也没有实例释放控制这一段,这使内存泄漏成了必然结果。我在以前的博客中讨论过单例的释放方法,叫《单例模式的释放控制》,有兴趣的朋友可以看一下。
以上主要是针对大多数单例模式的实现而言的。这些实现类似书《设计模式》那本书上的描述,保护的构造函数,GetInstance得到实例。其它方法实现的单例不在本文的讨论之列。
对说初学者来说,很难把握住“变化”。也许当初以为只有一个实例,以后会有多个,这就会受到害处二的限制。以我的观点来看,尽量不要用它,我们来有更多更明了的方式来建立两个类的关系。
单例模式是一种限制性模式。如果你并不需要限制,就不要受GetInstance方式获取实例的诱惑,它比全局变量好不了多少。
作者:苏林