何时使用构造函数以及何时使用getInstance()方法(静态工厂方法)?


84
  1. 何时以及如何使用构造函数

    Foo bar = new Foo();
    
  2. 以及何时以及如何使用getInstance()(静态工厂方法)

    Foo bar = Foo.getInstance();
    

两者有什么区别?我一直使用构造函数,但是什么时候应该使用getInstance()呢?


您是在自己上课吗?如果没有,您在打电话给它提供什么?
克里斯·B

所以,类本身的实现意味着我正在实现单例模式,对吗?
zengr

您在打电话 getInstance(),还是在写一个叫做的方法getInstance()
克里斯·B

1
如果问题是关于构造函数还是静态工厂方法,我建议您进行澄清并更改标题。
Pascal Thivent

@zengr:关于您的update2,这可能是因为您没有按照约定命名静态方法,该方法要求应将其命名为Foo.newInstance()Foo.getInstance()是用于获取类的单例实例的约定。您应该更正示例并Foo.newInstance()改为使用。
JRL

Answers:


96

每个人似乎都专注于单例,而我认为问题实际上是关于构造函数与静态工厂方法

这实际上是项目1:考虑静态工厂方法,而不是Joshua Bloch的 Effective Java的构造函数

项目1:考虑静态工厂方法而不是构造函数

类允许客户端获取其自身实例的通常方法是提供一个公共构造函数。每个程序员的工具箱中都应该包含另一种技术。一个类可以提供一个公共的 静态工厂方法,该方法只是返回类实例的静态方法。这是来自的一个简单示例Boolean(原始类型的装箱原始类型 boolean)。此方法将布尔基本值转换为 Boolean对象引用:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

请注意,静态工厂方法 与“设计模式” [Gamma95,p。1]中的工厂方法模式不同。107]。此项中描述的静态工厂方法在设计模式中没有直接等效的方法

一个类可以向其客户端提供静态工厂方法,而不是构造方法或为构造方法提供补充。提供静态工厂方法而不是公共构造方法具有优点和缺点。

优点(引用书):

  • 静态工厂方法的一个优点是,与构造函数不同,它们具有名称。
  • 静态工厂方法的第二个优点是,与构造函数不同,不需要每次调用它们时都创建一个新对象。
  • 静态工厂方法的第三个优点是,与构造函数不同,它们可以返回其返回类型的任何子类型的对象。
  • 静态工厂方法的第四个优点是它们减少了创建参数化类型实例的冗长性。

缺点(仍在引用这本书):

  • 仅提供静态工厂方法的主要缺点是没有公共或受保护的构造函数的类不能被子类化。
  • 静态工厂方法的第二个缺点是它们不易与其他静态方法区分开。

8
最后一个可以有所缓解。我倾向于使用一个名为Factory的静态内部类和各种newInstance方法,以便在创建工厂方法时更加清楚。它使我免于过去的狩猎时间。:)
Rich Schuler 2010年

@Qberticus专门用于工厂方法的内部类是一个好主意。我会尝试的,谢谢。
Dave O.

当然,类的构造函数至少protected必须允许子类化,但是与编写静态对象方法相比,客户端代码是否可以通过创建新对象并调用其构造函数获得任何真正的好处?在我看来,链式构造函数调用在语义上是一个实例方法,而序列“创建统一的对象并调用其构造函数”在语义上等效于静态工厂方法调用。
2014年

9

您有两个问题:什么时候应该调用一个getInstance()方法,什么时候应该创建一个?

如果你决定是否要调用一个getInstance()方法,这很容易。您只需要阅读类文档以了解何时调用它。例如,NumberFormat提供了一个构造函数一个getInstance()方法;该getInstance()方法将使您本地化NumberFormatCalendar另一方面,对于,构造函数受到保护。您必须打电话getInstance()给一个。

如果你决定是否要创建一个getInstance()方法,你需要决定你想要完成的任务。您要么希望人们调用您的构造函数(您正在创建单例工厂),要么您不介意(如上NumberFormat所示,为了方便调用者,他们正在初始化一些对象)。


长话短说?不必担心getInstance()在自己的代码中创建方法。如果它们有用的时候到了,您会知道的。通常,如果您可以调用类的构造函数,则即使该类提供了一个getInstance()方法,也可能应该这样做。


7

getInstance方法的用途:

但是大多数时候,您的对象将是一个简单的POJO,使用公共构造函数是最实际,最明显的解决方案。

U1:另一个类的getInstance

要返回其他类的实例:

public class FooFactory {
    public static Foo getInstance() {
        return new Foo();
    }
}

NumberFormat.getInstance方法会这样做,因为它们实际上返回的实例DecimalFormat

U2:单例问题

单例模式限制了面向对象编程的许多好处。单例通常具有私有构造函数,因此您不能扩展它们。由于您将通过其getInstance方法访问它并且未引用任何接口,因此您将无法将其换出另一个实现。


在工厂模式的情况下,另一个方法名称(例如'createInstance'或'buildInstance')会更合适,但仍然可能是问询者想要的东西(声音仍然有点小题大作,对我来说像是一些家庭作业)
jdehaan 2010年

6

如果可以同时使用这两种方法,则听起来像是实现不佳的单例模式

如果您打算在系统中仅拥有该类的单个实例,然后将构造函数设为私有,请使用第二个选项。

使用第一个允许构建该类的多个对象。

但不要给您的班级两种可能性。

注意不要过度使用单例,只有在系统中仅存在一个实例的情况下才使用它们,否则将限制在其他项目中重用类的可能性。能够从项目中的任何地方调用getInstance听起来很有趣,但这并不清楚谁真正拥有该实例:没人和/或所有人。如果您在一个项目中有很多单例,您可以打赌该系统的设计不佳(通常)。应该谨慎使用单例,并且应使用与全局变量相同的建议。


更新了问题,有点困惑,对不起
zengr

4
------>“注意不要过度使用单身人士” <------
Bart van Heukelom 2010年

1

我总是喜欢静态工厂而不是常规构造函数的一种情况是,当我知道对象构造会变慢时。我对构造函数进行了简单的初始化,但是如果需要创建一些繁重的东西,我将使用静态方法并记录其行为。


0

单身人士是邪恶的。我所看到的围绕它的问题与系统的重用性或可扩展性无关(尽管我可以看到这种情况是如何发生的),而更多的是,这样我就无法数出我在一个系统中看到晦涩的错误的次数。由单例产生的系统。

如果确实需要使用单例,请确保其范围非常狭窄,即明智地限制系统中知道它的其他对象的数量。

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.