【问题标题】:When to use a Constructor and when to use getInstance() method (static factory methods)?何时使用构造函数以及何时使用 getInstance() 方法(静态工厂方法)?
【发布时间】:2010-07-02 22:03:47
【问题描述】:
  1. 我们应该何时以及如何使用构造函数

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

    Foo bar = Foo.getInstance();
    

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

【问题讨论】:

  • 你自己写这门课吗?如果没有,你说什么提供了它?
  • 所以,类本身的实现意味着我正在实现单例模式,对吧?
  • 您是在 调用 getInstance(),还是在编写一个方法 称为getInstance()
  • 如果问题是关于构造函数与静态工厂方法,我建议澄清并更改标题。
  • @zengr:关于你的update2,这可能是因为你没有按照约定命名你的静态方法,它规定它应该命名为Foo.newInstance()Foo.getInstance() 是获取类的单例实例的约定。您应该更正您的示例并改用Foo.newInstance()

标签: java


【解决方案1】:

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

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

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

一个类允许一个 客户端获取自身的实例 就是提供一个公共的构造函数。 还有另一种技术应该 成为每个程序员的一部分 工具包。一个类可以提供一个公共的 静态工厂方法,这是一个简单的静态方法,它返回一个 类的实例。这里有一个简单的 来自Boolean 的示例(盒装 原始类型的原始类 boolean)。这个方法翻译了一个 布尔原始值转换为 Boolean对象引用:

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

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

一个类可以为它的客户提供 静态工厂方法,而不是,或 此外,构造函数。 提供静态工厂方法 而不是公共构造函数有 各有优劣。

优点(引用本书):

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

缺点(还是引用书):

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

【讨论】:

  • 最后一个可以稍微缓解。我倾向于有一个名为 Factory 的静态内部类和其中的各种 newInstance 方法,以便在我创建工厂方法时更清楚。它为我节省了过去打猎的时间。 :)
  • @Qberticus 专用于工厂方法的内部类是个好主意。我试试看,谢谢。
  • 当然,类的构造函数必须至少为protected 以允许子类化,但是客户端代码是否从创建新对象并调用其构造函数而不是调用静态工厂方法获得任何真正的优势?在我看来,链式构造函数调用在语义上是一个实例方法,而序列“创建单元化对象并调用其构造函数”在语义上等价于静态工厂方法调用。
【解决方案2】:

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

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

如果您决定是否创建getInstance() 方法,您需要决定您要完成的工作。您希望人们调用您的构造函数(您正在创建 singletonfactory),或者您不介意(如上面的 NumberFormat,他们在'为了方便调用者正在初始化一些对象)。


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

【讨论】:

    【解决方案3】:

    getInstance 方法的用途:

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

    U1:从另一个类中获取实例

    返回不同类的实例:

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

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

    U2:单例问题

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

    【讨论】:

    • 在工厂模式的情况下,另一个方法名称,如 'createInstance' 或 'buildInstance' 会更合适,但仍然可能是询问者想要的情况(听起来还是有点模糊并喜欢我的一些家庭作业)
    【解决方案4】:

    如果你可以同时使用这两种方法,那么这听起来就像是一个糟糕的实现 singleton pattern

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

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

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

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

    【讨论】:

    • 更新了问题,有点混乱,抱歉
    • -----> "注意不要过度使用单例"
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      单身人士是邪恶的。我看到的围绕它的问题与系统的重用或可扩展性无关(尽管我可以看到这是如何发生的),更多的是我无法计算我在一个系统中看到模糊错误的次数由单例产生的系统。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-04
        • 2010-09-25
        • 1970-01-01
        • 2011-03-17
        相关资源
        最近更新 更多