如何遵循鲍勃叔叔的规则(建议)在一种方法中正确使用一个 try-catch 块?

Pav*_*hov 3 java architecture refactoring exception clean-architecture

例如我有一个方法

\n
void process(String userId) {\n  if(userId == null) throw new IlligalArgumentException("User ID is required");\n\n  User user = userService.findUserById(userId);\n\n  if(user == null) throw new UserNotFoundException("User with ID: "+ userId +" not found");\n  \n  try {\n    DataResponse response = analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());  \n\n    //logic\n  } catch(AnalyticsAPIException e) {\n     //logic\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. IlligalArgumentException未经检查的异常
  2. \n
  3. UserNotFoundException未经检查的异常
  4. \n
  5. AnalyticsAPIException已检查的异常
  6. \n
\n

我读到,最好的做法是从 try 开始该方法并以 catch 结束,而不是在一个方法中乘以 try-catch 块。

\n
\n

优先使用异常而不是错误代码 我们更喜欢使用异常而不是错误代码\n因为它们更加明确。在处理 try / catch 时,我们不应该在函数中添加比 try / catch 块更多的逻辑,因此该函数只做一件事:处理错误。建议:不要\xe2\x80\x99t使用\n嵌套的try/catch。

\n
\n

像这样的东西:

\n
void process(String userId) {\n  try {\n    if(userId == null) throw new IlligalArgumentException("User ID is required");\n  \n    User user = userService.findUserById(userId);\n  \n    if(user == null) throw new UserNotFoundException("User with ID: "+ userId +" not found");\n    \n    DataResponse response = analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());  \n    // logic\n  } catch(AnalyticsAPIException e) {\n       //logic\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

但看起来很奇怪。我在 try-catch 块内抛出异常,并希望它不会在 catch 中处理。我希望它将被抛出到调用该方法的服务上。

\n

接下来我可以做:

\n
public void process(String userId) {\n  if(userId == null) throw new IlligalArgumentException("User ID is required");\n\n  User user = userService.findUserById(userId);\n\n  if(user == null) throw new UserNotFoundException("User with ID: "+ userId +" not found");\n  \n  DataResponse response = callApi(userId, user.getDob(), user.getFirstName());  \n\n  //logic\n }\n    \nprivate DataResponse callApi(String userId, Date dob, String firstName){\n  try {\n    return analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());  \n  } catch(AnalyticsAPIException e) {\n    //logic\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

但它并不总是有效。那么,什么是更好的呢?

\n

Ren*_*ink 5

Unclebob 的建议是,在 try 或 catch 块中没有语句列表。相反,您应该将“正常”情况与异常情况分开。目标是分离不同的抽象级别。

为了避免名称冲突,我经常在“正常”情况下加上“try”一词。我还经常用自己的方法将 try/catch 分开,以保持重点。例如

“process”方法应该关注“处理用户(userId)”的含义。它是一个抽象级别,如果将其与其他方法分开,它会更容易阅读和理解。

void process(String userId) {
    User user = getUserById(userId);

    loadAnalytics(user);
}
Run Code Online (Sandbox Code Playgroud)

getUserById 仅关注当您想通过用户的 id 获取用户时所需的逻辑。

void void getUserById(String userId){
    if(userId == null) throw new IlligalArgumentException("Usesr ID is required");
    
    User user = userService.findUserById(userId);
    if(user == null) throw new UserNotFoundException("User with ID: "+ userId +" not found");
    
    return user;
}
Run Code Online (Sandbox Code Playgroud)

要理解“过程”方法,不必了解加载分析可能会导致异常状态,甚至不需要了解它们的处理方式。

但是当您深入研究 loadAnalytics 方法时,您想知道它是如何工作的。现在您可以立即看到加载分析可能会导致异常状态。该方法专注于异常处理,因为它只包含 try/catch,并且您还关注可能发生的异常类型。

void loadAnalytics(User user){
    try {
     tryLoadAnalytics(user);
    } catch(AnalyticsAPIException e) {
     handleAnalyticsError(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

我经常使用“try”前缀来避免名称冲突并明确方法可能会失败。

void tryLoadAnalytics(){
    DataResponse response = callApi(userId, user.getDob(), user.getFirstName());  
    
    //logic
}
Run Code Online (Sandbox Code Playgroud)

与“正常”情况一样,异常处理是分开的,以便您可以专注于如何处理特定异常。

void handleAnalyticsError(AnalyticsAPIException e){
    //logic
}
Run Code Online (Sandbox Code Playgroud)