Is Singleton Really Evil?

GOF23OO设计模式中,在现实项目,尤其是C++项目里,最为常见的当属Singleton

之所以出现这样的现象,是因为它的简单。完全不需要什么OO思想就可以熟练使用。

因此,对于从面向过程程序员转过来的C++程序员,更多的把Singleton当做一个普通C函数的集合,而这个集合往往是一个职责不明的上帝类:名字往往包含:Manager, Controller, Layer等。

正是因为如此,Singleton一直背负着恶名。其中最为人诟病的是它增加了耦合度

Singleton的问题究竟在哪?

Singleton作为一个创建型设计模式,其客户代码一般会这么写:

Foo::getInstance().doSomething();

这种情况下,一个Singleton与普通的类的唯一差别是,其客户会同时依赖两项知识:

  1. 类所提供的服务;
  2. 类是一个单例。

而普通的类,只会造成第一类耦合。

我们知道耦合最大的问题,在于被依赖方的变化会导致依赖方跟着变化。因而,当Foo某天从单实例变为多实例时,就会造成Foo的所有客户代码都会跟着修改。

怎样降低耦合?

既然Singleton仅仅是在第二点处增加了耦合,那么降低其耦合的手段也就呼之欲出:让客户不依赖类是一个单例这个事实

具体做法则是:依赖注入。即让客户的工厂将Singleton实例注入给客户。比如:

struct Client
{
  Client(Foo* foo) : foo(foo) {}
  
  void f() 
  {
     foo->doSomething();
  }
private:
  Foo* foo;
};

struct ClientFactory
{
    static Client* create()
    {
        return new Client(Foo::getInstance());
    }
};

Or DON'T...

这样的做法确实降低了ClientSingleton之间的耦合度。但同时,这也增加了实现的复杂度,另外,也会让每个Client实例都会增加一个指针开销。得失之间,我们需要Think Twice

我们知道耦合所带来的危害:即被依赖方的变化导致依赖方的变化。但我们也同样知道,按照向着稳定的方向依赖原则,如果被依赖方是稳定的,那么客户代码也不会受到变化的影响。

因而,如果一个Singleton从本质上就是单例,从可以设想的范围内,这项知识不会变化。那么系统的耦合度事实上并未因此而增加。比如:

Log::getInstance().trace("blah, blah...");

全局变量

另外,Foo::getInstance()这样的写法,和一个全局变量g_Foo的写法无异。我们都知道全局变量是邪恶的,因为它们会大大增加系统的耦合度。

但是,全局变量必然会增加系统的耦合度吗?

让我们再次回到同样的出发点:只有不稳定的依赖才会真正增加耦合度

而很多全局变量,都是把实现细节,而不是对问题的抽象暴露给其客户。

同时,作为全局变量,整个系统随处均可访问,从而造成大面积的对于实现细节的耦合。一旦这些不稳定的实现细节发生变化,则会导致整个系统大面积的受到影响。这正是全局变量原罪的由来。

而作为一个Singleton,客户对其造成的耦合点:

  1. 类的服务接口;
  2. 类是一个单例。

如果这两点都是稳定的,那么用户对其的依赖并不会造成真正的问题。

在很多现实项目中,正是因为设计素养的缺乏,从而造成Singleton本身是一个职责不明的上帝类,因而其服务接口本身就是不稳定的;同时也违背的缩小依赖范围原则,强迫客户依赖了很多它并不真正需要的接口。这种耦合给客户造成的恶劣影响,要远远大于类是一个Singleton所带来的影响。

另外,一个上帝类自身也是高度不稳定的,这会更加恶化系统的正交度。

而反过来,如果Singleton类本身已经对实现细节做了很好的信息隐藏,同时也做到了单一职责,其API定义又非常合理,那么客户对于类本身接口的依赖就是稳定的。

另外,如果从本质上看,这个类本来就应该只有一个实例,那么客户对于第二点的依赖也是稳定的。

结论

Singleton本身并不必然会增加系统的耦合度。糟糕的类设计,以及类本身并不必然是个单例,这两个不稳定的设计,才会导致系统的耦合度的增加。

因而,我们运用任何一个具体规则时,都要回到更高层,更本质的层面去衡量,这样才能看清每个现象背后的原因,从而有助于我们做好每一个设计决策。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容