1

可能重复:
考虑提供静态工厂方法而不是构造函数

这可能是一个有争议的问题,可能不适合这个论坛(所以如果你选择关闭这个问题,我不会受到侮辱)。

鉴于 Java 的当前功能,似乎没有理由制作构造函数public……永远。友好,,privateprotected可以,但public不行。

似乎提供一种public static创建对象的方法几乎总是一个更好的主意。每个 Java Bean 序列化技术(JAXB、Jackson、Spring 等)都可以调用受保护或私有的无参数构造函数。

我的问题是:

  • 我从来没有在任何地方看到过这种做法被法令或写下来?也许布洛赫提到了它,但我不拥有这本书。
  • 除了可能不是我错过的超级干燥之外,还有其他用例吗?

编辑:我解释了为什么静态方法更好。

.1。一方面,您可以获得更好的类型推断。例如参见 Guava 的http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained

.2. 作为该类的设计者,您可以稍后更改使用静态方法返回的内容。

.3. 处理构造函数继承是很痛苦的,特别是如果你必须预先计算一些东西。.4. 更多原因在这里:https ://stackoverflow.com/a/3852556/318174

我应该已经发布了这是用于公共 API 之类的代码。我经常违反各种规则(比如使用直接字段访问)来进行单元测试,方便,因为我很懒。所以当我的意思是ever时,我的意思是你将它释放到野外。

4

4 回答 4

7

如果以下是该方法的外观

public class MyClass {

    private MyClass() { }

    public static MyClass getInstance() {
        return new MyClass();
    }
}

那么宁愿有一个公共的无参数构造函数。

如果我必须调用这样的方法,它会让我觉得它正在做一些事情来构造对象,但实际上并非如此。

我认为拥有一个除了调用构造函数本身之外什么都不做的方法没有任何意义。

于 2012-10-24T16:54:35.303 回答
5

简而言之,静态工厂很酷并且有其用途,但有些 API 陷入了到处使用它们的陷阱,即使它们没有增加价值而只是增加了复杂性。

静态工厂在 Java 中运行良好的一个例子是 EnumSet,它有许多命名的构建器,不能自然地实现为重载的构造器。

例如,即使它们具有相同的论点,它们也不会做同样的事情。

EnumSet.of(E1, E3);
EnumSet.range(E1, E3);

EnumSet 还根据枚举中元素的数量返回两种不同的实现。

    if (universe.length <= 64)
        return new RegularEnumSet<>(elementType, universe);
    else
        return new JumboEnumSet<>(elementType, universe);

不幸的是 EnumMap 并没有做类似的事情,所以只有一个实现。


.1。一方面,您可以获得更好的类型推断。例如参见番石榴的

所以番石榴有类似的方法

List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList();
Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();

在 Java 7 中只是

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>();
Map<KeyType, LongishValueType> map = new LinkedHashMap<>();

这更短,您不需要学习任何新方法,如果您不需要对类型进行双重检查,您可以执行以下操作。

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList();
Map<KeyType, LongishValueType> map = new LinkedHashMap();

在番石榴中,你有

Set<Type> copySet = Sets.newHashSet(elements);
List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");

内置方法在哪里

Set<Type> copySet = new HashSet<String>(elements);
List<String> theseElements = Arrays.asList("alpha", "beta", "gamma");

如果您<String>从 HashSet 中删除,您将失去类型安全性,但鉴于大多数体面的 IDE 会为您自动完成此代码,您实际上不会保存任何输入。

.2. 作为该类的设计者,您可以稍后更改使用静态方法返回的内容。

我会说 YAGNI,实际上很难透明地显着改变实现。您不太可能在无需重新构建或重新测试代码的情况下更换具有完全向后兼容性的替代品。

.3. 处理构造函数继承是很痛苦的,特别是如果你必须预先计算一些东西。

这是真的,但很少见。对于这种情况,我通常有一个用于复杂构造的构建器类,单独的因子方法不能解决问题。


值得考虑的是,Java 库中的大多数类都使用构造函数而不是静态工厂。我能想到的唯一使用构造函数的类,但后来在可能的情况下改为使用静态工厂是自动装箱包装类。知道调用哪个工厂方法的复杂性被语言隐藏了。

鉴于 Java 的当前功能,似乎没有理由将构造函数公开......永远。友好的、私人的、受保护的都可以,但公开的不行。

仅仅因为你可以做某事并不意味着它是个好主意。

例如,您可以将所有的类、方法和变量设置为 1 或 2 个字符长(您永远不需要使用 3 个字母或更长的名称,有些人认为这样做更好),但这并不是一个好主意。

顺便说一句,如果您查看了常见的 UNIX 命令,很多都是两个字符长。;)

似乎为创建对象提供公共静态方法几乎总是一个更好的主意。

除非你更喜欢简单而不是让你的代码变得不必要的复杂。

我从来没有在任何地方看到过这种做法被法令或写下来?

我也没有。可能是因为这不是一个好主意。恕我直言。

除了可能不是我错过的超级干燥之外,还有其他用例吗?

您没有说明这样做的充分理由,这就是不为我这样做的充分理由。;)

于 2012-10-24T16:51:29.873 回答
2

我不这么认为。创建对象需要构造函数。构造函数保证确实创建的对象对于可以返回 null 的工厂方法是不正确的。

构造函数或工厂方法之间的选择取决于具体的用例。有时构造函数更好,有时工厂方法有优势。

我宁愿说在大多数情况下将静态工厂方法放入类本身是不好的做法。恕我直言,类本身及其工厂的分离是优选的解决方案。

我不认为所有框架都支持工厂方法。Spring 和 JAXB 确实支持,但例如HttpServlet,、HttpFilterEJB、Applet 等必须具有公共默认构造函数。即使在 spring 使用具有构造函数的 bean 的情况下,也比需要工厂方法进行实例化的 bean 更方便。

于 2012-10-24T16:54:27.333 回答
0

我可以想到公共构造函数的一个用例。如果您使用注释处理框架,则需要实现一个Processor. 编译器将使用反射来创建处理器的实例。为了使系统正常工作,您的实现类应该是公共的,而不是通用的,并且有一个公共的无参数构造函数。

因此,公共构造函数可能对使用反射的框架很有用。

于 2012-10-24T17:28:27.767 回答