iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 10
0
Software Development

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

Day 10 抽象之後

昨天提到了可以透過抽取方法整理複雜邏輯,在Sum Consecutive的例子中,雖然把while迴圈抽出去之後多了好幾個方法,但是可以發現每個方法做的事情比較簡單且具體,讓我們更能專注在每個方法的實作。

以昨天例子來說,當我們把方法拆細,每一個方法功能看上去邏輯比較簡單,再來我們就可以繼續往每個方法實作調整。例如前兩個方法都是使用迴圈實作,就可以想想看使否可以使用LINQ來簡化。

public List<int> SumConsecutives(List<int> numbers)
{
    var consecutiveSums = new List<int>();
    for (var index = 0; index < numbers.Count; )
    {
        var consecutiveNumbers = GetConsecutiveNumbers(numbers, index);
        consecutiveSums.Add(consecutiveNumbers.Sum());
        index += consecutiveNumbers.Count;
    }
    
    return consecutiveSums;
}

private List<int> GetConsecutiveNumbers(List<int> numbers, int index)
{
    var consecutiveNumbers = new List<int> {numbers[index]};
    while (!IsLastNumber(numbers, index) && IsNumberConsecutive(numbers, index))
    {
        consecutiveNumbers.Add(numbers[index]);
        index++;
    }
    return consecutiveNumbers;
}

private static bool IsNumberConsecutive(List<int> numbers, int index)
{
    return numbers[index] == numbers[index + 1];
}

private bool IsLastNumber(List<int> numbers, int index)
{
    return index == numbers.Count - 1;
}

GetConsecutiveNumbers(List numbers, int index)目的是找出當前index後的連續數字。我們能夠使用LINQ所提供的Paging method來簡化他。

private List<int> GetConsecutiveNumbers(List<int> numbers, int index)
{
    return numbers.Skip(index)
        .TakeWhile(number => number == numbers[index]).ToList();
}

關於LINQ的Paging method,之後有機會我們會在多用一點例子來聊。

在看到原本的SumConsecutives(List numbers),裡頭是把一組一組連續數字加起來並放到consecutiveSums中。原本的演算法因為Index遞增的方式不固定,沒那麼好改成LINQ形式。但是如果試著改用其他演算法,還是能以LINQ的形式來簡化代碼。例如pengzhisun的做法,相比原本的代碼,變得較為簡潔。

public List<int> SumConsecutives(List<int> numbers)
{
    return numbers.Select((number, index) => index > 0 && numbers[index - 1] == number ? (int?) null : GetConsecutiveNumbers(numbers, index).Sum())
					        .Where(sum => sum.HasValue)
									.Select(sum => sum.Value)
									.ToList();
}

最後代碼長度一開始的解法長度差不多,但是透過LINQ語法和適當的方法名稱,讓最後的做法可讀性好了一些。

public List<int> SumConsecutives(List<int> numbers)
{
    return numbers.Select((number, index) => index > 0 && numbers[index - 1] == number ? (int?) null : GetConsecutiveNumbers(numbers, index).Sum())
					        .Where(sum => sum.HasValue)
									.Select(sum => sum.Value)
									.ToList();
}

private List<int> GetConsecutiveNumbers(List<int> numbers, int index)
{
    return numbers.Skip(index)
					        .TakeWhile(number => number == numbers[index]).ToList();
}

透過抽取方法來讓我們拆解原本的大方法,不但能使代碼更好閱讀理解,而且能讓我們更關注每個一個方法的實作內容,進而把每個小方法優化改善。這種Divide and Conquer思考方式,不但能運用在演算法上,更能運用在優化代碼。


上一篇
Day 9 抽象你的代碼
下一篇
Day 11 抽取方法的時機
系列文
在Kata中尋找Clean Code是否搞錯了什麼30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言