【问题标题】:Try / Catch in Constructor - Recommended Practice?Try / Catch in Constructor - 推荐做法?
【发布时间】:2013-05-23 15:53:15
【问题描述】:

我一直很好奇的事情

public class FileDataValidator {

private String[] lineData;

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        e.printStackTrace();
    }

}

//validation methods below all throwing InvalidFormatException

不建议在我的构造函数中包含 try/catch 块吗? 我知道我可以让构造函数将异常抛出给调用者。你们在调用我在构造函数中所做的方法时更喜欢什么?在调用类中,您更愿意创建 FileDataValidator 的实例并在该实例上调用方法吗?只是想听听一些反馈!

【问题讨论】:

  • 更令人担忧的是,在 API 的情况下,您将使用 e 做什么,printStackTrace 闻起来很有趣。当然,您应该让代码的用户遇到异常,以便他们可以做点什么吗?这就是例外的原因。
  • 为什么不将validateXXX 操作更改为返回布尔值,然后将名为valid 的变量设置为如果所有三个validateXXX 调用都有效。然后使用 isValid 方法公开该变量
  • 使用Command Pattern 做一些事情可能会有所帮助;也就是说,实例化一个您将多次调用的方法,传入数据进行验证,然后让该方法抛出异常。

标签: java oop exception try-catch ooad


【解决方案1】:

在您显示的代码中,验证问题不会反馈到创建此对象实例的代码。这可能不是一件好事。

变体 1:

如果您在方法/构造函数中捕获异常,请务必将某些内容传回给调用者。如果一切正常,您可以放置​​一个字段isValid,将其设置为true。看起来像这样:

private boolean isValid = false;

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
        isValid = true;
    }
    catch(InvalidFormatException e)
    {
        isValid = false;
    }
}

public boolean isValid() {
    return isValid;
}

变体 2:

或者您可以让异常或其他一些异常传播给调用者。我已将其显示为未经检查的异常,但根据您的异常处理宗教做任何工作:

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}

变体 3:

我要提到的第三种方法有这样的代码。在调用代码中,您必须调用构造函数,然后调用 build() 函数,该函数可以工作也可以不工作。

String[] lineData = readLineData();
FileDataValidator onePerson = new FileDataValidator();
try {
    onePerson.build(lineData);
} catch (InvalidDataException e) {
    // What to do it its bad?
}

这是课程代码:

public FileDataValidator() {
    // maybe you need some code in here, maybe not
}

public void build(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}

当然,build() 函数可以使用 isValid() 方法,您可以调用该方法来查看它是否正确,但对我来说,异常似乎是构建函数的正确方法。

变体 4:

我要提的第四种方法是我最喜欢的。它有这样的代码。在调用代码中,您必须调用构造函数,然后调用 build() 函数,该函数可以工作也可以不工作。

这种方式遵循 JaxB 和 JaxRS 的工作方式,这与您所拥有的情况类似。

  1. 外部数据源 - 您有一个文件,它们有一个 XML 或 JSON 格式的传入消息。
  2. 构建对象的代码 - 您有自己的代码,它们的代码库根据各种 JSR 中的规范工作。
  3. 验证与对象的构建无关。

调用代码:

String[] lineData = readLineData();
Person onePerson = new Person();
FileDataUtilities util = new FileDataUtilities();
try {
    util.build(onePerson, lineData);
    util.validate(onePerson);
} catch (InvalidDataException e) {
    // What to do it its bad?
}

这是数据所在的类代码:

public class Person {
    private Name name;
    private Age age;
    private Town town;
... lots more stuff here ...
}

以及用于构建和验证的实用程序代码:

public FileDataValidator() {
    // maybe you need some code in here, maybe not
}

public void build(Person person, String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();
    setNameFromData(person);
    setAgeFromData(person);
    setTownFromData(person);
}

public boolean validate(Person person) {

    try
    {
        validateName(person);
        validateAge(person);
        validateTown(person);
        return true;
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}

【讨论】:

  • 你的第一个类型被破坏了(方法构造函数中?),第二个只是冗余练习。
  • @GrantThomas 我很想听听更多。我认为在构造函数中调用方法没有问题,如果它们有用的话。也许你可以解释一下。 OP 必须决定什么是有用的。其次,您是说catch-rethrow 是多余的吗?
  • @LeeMeador 感谢您花这么多时间写出几个想法!他们提供了很多思考的食物。我想我会选择变体 3。从构造函数中移出验证调用并将异常传播给调用者在能够重用单个对象方面更加友好,并且调用类更适合知道什么如果传递了无效数据,应该会发生。
  • @LeeMeador 我也认为你的第一个例子是坏的。您确实是从构造函数中调用方法,这在语法上是可以的,但是您还在构造函数中声明了一个方法isValid()。这是一个大括号问题。
  • @Timmos 已修复!谢谢。
【解决方案2】:

您应该考虑静态工厂模式。将您的全参数构造函数设为私有。提供一个静态 FileDataValidator(args...) 方法。这接受并验证所有参数。如果一切正常,它可以调用私有构造函数并返回新创建的对象。如果有任何失败,抛出一个异常通知调用者它提供了错误的值。

我还必须提到这一点: 捕捉(异常 e){ 打印一些东西(e); }

是你可以用异常做的最致命的反模式。是的,你可以在命令行上读取一些错误值,然后呢?调用者(提供了错误值)没有收到错误值的通知,程序将继续执行。

【讨论】:

  • 感谢您提出此模式。我非常喜欢在我的代码中实现模式,这是我肯定会尝试的。也感谢您的反模式建议!老实说,我只是将它用于开发目的,直到我为最初的问题找到解决方案!
【解决方案3】:

我更喜欢通过知道如何处理异常的代码来处理异常。在这种情况下,我假设创建 FileDataValidator 的代码知道如果文件数据无效会发生什么,并且应该在那里处理异常(我主张传播给调用者)。

在讨论最佳实践时 - 类名 FileDataValidator 对我来说很陌生。如果您正在创建的对象存储文件数据,那么我将其称为 FileData - 也许使用 validate 方法?如果您只想验证文件数据,那么静态方法就足够了。

【讨论】:

    猜你喜欢
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-17
    • 2011-01-26
    • 2011-11-19
    相关资源
    最近更新 更多