为什么使用单例而不是静态方法?


92

对于有关辅助程序/实用程序类的这些简单问题,我从未找到好的答案:

为什么我要创建一个单例(无状态)而不使用静态方法?

如果对象没有状态,为什么需要一个对象实例?



不要以为,因为我们谈论的是两种不同的语言,所以答案可能会有所不同,但是感谢您提供的链接,在Java中从未听说过“单态”一词
Sebastien Lorber

Answers:


79

通常,单例用于向应用程序引入某种全局状态。(说实话,通常不是很必要,但这是另一个话题。)

但是,在一些极端情况下,甚至无状态单例也可能有用:

  • 您希望在可预见的将来用状态扩展它。
  • 由于某些特定的技术原因,您需要一个对象实例。示例:C#或Java 语句的同步对象。
    locksynchronized
  • 您需要继承,即您希望能够使用相同的接口但使用不同的实现轻松地用另一个替换单例。
    示例:Toolkit.getDefaultToolkit()Java中的方法将返回一个单例,其确切类型取决于系统。
  • 您想要一个哨兵值的引用相等。示例:在C#中。
    DBNull.Value

27
我将采用+1,尽管IMHO单身人士被滥用来介绍全球性的州。单例的目的不是使对象全局可用,而是强制对象仅实例化一次。全局对象是必不可少的邪恶。除非真正需要,否则应尽量不要使用它们,因为它们通常导致与SomeSingleton.getInstance()。someMethod()的高度耦合。:)
back2dos

在只需要一个渲染实例而不是多个渲染实例的游戏中,或者在网络管道类中设置了一次只能有一个连接的安全通道时,此功能很有用。
Tschallacka 2015年

2
在我看来,“轻松替换您的单身人士”部分是最重要的观点。Rest与静态类的实现非常接近。
Teoman shipahi

1
单件对于总和/乘积类型和类似类型有用的非空前哨值。例如,是“选项”类型中的“无/无”值或集合类型的“空”值。您将获得快速的无/空测试作为奖励,因为只需要引用相等即可。
itsbruce

@itsbruce:您的示例并非单例:空列表不是L​​ist类的唯一可能实例,并且None值不是Option类的唯一可能实例。但是,在某些示例中,前哨值单例,因此我可以自由地在答案中添加一个。感谢您的参与!
Heinzi '18

37

我可以看到使用无状态单例而不是静态方法类(即依赖注入)的情况

如果您有直接使用的实用程序函数的帮助程序类,它将创建一个隐藏的依赖项;您无法控制谁可以在哪里使用它。通过无状态单例实例注入相同的帮助程序类,使您可以控制在何处以及如何使用它,并在需要时进行替换/模拟/等等。

使它成为单例实例仅可确保您不会分配任何不必要的类型的对象(因为您只需要一个)。


1
“您无法控制谁可以在哪里使用它。” 为什么有人需要它?
hagrawal

1
@hagrawal用于测试目的,您应该可以对其进行嘲笑
Jemshit Iskenderov 2016年

15

实际上,我找到了这里未提及的另一个答案:静态方法更难测试。

看来大多数测试框架都非常适合模拟实例方法,但是其中许多都不能以一种体面的方式处理静态方法的模拟。


2
但是Powermock似乎可以做到这一点
Sebastien Lorber

6

在大多数编程语言中,类避开了很多类型系统。虽然具有静态方法和变量的类是一个对象,但它通常无法实现接口或扩展其他类。因此,它不能以多态的方式使用,因为它不能是另一种类型的子类型。例如,如果具有IFooable其他类的多个方法签名所必需的interface ,StaticFoo则不能使用class对象代替IFooable,而FooSingleton.getInstance()可以使用(可以FooSingleton实现IFooable)。

请注意,正如我对Heinzi的回答所评论的,单例是控制实例化的模式。它替换new Class()Class.getInstance(),从而为作者提供了Class更多的实例控制权,他可以用来防止创建不必要的实例。单例只是工厂模式的一个非常特殊的情况,应该这样对待。通用使它成为全球注册管理机构的特例,而这种情况常常以失败告终,因为不应仅仅随意地使用全球注册管理机构。

如果您打算提供全局帮助器功能,则静态方法将可以正常工作。该类将不充当类,而仅充当命名空间。我建议您保持较高的凝聚力,否则可能会遇到最奇怪的耦合问题。

greetz
back2dos


4

在使用哪一个之间需要权衡。单例可能具有状态,也可能不具有状态,并且它们引用对象。如果它们不保持状态并且仅用于全局访问,则静态会更好,因为这些方法会更快。但是,如果要利用对象和OOP概念(继承多态性),则单例更好。

考虑一个示例:java.lang.Runtime是java中的单例类。此类允许每个JVM使用不同的实现。每个JVM的实现是单个的。如果此类是静态的,则我们无法基于JVM传递不同的实现。

我发现此链接非常有帮助:http : //javarevisited.blogspot.com/2013/03/difference-between-singleton-pattern-vs-static-class-java.html

希望能帮助到你!!


该答案非常适合在现实世界中包含一个具体示例。
systemovich

2

对我来说“想要对象状态使用Singleton,想要功能使用静态方法”

这取决于您想要什么。每当您想要对象状态(例如,像Null状态而不是Null状态的多态性null,或默认状态)时,单例都是适合您的选择,而静态方法在需要函数时使用(接收输入然后返回输出)。

我建议在单例情况下,实例化后应该始终保持相同状态。它既不应该是可克隆的,也不能接收任何要设置的值(文件中的静态配置(例如Java中的属性文件)除外)。

PS这两个之间的性能以毫秒为单位有所不同,因此请首先关注体系结构


1

Singleton不是无状态的,它拥有全局状态。

我可以考虑使用Singleton的一些原因是:

  • 为了避免内存泄漏
  • 为应用程序中的所有模块(例如数据库连接)提供相同的状态

我知道,但实际上一个单身人士或多或少可以是无状态的……如果它不共享任何类属性……
塞巴斯蒂安·洛伯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.