深一层看Singleton

本文深入探讨Singleton模式,分析其在保证唯一实例与提供全局访问点之间的耦合问题,提出依赖注入与多态的重要性,对比Singleton与MonoState模式,强调设计模式的正确应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Singleton模式通常被认为是比较容易理解和运用的设计模式。目前,网上已经有相当多的资料讲解Singleton的基础知识,本文试图避免重复性的介绍,而是尝试从不同的角度更深入地探讨Singleton。

“保证对象有唯一的实例,并且提供一个全局访问点”是Singleton模式比较常见的描述。不知您是否意识到,这个定义本身就散发着某种"bad smell"。为什么要把“保证对象有唯一实例”的创建职责,和“提供一个全局访问点”的访问职责混入一个模式呢?

为了更清楚地说明这个问题,我们假设有一个Singleton的类A,并通过静态方法A.Instance()提供唯一实例。使用者类B通常会直接以A.Intance().Do()的方式来使用它(因为这样很方便)。这里我们需要追问的是B的设计,“B一定要A的单例对象才行吗?不是单例可以吗?”。其实,绝大多数情况B所需要的仅仅是A的对象,它甚至根本不需要知道“单例”为何物。但不幸的是以方便的理由,“单例”成功地混入了B,这就造成B对A的“单例耦合”。"单例耦合"直接损害了B的可测试性,因为我们无法对类A进行mock,只能带着A测试B,扩大了测试边界。一般来讲,具有好的可测试性的模块,通常表现为依赖注入的开放式设计,因为这样才方便通过mock等方式模拟外部依赖,让测试专注于模块本身的逻辑,即测试边界只包含被测模块本身。

通过上面的分析,问题已经很清楚了,B是A的使用者,它只关注A提供的功能接口;A只能有唯一实例是A的创建者的事情,与B无关。所以,我们提倡B应该采用开放式的依赖注入,比如通过构造函数或者通过Setter让外部注入A的实例,不要仅仅因为方便而滥用Singleton。对于创建唯一实例的职责可以采用传统Singleton模式,也可以采用其它方式。从理论上讲,创建A的职责不一定要放在A内部来实现,我们完全可以结合工厂模式,设计所谓的单例工厂,保证从这个工厂出品的对象是唯一实例。

其实,某些情况下,我们的真实需求并非“保证类有唯一对象”而是“保证类对象有唯一的状态”。这里涉及两个问题:

第一“既然是类,又是唯一,为什么不干脆弄成static算了,还要Singleton模式干嘛?”。其实,这是为了避免使用者B对类A的直接依赖,采用static就无法把B设计成依赖注入式的,采用static意味着失去了多态。这里的多态特别指对Singleton类本身有接口要求,那么static设计就直接被排除在外了。

第二“既然用户不关心是一个对象还是多个对象,是否可能类有多个对象,但都共享同一状态,用这种方式实现状态唯一性需求?”。答案是肯定的,MonoState模式正是采用这种方式,把唯一状态通过static成员封装在类内部,让所有对象共享同一状态。与Singleton相比,MonoState更好地分离了状态唯一性和对象使用,避免出现耦合。

更多关于Singleton模式的探讨,可以参考:

1. Patterns I Hate #1: Singleton

2. Why Singletons are Evil

转载于:https://www.cnblogs.com/weidagang2046/archive/2009/10/31/1593473.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值