我反对单例的主要论点基本上是它们结合了两个不良特性。
当然,您提到的内容可能是个问题,但不一定如此。 同步问题可以解决,只有在许多线程频繁访问单例时,它才成为瓶颈,依此类推。 这些问题很烦人,但不会破坏交易。
单身人士最根本的问题是,他们试图做的事情本质上是不好的。
根据GoF的定义,单例具有两个属性:
它是全球可访问的,并且
它防止类被实例化多次。
第一个应该很简单。 一般来说,全球人士是坏的。 如果您不想要全局变量,那么也不要单例。
第二个问题不太明显,但是从根本上讲,它试图解决一个不存在的问题。
您上次不小心实例化一个类是在什么时候打算重用现有实例?
您上次不小心输入“ 2947630597398856856704”是什么时候?
只是没有发生。 因此,我们无需一开始就防止这种情况。
但更重要的是,“只有一个实例必须存在”的直觉几乎总是错误的。我们通常的意思是“我目前只能看到一个实例的用途”。
但是“我只能看到一个实例的用途”与“如果有人敢创建两个实例,应用程序将崩溃”是不同的。
在后一种情况下,单例可能是合理的。 但在前者中,这实际上是一个过早的设计选择。
通常,最终我们确实需要多个实例。
您通常最终需要多个记录器。 有一个日志,您可以将干净,结构化的消息写入该日志,以供客户端监视,还有一个日志,您可以将调试数据转储以供自己使用。
也很容易想象您最终可能会使用多个数据库。
或程序设置。 当然,一次只能启用一组设置。 但是,当它们处于活动状态时,用户可能会进入“选项”对话框并配置第二组设置。 他还没有应用它们,但是一旦他击中“ ok”,就必须交换它们并替换当前活动的集合。 这意味着在他被击中之前,实际上存在两组选择。
更一般而言,单元测试:
单元测试的基本规则之一是它们应独立运行。 每个测试都应从头开始设置环境,运行测试,然后拆除所有内容。 这意味着每个测试都想要创建一个新的单例对象,对它运行测试,然后关闭它。
这显然是不可能的,因为一个单例只会创建一次,而且只会创建一次。 无法删除。 无法创建新实例。
因此,最终,单例的问题不在于诸如“很难确保线程安全性正确”之类的技术性,而是更为根本的“它们实际上并未对您的代码做出任何贡献。它们增加了两个特征,每个特征都是负面的, 到您的代码库。谁会想要的?”