类内部或外部的值检查逻辑?

时间:2009-12-06 19:13:47

标签: java constructor instantiation

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

public class RSSStream extends Thread {

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

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

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

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

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

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

6 个答案:

答案 0 :(得分:3)

一些最常见的事情:

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

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

    公共类人员{

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

答案 1 :(得分:2)

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

但是,如果在验证失败的情况下,您的对象可以使用默认值在构造函数中“修复”,那么使用默认值为调用者提供一个无用但没有损坏的对象。

答案 2 :(得分:2)

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

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

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

希望这有帮助

答案 3 :(得分:2)

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

答案 4 :(得分:2)

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

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

还有Builder模式。你有一个名为RSSStream.Builder的嵌套内部类,可以说:

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

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

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

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

答案 5 :(得分:2)

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