如何按照Uncle Bob的规则(推荐)在一种方法中正确使用一个try-catch块?
How to follow Uncle Bob's rule(recommendation) to use one try-catch block in one method correctly?
比如我有一个方法
void process(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");
try {
DataResponse response = analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());
//logic
} catch(AnalyticsAPIException e) {
//logic
}
}
IlligalArgumentException
是 未检查 异常
UserNotFoundException
是 未检查 异常
AnalyticsAPIException
是 checked 异常
我读到最好的做法是从 try 开始该方法并以 catch 结束,而不是在一个方法中乘以 try-catch 块。
Prefer exceptions to error codes We prefer exceptions to error codes
because they are more explicit. When dealing with try / catch, we
should not add more logic in a function than the try / catch block, so
that function does one thing: handle errors. Recommendation: don’t use
nested try / catch.
像这样:
void process(String userId) {
try {
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");
DataResponse response = analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());
//logic
} catch(AnalyticsAPIException e) {
//logic
}
}
但看起来很奇怪。我在 try-catch 块中抛出一个异常,希望它不会在 catch 中被处理。我希望它会被抛到调用该方法的服务之上。
接下来我可以做:
public void process(String userId) {
try {
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");
DataResponse response = callApi(userId, user.getDob(), user.getFirstName());
//logic
}
private DataResponse callApi(String userId, Date dob, String firstName){
try {
return analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());
} catch(AnalyticsAPIException e) {
//logic
}
}
但它并不总是有效。那么,什么更好呢?
unclebob 建议您在 try 或 catch 块中没有语句列表。相反,您应该将“正常”情况与异常情况分开。目标是分离不同的抽象级别。
为了避免名称冲突,我经常在“正常”案例前加上“尝试”一词。
我也经常用自己的方法将 try/catch 分开,以保持重点。例如
“处理”方法应该关注“处理用户(userId)”的含义。它是一个抽象级别,如果将它与其他方法分开,它更容易阅读和理解。
void process(String userId) {
User user = getUserById(userId);
loadAnalytics(user);
}
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;
}
理解“过程”的方法不一定要懂
加载分析可能会导致异常状态或什至如何处理它们。
但是当您深入研究 loadAnalytics 方法时,您想知道它是如何工作的。现在您可以立即看到加载分析可能会导致异常状态。该方法专注于异常处理,因为它只包含 try/catch 而您还关注可能发生的异常类型。
void loadAnalytics(User user){
try {
tryLoadAnalytics(user);
} catch(AnalyticsAPIException e) {
handleAnalyticsError(e);
}
}
我经常使用“try”前缀来避免名称冲突并明确方法可能会失败。
void tryLoadAnalytics(){
DataResponse response = callApi(userId, user.getDob(), user.getFirstName());
//logic
}
与“正常”情况一样,异常处理是分开的,因此您可以专注于特定异常的处理方式。
void handleAnalyticsError(AnalyticsAPIException e){
//logic
}
比如我有一个方法
void process(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");
try {
DataResponse response = analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());
//logic
} catch(AnalyticsAPIException e) {
//logic
}
}
IlligalArgumentException
是 未检查 异常UserNotFoundException
是 未检查 异常AnalyticsAPIException
是 checked 异常
我读到最好的做法是从 try 开始该方法并以 catch 结束,而不是在一个方法中乘以 try-catch 块。
Prefer exceptions to error codes We prefer exceptions to error codes because they are more explicit. When dealing with try / catch, we should not add more logic in a function than the try / catch block, so that function does one thing: handle errors. Recommendation: don’t use nested try / catch.
像这样:
void process(String userId) {
try {
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");
DataResponse response = analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());
//logic
} catch(AnalyticsAPIException e) {
//logic
}
}
但看起来很奇怪。我在 try-catch 块中抛出一个异常,希望它不会在 catch 中被处理。我希望它会被抛到调用该方法的服务之上。
接下来我可以做:
public void process(String userId) {
try {
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");
DataResponse response = callApi(userId, user.getDob(), user.getFirstName());
//logic
}
private DataResponse callApi(String userId, Date dob, String firstName){
try {
return analyticsAPI.loadAnalytics(userId, user.getDob(), user.getFirstName());
} catch(AnalyticsAPIException e) {
//logic
}
}
但它并不总是有效。那么,什么更好呢?
unclebob 建议您在 try 或 catch 块中没有语句列表。相反,您应该将“正常”情况与异常情况分开。目标是分离不同的抽象级别。
为了避免名称冲突,我经常在“正常”案例前加上“尝试”一词。 我也经常用自己的方法将 try/catch 分开,以保持重点。例如
“处理”方法应该关注“处理用户(userId)”的含义。它是一个抽象级别,如果将它与其他方法分开,它更容易阅读和理解。
void process(String userId) {
User user = getUserById(userId);
loadAnalytics(user);
}
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;
}
理解“过程”的方法不一定要懂 加载分析可能会导致异常状态或什至如何处理它们。
但是当您深入研究 loadAnalytics 方法时,您想知道它是如何工作的。现在您可以立即看到加载分析可能会导致异常状态。该方法专注于异常处理,因为它只包含 try/catch 而您还关注可能发生的异常类型。
void loadAnalytics(User user){
try {
tryLoadAnalytics(user);
} catch(AnalyticsAPIException e) {
handleAnalyticsError(e);
}
}
我经常使用“try”前缀来避免名称冲突并明确方法可能会失败。
void tryLoadAnalytics(){
DataResponse response = callApi(userId, user.getDob(), user.getFirstName());
//logic
}
与“正常”情况一样,异常处理是分开的,因此您可以专注于特定异常的处理方式。
void handleAnalyticsError(AnalyticsAPIException e){
//logic
}