在Java中,似乎公共构造师始终是一种不良的编码实践



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

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

鉴于Java的当前功能似乎没有理由使构造函数public ... Ever 。友好,privateprotected还可以,但是public否。

似乎几乎总是一个更好的主意来提供一种public static来创建对象的方法。每种Java Bean序列化技术(Jaxb,Jackson,Spring等)都可以称呼受保护或私人的No-Arg构造函数。

我的问题是:

  • 我从未见过这种做法在任何地方都命令或写下来?也许布洛赫(Bloch)提到了这一点,但我不拥有书。
  • 除了我错过的超级干燥外,还有其他用例吗?

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

.1。对于一个人,您会得到更好的类型推理。例如,请参见Guava的http://code.google.com/p/guava-libraries/wiki/collectionutilitiesexplained

.2。作为班级的设计师,您以后可以通过静态方法更改返回的内容。

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

我应该张贴这是针对公共API的代码。我经常违反各种规则(例如使用直接访问)进行单元测试,便利性和原因,因为我很懒惰。因此,当我的意思是 ever 时,我的意思是您将其释放到野外。

如果以下是该方法看起来

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

然后宁愿拥有一个公共no-arg构造函数。

如果我必须调用这样的方法,它会让我觉得它在做一些构建对象的事情,但实际上不是。

我认为拥有一种无需做任何事情的方法除了调用构造函数本身没有任何意义。

在简短的静态工厂很酷,它们有其用途,但是有些API落入了陷阱的陷阱,即使它们不增加价值并增加了复杂性。

静态工厂在Java中运行良好的一个示例是ENUMSET,它的许多命名构建器不能自然地作为超载构造函数实现。

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

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

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

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

不幸的是,Enummap并非类似,因此只有一个实现。


.1。对于一个人,您会得到更好的类型推理。例如,请参见Guava的

因此,番石榴具有

之类的方法
List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList();
Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();

Java 7中的哪个只是

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

较短的是,您不需要学习任何新方法,而Java 6您不需要对类型进行双重检查,可以执行以下操作。

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");

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

.2。作为班级的设计师,您以后可以通过静态方法更改返回的内容。

我会说Yagni,在实践中很难透明地改变实施。您不太可能在不必重建或重新测试代码的情况下以完全向后兼容的替换。

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

这是真的,但很少见。在这种情况下,我通常有一个建造者课程用于复杂的结构,仅一个因素方法无法解决问题。


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

鉴于Java当前的功能似乎没有理由将构造函数公开。友好,私人,受保护是可以的,但公共

仅仅因为您可以做某事并不是一个好主意。

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

顺便说一句,如果您查看了通用的Unix命令,则许多是两个字符。;)

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

除非您更喜欢简单性并且不必使您的代码不必要地复杂。

我从未见过这种做法在任何地方都命令或写下来?

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

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

您没有说一个充分的理由这样做,这是足够的理由不为我做。;)

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

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

我宁愿说,在大多数情况下,将静态工厂方法纳入班级本身是不好的做法。恕我直言的班级本身及其工厂是可取的解决方案。

我认为所有框架都不支持工厂方法。Spring和JAXB确实支持,但是例如HttpServletHttpFilter,EJB,Applet等必须具有公共默认构造函数。即使使用具有构造函数的豆子的弹簧使用比需要工厂方法的豆类更方便。用于公共建筑商。如果使用注释处理框架,则需要实现Processor。编译器将使用反射来创建处理器的实例。为了使系统工作,您的实施类应该是公开的,而不是通用,并拥有公共no-args构造函数。

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

最新更新