Java 先验证还是尝试捕获?

Java 先验证还是尝试捕获?,java,code-design,Java,Code Design,假设我有一个如下的函数: public void saveBooking(/* some inputs */) { //save into database } //do all the validations and do any necessary handling. Then... saveBooking(/*inputs*/); public void saveBooking(/* some inputs */) /* throws various exceptions */

假设我有一个如下的函数:

public void saveBooking(/* some inputs */) {
    //save into database
}
//do all the validations and do any necessary handling. Then...
saveBooking(/*inputs*/);
public void saveBooking(/* some inputs */) /* throws various exceptions */ {
    //various validations
    //save into database
}

//...and in the main program...
try{
    saveBooking(/*inputs*/);
}
catch(MyException1 e1){
    //do something
}
catch(MyException2 e2){
    //do something
}
在保存到数据库之前,我必须进行各种验证。我可以在我的主程序中执行以下操作:

public void saveBooking(/* some inputs */) {
    //save into database
}
//do all the validations and do any necessary handling. Then...
saveBooking(/*inputs*/);
public void saveBooking(/* some inputs */) /* throws various exceptions */ {
    //various validations
    //save into database
}

//...and in the main program...
try{
    saveBooking(/*inputs*/);
}
catch(MyException1 e1){
    //do something
}
catch(MyException2 e2){
    //do something
}
这样,我确信所有数据在保存到数据库之前都必须通过所有必要的验证。但是,这意味着函数
saveBooking()
与验证方法密切相关。每次我想调用
saveBooking()
,我都必须确保不会忘记调用验证

或者,我可以将所有验证放在函数本身中,这样我所要做的就是调用该方法,一切都会得到处理。但是,为了独立处理所有错误,我必须让函数在主程序中抛出异常和捕获。它应该是这样的:

public void saveBooking(/* some inputs */) {
    //save into database
}
//do all the validations and do any necessary handling. Then...
saveBooking(/*inputs*/);
public void saveBooking(/* some inputs */) /* throws various exceptions */ {
    //various validations
    //save into database
}

//...and in the main program...
try{
    saveBooking(/*inputs*/);
}
catch(MyException1 e1){
    //do something
}
catch(MyException2 e2){
    //do something
}
这也意味着我必须自己创建多个异常。好的是,我不必担心我必须将什么样的验证摆在面前


有了这些,我不确定哪一个是最好的代码设计。我个人更喜欢第一种方法,它更具可读性,但它相互依赖太多,当我需要在许多地方使用它时,它会变得更糟。请指教

肯定是第一个选项,而不是第二个选项。我认为第二个是滥用例外。异常是指异常情况,验证失败不是“异常”

每次我想调用
saveBooking()
,我都必须确保不会忘记调用验证

将验证逻辑放入一个单独的方法中,并让
saveBooking()
在执行任何其他操作之前调用验证方法

public List<ValidationError> validateBooking(/* args */)
{
    // as @Jared Farrish suggests, return a list of validation errors
}

public boolean saveBooking(/* args */)
{
    List<ValidationError> errors = validateBooking(/* args */);

    if (errors.size() != 0) return false; // validation failed

    // save to the database

    return true;
}
public List validateBooking(/*args*/)
{
//正如@Jared Farrish所建议的,返回验证错误列表
}
公共布尔存储预订(/*args*/)
{
列表错误=validateBooking(/*args*/);
if(errors.size()!=0)返回false;//验证失败
//保存到数据库
返回true;
}

三层方法非常常见,其中有以下三层:

  • 客户端接口。这可以包括web应用程序的JavaScript验证或GUI控件上的一些基本SWT验证
  • 业务层。该层了解业务规则。这是您通常希望放置服务器端验证的地方。任何应该保存、更改等的操作都应该通过业务层完成(这样,您就可以随时进行验证,而不必关心数据存储)
  • 数据层。该层通常是“哑的”。只需保存、删除、选择等;无论业务层请求什么,这个层都会简单地执行

这非常简单,但它为您提供了一种分离不同类型逻辑的好方法,以便于维护。

您在插入之前验证所有内容肯定是正确的,您还应该验证插入的所有内容是否都符合数据库中的约束条件,以避免意外的SQLException,这是您所不期望的,并且会一直到顶部

我建议创建一个带有一些属性的自定义异常来描述错误的原因,这样您只需担心捕获一种异常


另外,我会明确地将验证放在方法中,因此它总是被调用。

检查通常应该在函数本身中执行,这样就不可能在不首先验证数据的情况下尝试保存数据。如果函数中没有这些检查,您可能会发现客户机试图在未经验证的情况下保存,这很少是一件好事

但您不限于为此使用异常,只需返回某种类型的错误代码供调用方检查即可。虽然我通常不在乎错误是由异常还是由返回代码造成的,但有些人可能认为这是对异常的滥用

验证代码可能仍然是一个单独的函数,因为您自己的代码可能希望调用它而不进行保存。类似于(伪代码):

如果只是为了处理I/O错误等,调用
saveBookings
仍然可能会捕获异常,但这不是绝对必要的:您也可以在函数中捕获这些异常并将其转换为返回代码


我倾向于从每个函数中选择一种报告方法,这样我(例如)就不必尝试/捕获并检查返回代码。

将大量代码放在一个地方不是一个好主意。抛出已检查的异常也不是一个好主意。

另一种方法(无论是好是坏)是从
saveBooking()
返回包含任何错误的对象,其中错误包含在数组或对象中。@Jared我同意,如果您不希望该方法返回错误,因为您已经返回了一些更有用的内容,那么在方法参数中传递一个HashMap或List。如果出现错误,那么您可以填充此数据结构并在以后处理它们。我不确定将某个内容作为参数传递给存储而不是处理它是否是一种好的做法。我以前是这样做的,但我觉得有点奇怪。有时我会问自己,“呃,为什么我要在函数中传递该参数?它似乎根本不需要”,然后发现它是用于数据存储的。此外,您的存储很可能是一个DB,您通常会在其中对模式的有效性和一致性进行进一步检查。有些检查也可以在数据库中更有效地执行。对不起,您所说的“报告方法”是什么意思?我以前从未想过返回错误代码。说到这里,听起来不错,只是我们必须维护错误代码列表。嗯…@Davuth,我是说m