iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 28
0
Software Development

在Kata中尋找Clean Code是否搞錯了什麼系列 第 28

Day 28 反向邏輯敘述

在很久鐵人賽剛開賽的第七天,我們聊過if判斷式可以如何改善

  1. 當條件判斷類似時,可以透過Dictionary
  2. 當出現巢狀if判斷式時,可以透過guard 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())
{
		// ???
}

調整判斷式的正反敘述大多時候相對其他問題是比較容易調整的,調整完通常也滿有效的改善閱讀代碼的體驗。


上一篇
Day 27 參數過多的問題
下一篇
Day 29 不必要的註解
系列文
在Kata中尋找Clean Code是否搞錯了什麼30

尚未有邦友留言

立即登入留言