单例  vs 单一实例

单例 vs 单一实例

原文:Singleton vs. single instance


欢迎大家来到 Monologue,今天我们讨论一个同程序设计相关的话题,其不仅适用于 iOS ,更适用于所有的程序开发。

虽然我并非是是程序设计方面的专家,但在个人看来,许多 app 项目对单例 / 单一实例的使用存在混淆不清的地方,更可怕的是,开发者们似乎还没有意识到。

因此,我想在这里同大家分享如何避免这样的设计缺陷。如前所述,我并非程序设计方面的专家,所以无法保证在这篇文章中所陈述的观点100%正确,欢迎大家批评指正。我更倾向于让将篇文章起到抛砖引玉的作用,引发讨论,从而解决当前的问题。所以,欢迎评论。


什么是“单例 vs 单一实例”

闲话少说,直入正题:什么是“单例 vs 单一实例”

追根溯源

我已记不得谁是第一个如此描述这个问题的人,但是我依然记得是从哪里读到这个术语的。那是一本名叫《玩转老旧代码》的书(Working effectively with legacy code by Michael Feathers)。如果你不知道这它,建议去读读。其中包含了许多有用的技巧,即使你认为自己从不需要和老旧代码打交道,依然可以从其中受益良多。

定义

“单例 vs 单一实例”表示一个很简单的概念:当你使用单例的时候,先问问自己是否可以改用单一实例。

按照单例模式设计的类(以下简称单例类)在整个 app 中有且只有一个实例对象,通常,在程序的任意地方都可以访问它。

相反,“单一实例”意味着类本身并非按照单例模式进行设计,但在使用时,我们每次只使用一个实例对象。

乍看上去,好像没什么大不了的,我们甚至会觉得单例更棒更好用。其实不然,且听我慢慢道来:

假象

封装性

不管是单例还是单一实例,我们都只使用一个实例对象。但是前者通过设计模式贯彻这一原则,后者仅仅依靠使用者主观遵守这个原则。很明显,在这种情况下,最好能够对使用者进行强制约束,所以,就封装性来看,单例胜出。

易用性

开发者都是懒人(也应该是),喜欢简单的接口。从这点上来讲,单例无人能及。只需要引入头文件(Swift 不用),调用返回单例对象的类方法,就 OK 了。够简单吧?如果使用单一实例,我们首先必须搞清楚谁拥有这个实例对象 & 如何能够获取到它。

不过,好用并不总是好事。说到这里,希望大家能够有所警觉,我们继续往下看。

进阶

测试驱动开发

同许多《玩转老旧代码》探讨的主题一样,这个主题也提到了测试驱动开发(以下简称 TDD)。即使你反对测试驱动开发,也不着急关闭页面。

TDD 的关键在于各项测试独立进行,程序环境在每次测试之间都会重置。此时,单例会造成麻烦:整个 app 的生命周期中有且仅有一个实例对象存在,我们无法保证这个对象是否还残留有前一个测试的状态信息(另外还需注意,许多 IDE 可以同时运行多个单元测试,它们之间的顺序无法保证)。这个问题是可以解决的,但总的来说,针对 TDD,“单例 vs 单一实例”之间较量为 0:1。

限制访问

同“易用性”相反,有时,我们必须限制对于某些对象的访问。

嗯,全局访问有着天生的缺陷。如果在整个程序的任何地方都可以访问一个对象,那么一旦出现问题,就很难知道是谁进行了误操作。试想一下找出一个被30个不同对象访问的单例对象除了问题,这种 debug 极为麻烦。

另一个问题就是越好用的东西就越会被频繁使用。这就会造成上一段文字讨论的局面,所有对象都肆意的调用单例。

并不是说绝对不可以使用单例,从功能上来说这种方式没问题。但是它很容易被滥用(事实也是如此),例如下面的例子:


例子

就我个人而言,单例模式及其好用,但必须意识到我们正在滥用它。如果你选择使用它,请三思:是不是只能有一个实例对象;如果有两个同时并存,就会破坏程序的结构?换句话来说,是不是一个对象就够了?

最常见的滥用单例的典型:当我们需要一个全局变量时。许多现代编程语言都强调尽量避免使用全局变量,但在有时我们不得不用。我们创建一个单例对象,因为在哪里都可以引用它(同全局变量),试着回答上面的问题:

是不是只能有一个实例对象,或者说是不是一个对象就足够了?

当然不是!

当然,上面只是举了一个很基础的例子。想要触及真正的问题, 就必须进一步深入挖掘。

以 MVC 架构中的控制器为例,它是处理业务逻辑的地方。我见过许多项目都在它们的业务逻辑中频繁使用单例:CommunicatinManager,DataManager,NotificationMananger,LoginMananger等等。它们都不约而同的使用了单例,但问题是:有必要吗?

拿 LoginMananger 来说,这个对象负责管理用户登陆周期,其包含 token / cookie / credential 等信息。

大多数 app 一次只允许一个用户登陆。所以,我们只一次只需要一个 LoginManager 实例对象。乍一看来,单例完美无缺。但回到上面的问题:

是不是只能有一个实例对象,或者说是不是一个对象就足够了?

是的,没错。如果同时出现两个 LoginManager 那就有问题了。等等,不觉得有点奇怪吗?考虑下面的情况:

  • 登入
  • 登出
  • 再次登陆,但使用不同的证书

啊!虽然整个程序只需要一个 LoginMananger 实例对象,但是这个其在程序运行的期间发生了变化。所以,上述问题可以修正为:

整个 app 的生命周期中,是不是只能有一个一成不变的实例对象?

对于单例模式来说, LoginManager 对象在不同用户登陆周期之间持续存在。因此,用户登出时,这个对象必须清除其所保存的用户信息。貌似简单,用户的登陆状态是通过若干项信息表示的-token,用户名等。可以其他用户相关数据,诸如缓存的好友列表,头像,密码呢?这些信息是很难维护的。你不能指望你的同事(甚至你自己)记得在登出时清除数据。

某次,如果你忘记清理用户 token,会发生什么?用户可能会以错误的身份能登陆!

如果 LoginMananger 对象不是一个单例,我们只需在用户登出时删除这个对象即可。完全不用担心自己忘记清理数据。

同现实生活类似,在软件中没有什么是永恒的。所以别舍不得释放你的对象,否则难过的是你😢。

好吧,今天的关于“单例 vs 单一实例”的讨论就到这里,感谢阅读🙏!

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,601评论 18 139
  • 单例模式(SingletonPattern)一般被认为是最简单、最易理解的设计模式,也因为它的简洁易懂,是项目中最...
    成热了阅读 4,227评论 4 34
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,522评论 25 707
  • 最近微信朋友圈偶然发现一些朋友默默对我设置了朋友圈对其不可见,屏幕上只留下一道黑线....插在了我幼小心灵里,又要...
    黑姆阅读 244评论 0 0
  • 有了自己的环境之后,你需要去学习Salesforce的基本知识,概念,操作方法,配置等。 我们假定你从零开始学习S...
    爱写作的项目经理阅读 3,939评论 2 5