Exception VS Control Flow
每當提到Exeption就會有人跳出來說“Exception not use for flow control”,那到底是什么意思呢?什么情況下Exception就算控制流程了,什么時候就該拋出Exception了呢?首先什么是Exception?
Definition:
An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions.
再看什么是“流程”?如果流程是指程序的每一步執行,那異常就是控制流程的,它就是用來區分程序的正常流程和非正常流程的,從上面異常的定義就可以看出。因此為了明確我們應該說”不要用異常控制程序的正常流程“。如何定義正常流程和非正常流程很難,這是一個主觀的決定,沒有一個統一的標準,只能根據實際情況。網上找個例子:
bool isDouble(string someString) {
try {
double d = Convert.ParseInt32(someString);
} catch(FormatException e) {
return false;
}
return true;
}
這個程序其實不是想convert數字,而是想知道一個字符串是否包含一個數字,通過判斷是不是有異常的方式來決定返回true還是false,這是個Smell,這種應該算”異常控制了正常流程“。我們可以通過正則表達式或其他方式來判斷。try {
double d = Convert.ParseInt32(someString);
} catch(FormatException e) {
return false;
}
return true;
}
另外Clean Code上一個例子:
try {
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {
m_total += getMealPerDiem();
}
MealExpensesNotFound異常影響了正常的計算m_total的業務邏輯。對于這種情況可以通過一下方式改進:MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {
m_total += getMealPerDiem();
}
public class PerDiemMealExpenses implements MealExpenses {
public int getTotal() {
// return the per diem default
}
}
public int getTotal() {
// return the per diem default
}
}
以上兩個例子是比較明顯的異常控制正常流程,Smell很明顯,不會有很大爭議,但是實際情況中可能有很多例子沒有這么明顯,因為都是主觀判定的。比如一下代碼,算不算異常控制正常流程?
public int doSomething()
{
doA();
try {
doB();
} catch (MyException e) {
return ERROR;
}
doC();
return SUCCESS;
}
{
doA();
try {
doB();
} catch (MyException e) {
return ERROR;
}
doC();
return SUCCESS;
}
看到這樣一段程序,如果沒有上下文,我們無法判斷。但是如果doSomething是想讓我們回答yes or no,success or error,我們不應該通過有無異常來判斷yes or no,success or error,應該有個單獨的方法來判斷,這個方法就只做這一件事情。如果doSometing是執行一個操作,那么在這個過程中我們假定是不會出現問題的,否則拋出異常是比較合理的。