-1

拿这个有人想要填写的骨架类来获取一系列网站上的 RSS 流:

public class RSSStream extends Thread {

    public RSSStream(String rssStreamName,String rssURL,int refreshTime){
        // constructor code goes here
    }
}

现在,让我们考虑 refreshTime 必须大于零,并且 rssURL 应该是一个有效的 http 地址。

明显的反射是在构造函数内部有一些值检查逻辑。但是,无论发生什么,对构造函数的调用都会实例化一个 Object。这意味着如果值不允许对象完成它的工作,则对象最终将变得无用。这也意味着对象最终应该被转储或重用。

所以,这里有几个关于这个主题的问题:

  • 为什么有些类强加一个 getInstance() 方法以及可能是一个私有构造函数?如果我没记错的话,一个例子是 GregorianCalendar。
  • 在什么情况下你会使用同样的方法?
  • 在大多数情况下,您的构造函数中有检查逻辑吗?
  • 如果是这样,您是否将此应用于域模型的持久性上下文中使用的实体样式类?

欢迎您的所有回答。清楚地了解最常见的做法会很有趣。

4

6 回答 6

3

一些最常见的事情:

  • 如果您使用FactoryMethod来构建复杂对象,您通常会有一个私有构造函数并需要通过工厂进行实例化。这支持与工厂切换施工策略。

  • 如果实例化不能发生,对我来说最清楚的是在构造函数中抛出一个自定义(并希望能提供信息)异常::

    公共类人{

        public Person(Integer id, String name) throws InvalidPersonException
             if (name==null) throw new InvalidPersonException("You cant have a person without a name");
             ...
    
  • 这可能会在实体持久性对象中造成混乱,主要是因为您希望这些对象是无逻辑的,并且因为框架应该为您处理这个 - 例如,如果您在休眠中有一个包含 (id, name) 的 bean 并尝试要持久保存到名称要求为非空的表中,db 抛出的错误或来自您的配置的错误就足够了。

于 2009-12-06T19:26:44.220 回答
2

如果您需要进行的值检查可能会使对象实例处于损坏状态,那么您应该考虑创建一个工厂来生成您的实例。这将允许您在传递的参数无效的情况下引发异常。

但是,如果在验证失败的情况下,您的对象可以在构造函数中使用默认值“固定”,则使用默认值向调用者提供无用但未损坏的对象。

于 2009-12-06T19:26:25.483 回答
2

如果您的构造函数抛出异常,则不会返回对您的对象的引用,因此可以立即对对象进行垃圾回收。所以你不会浪费内存。我认为这是在构建对象时进行验证的正确位置,尤其是不可变对象。

getInstance() 是返回子类实例的工厂方法。当您根据情况返回不同的子类时,您可以使用它。例如,Calendar.getInstance() 在我的语言环境中返回一个 GregorianCalendar,但如果我的语言环境设置不同,它可能会返回不同的实现。

有关创建对象的各种模式,请查看http://en.wikipedia.org/wiki/Creational_pattern

希望这可以帮助

于 2009-12-06T19:30:57.247 回答
2
  • 我主要看到并使用了静态getInstance()和私有构造函数,只是为了使用单例模式。但是,Java 的Calendar抽象类使用此方法不是用于 Singleton,而是用于实例化具有默认时区和语言环境的默认实现(公历)。可能这样做是因为Calendar它是抽象的并且不能被实例化。 GregorianCalendar实际上有公共构造函数。
  • 在大多数情况下,我只是一个无参数的公共构造函数和 setter 方法,并将检查逻辑和异常抛出放在 setter 方法中。我经常使用 Spring 的依赖注入,如果没有设置所有必需的属性,@Required注释可以让容器抛出异常。
  • 我不在实体对象中应用这种逻辑,为了简单起见,我尝试将所有逻辑排除在外,并使它们严格成为 POJO。
于 2009-12-06T19:32:31.743 回答
2

在这种特殊情况下,我将只在构造函数中进行参数验证,并抛出带有描述性错误消息的 IllegalArgumentException。该对象将不会被创建,调用代码将不得不被修复。

您也可以在 getInstance() 方法中执行此操作。使用 getInstance() 的一个原因是,如果实例非常可共享,则可以减少您创建的对象数量。例如,如果您可以拥有多个具有相同 URL 的 RSSStream 对象,则可以装配一个 getInstance() 来共享这些实例。(这假设实例是不可变的。)

还有建造者模式。您将有一个名为 RSSStream.Builder 的嵌套内部类,并说以下内容:

RSSStream rss = new RSSStream.Builder.build(url)
                                     .name("stack overflow")
                                     .refreshTime(2)
                                     .build();

这种模式使您的客户端代码更具自我描述性,并确保您永远不会在无效状态下构建 RSSStream(构建器的方法抛出 IllegalArgumentException)。构建器足够灵活,可以替换所有公共构造器。

我通常使用 IllegalArgumentException,将其从构造函数中抛出,或验证方法(将错误检查代码从多个构造函数中分解出来)。我已经开始将 Builder 方法用于我更重要的公共 API 并且非常喜欢它。

您当然可以在您的领域模型类中使用这些技术。

于 2009-12-06T19:36:36.847 回答
2

IllegalArgumentException如果验证失败,需要验证其参数的构造函数通常会抛出一个。当构造函数抛出异常时,无需担心“中途初始化”对象。

于 2009-12-06T19:50:28.650 回答