在很久鐵人賽剛開賽的第七天,我們聊過if判斷式可以如何改善
但是那時候少談到布林邏輯容易造成的問題,所以今天我們就來聊聊寫得不好的布林邏輯會造成什麼問題。
假設今天客戶提了一個需求,想要從一串數字中找出不是三的倍數但不是四個倍數的數字,我們可以很輕易的按照敘述的直接實作我們的邏輯。
public List<int> FilterSpecificNumber(List<int> numbers)
{
return numbers.Where(number => !Is3的倍數(number) && !Is4的倍數(number)).ToList();
}
其中我們如果按照需求直接使用!來反向布林邏輯會讓這段代碼變得不易理解。如果我們整理一下這段邏輯,並使用正向敘述把!直接放到方法名稱中。
public List<int> FilterSpecificNumber(List<int> numbers)
{
return numbers.Where(number => IsNot12的倍數(number)).ToList();
}
讓我們來看另一段更貼近產品代碼的例子,這是一段檢查使用者是否可以正常登入系統的代碼。
private void CheckUserCanLogin(User user)
{
if ((user.IsSuspend() ||
!user.IsAuthenticated() ||
user.IsPasswordExpired()) &&
!user.IsTestAccount())
{
throw new LoginFailException();
}
}
可以發現當條件一多且反向邏輯與and/or混用時,代碼會變得十分難以閱讀。其中像是!user.IsAuthenticated()是反向的,但是與他一起or的user.IsSuspend()和user.IsPasswordExpired()卻都是正向的,這讓閱讀代碼變得稍微困難一些。
如果我們透過新增或修改原本的方法,讓敘述變成正向的,閱讀起來也會比較容易理解。
private void CheckUserCanLogin(User user)
{
if ((user.IsSuspend() ||
user.IsUnauthenticated() ||
user.IsPasswordExpired()) &&
user.IsNormalAccount())
{
throw new LoginFailException();
}
}
有些時候使用反上邏輯加上反向敘述,方法名稱相對!來說比較長,視覺上容易注意方法名稱。閱讀到後面的條件的時候還會記得前面的方法名稱,忘記了!,造成看錯代碼,例如
if (!user.IsSuspend() && user.IsAuthenticated() && !user.IsPasswordExpired())
{
// ???
}
調整判斷式的正反敘述大多時候相對其他問題是比較容易調整的,調整完通常也滿有效的改善閱讀代碼的體驗。