56

在两者都可用的语言中,您希望看到实例构造函数还是返回实例的静态方法?

例如,如果您String从 a创建一个char[]

  1. String.FromCharacters(chars);

  2. new String(chars);

4

11 回答 11

65

Effective Java, 2nd edition中,Joshua Bloch 肯定推荐前者。我记得有几个原因,毫无疑问有些我不记得了:

  • 你可以给方法一个有意义的名字。如果您有两种构造实例的方法,这两种方法都采用 int,但对于该 int 具有不同的含义,则使用普通方法会使调用代码更具可读性。
  • 第一个推论 - 您可以使用具有相同参数列表的不同工厂方法
  • 您可以为“潜在的预期失败”情况返回 null,而构造函数将始终返回值或引发异常
  • 您可以返回声明以外的类型(例如返回派生类)
  • 您可以将其用作工厂,以可能多次返回对同一对象的引用

缺点:

  • 目前,它并不惯用 - 开发人员更习惯于看到“新”
  • 如果您看到“新”,您就知道您正在获得一个新实例(以我最近提到的奇怪为模)
  • 您需要为子类提供适当的构造函数
  • 在 C# 3 中,构造函数调用能够使用对象初始化表达式以紧凑的方式设置字段/属性;该功能不适用于静态方法调用
于 2008-10-11T19:48:22.500 回答
25

我在创建实例时编写了一个构造函数,没有副作用,即构造函数唯一要做的事情就是初始化属性。如果创建实例执行您通常不希望构造函数执行的操作,我将编写一个静态方法(并将构造函数设为私有)。

例如:

public class Foo
{
   private Foo() { }

   private static List<Foo> FooList = new List<Foo>();
   public static Foo CreateFoo()
   {
      Foo f = new Foo();
      FooList.Add(f);
      return f;
   }
}

因为我遵守这个约定,如果我看到

Foo f = Foo.CreateFoo();
Bar b = new Bar();

在阅读我的代码时,我对这两行中的每一行所做的事情有着截然不同的期望。该代码并没有告诉我是什么让创建 Foo 与创建 Bar 不同,但它告诉我我需要查看。

于 2008-10-11T20:06:18.533 回答
14

我最近一直在研究一个公共 API,我一直在为静态工厂方法与构造函数的选择而苦恼。静态工厂方法在某些情况下肯定是有意义的,但在其他情况下则不是很清楚,我不确定与 API 其余部分的一致性是否足以将它们包含在构造函数中。

无论如何,我在Bill-Venners 对 Josh Bloch 的采访中看到了一句我觉得很有帮助的名言:

当你在写一个类时,你可以在我的书中列出静态工厂相对于公共构造函数的优势。如果您发现这些优势中的大部分实际上适用于您的情况,那么您应该使用静态工厂。否则,您应该使用构造函数。

有些人对在我的书中找到这个建议感到失望。他们读到它并说:“你强烈支持公共静态工厂,我们应该默认使用它们。” 我认为这样做的唯一真正缺点是,对于习惯于使用构造函数来创建对象的人来说,这有点令人不安。而且我认为它在程序中提供的视觉提示要少一些。(您看不到 new 关键字。)此外,在文档中找到静态工厂有点困难,因为 Javadoc 将所有构造函数组合在一起。但是我想说大家应该一直考虑静态工厂,在合适的时候使用。

阅读了那句话,以及Uri 提到的研究* 后,我感觉倾向于错误地支持构造函数,除非有令人信服的理由不这样做。我认为没有正当理由的静态工厂方法可能只是不必要的复杂性和过度工程。虽然明天我可能会再次改变主意......

*不幸的是,这项研究较少关注静态工厂方法,而更多地关注工厂模式(存在一个单独的工厂对象来创建新实例),所以我不确定是否真的可以得出静态工厂方法让许多程序员感到困惑的结论。尽管这项研究确实给我的印象是他们经常会这样做。

于 2011-06-15T10:59:56.810 回答
10

如果您的对象是不可变的,您也许可以使用静态方法返回缓存的对象并为自己节省内存分配和处理。

于 2008-10-11T20:05:56.473 回答
8

ICSE'07 有一篇论文研究了构造函数与工厂模式的可用性。虽然我更喜欢工厂模式,但研究表明开发人员在找到正确的工厂方法方面速度较慢。

http://www.cs.cmu.edu/~NatProg/papers/Ellis2007FactoryUsability.pdf

于 2008-10-11T19:54:25.420 回答
4

这取决于。对于使用实例构造函数是“正常”的语言,我通常会使用一个,除非我有充分的理由不这样做。这遵循最小意外原则。

顺便说一句,您忘记了另一个常见情况:null/default 构造函数与初始化方法配对。

于 2008-10-11T20:08:08.293 回答
3

静态方法。然后你可以返回一个null,而不是抛出一个异常(除非是引用类型)

于 2008-10-11T19:44:48.607 回答
3

我更喜欢实例构造函数,只是因为这对我来说更有意义,并且你试图表达的内容的潜在歧义更少(即:如果 FromCharacters 是一个采用单个字符的方法怎么办)。不过,当然是主观的。

于 2008-10-11T19:46:01.630 回答
3

我个人更喜欢看普通的构造函数,因为构造函数应该是用来构造的。但是,如果有充分的理由使用它,即如果 FromCharacters 明确声明它没有分配新内存,那将是值得的。调用中的“新”是有意义的。

于 2008-10-11T19:48:30.830 回答
2

正如 Jon Skeet 解释 Josh Bloch 所说,在许多情况下静态工厂方法比构造函数更可取的原因有很多。我会说,如果该类是一个简单的类,没有昂贵的设置或复杂的使用,请使用惯用的构造函数。现代 JVM 使对象创建极其快速和廉价。如果该类可能是子类,或者您能够使其不可变(并发编程的一大优势,这只会变得更加重要),然后使用工厂方法。

还有一个提示。不要命名工厂方法Foo.new*Foo.create*. 具有这些名称的方法应始终返回一个新实例,这样做会错过工厂方法的一大优势。更好的命名约定是Foo.of*or Foo.for*Google Guava 库(以前的Google Collections Library)在这方面做得很好,恕我直言。

于 2008-10-12T05:03:01.117 回答
1

当然,静态工厂方法相对于构造函数有几个优点。

  1. 工厂方法帮助我们编写灵活的代码。而构造函数使其紧密耦合。
  2. 静态工厂方法提高了可读性,因为它们允许您创建不同的实例。
  3. 您可以返回一个缓存的对象。例如getInstance()在单例中。
于 2019-01-28T17:31:55.050 回答