在Java中,从调用方还是在它调用的方法中进行所有必要的检查更好?

2022-09-02 00:20:39

让我们看一下这两个例子。

第一:

try {
    execute(testObj);
} catch(Exception e) {
    //do somethingwith that
}

public void execute(TestObj testObj) throws Exception {
    if (testObj == null){
        throw new Exception("No such object");
    }
    //do something with object
}

第二:

if (testObj != null){
    execute(testObj);
} else {
    //handle this differently
}

public void execute(TestObj testObj) {
    //do something with object
}

如果我们需要检查“是空”或其他任何东西,现在不是重点。我想知道哪种做法总体上更好 - “检查,然后做”或“做,然后处理异常,如果发生”?


答案 1

两者都不是,您只应该检查系统之间的边界

系统之间的界限是什么?

  1. 当您收到来自用户的输入时,
  2. 当您读取文件、网络或套接字时,
  3. 当您执行系统调用或进程间通信时,
  4. 在库代码中,在所有面向公众的 API 中。
  5. 等。

在库代码中,由于公共 API 可能由外部代码调用,因此应始终签入可由外部代码调用的任何内容。无需检查仅供内部使用的方法(即使其访问修饰符是公共的)。然后,根据个人喜好,参数错误可以通过异常或返回代码发出信号,但是检查的异常不能被忽略,因此它通常是向外部代码发出错误信号的首选方法。

在应用程序代码(与库相反)中,只有在接收来自用户的输入、加载 URL、读取文件等时,才应进行检查。无需在公共方法中执行输入检查,因为它们只能由您自己的应用程序代码调用。

在您自己的代码中,您应该避免首先需要签入的情况。例如,您可以使用“空对象模式”,而不是检查 。如果你仍然需要做检查,尽早做,这通常意味着在外面做,但要尽量找到更早的点。null

即使没有正式写下来,即使它没有被执行,所有的方法,无论是内部的还是面向公众的,都应该有一个合同。不同之处在于,在面向外部的代码中,您应该强制执行协定作为错误检查的一部分,而在仅内部代码中,您可以并且应该依靠调用方知道要传递什么和不传递什么。

简而言之,由于检查仅在系统边界上完成,因此如果您实际上可以选择检查方法内部还是外部,那么您可能不应该在那里进行检查,因为这不是系统边界

在系统边界中,双方始终必须检查。调用方必须检查外部调用是否成功,被调用方必须检查调用方是否给出了合理的输入/参数。忽略调用方中的错误几乎总是一个错误。鲁棒性原则适用,始终检查您从外部系统收到的所有内容,但只发送您知道外部系统可以肯定接受的内容。

但是,在非常大的项目中,通常有必要将项目划分为小模块。在这种情况下,可以将项目中的其他模块视为外部系统,因此,您应该在向项目中其他模块公开的 API 中执行检查。

TLDR;检查很难。


答案 2

正如@LieRyan所说,您应该只检查系统之间的边界。但是,一旦你进入了自己的代码,你仍然需要能够检测到意想不到的问题,主要是因为:

  • 您(和您的同事)并不完美,可能会将 null 传递给无法处理它的方法。
  • 如果一行使用两个对象,一个是空的,NPE不会告诉你哪一个是罪魁祸首(System.out.println (“a.getName() ” + “b.getName()”) 会抛出 NPE...是空或 b 空或两者兼而有之?

清楚地记录哪些方法接受 null 作为参数,或者可能返回 null。
如果您使用的是Eclipse Juno,一个很好的工具可以帮助您(我认为IntelliJ-Idea也有类似的东西)是启用空值检查分析。它允许您编写一些注释以进行编译时空检查。这真的很棒。你可以写类似的东西

public @NonNull String method(@Nullable String a){
//since a may be null, you need to make the check or code will not compile
    if(a != null){
        return a.toUppercase();        
    }
    return ""; //if you write return null it won't compile, 
     //because method is marked NonNull 
}

它还具有很好的@NonNullByDefault,它基本上说“除非标记为@Nullable,否则没有方法接受或返回null”这是一个不错的默认值,可以保持代码的干净和安全。

有关更多信息,请查看 Eclipse 帮助


推荐