[TDD][Codewars] Mumbling

上回邀請朋友一起來進行 pair programming,隨機挑了一個 Codewars 的題目來練習:Mumbling

題目描述:

【題目說明】給一個字串 s, 將第 n 個字母,重複 N 次,並用 "-" 串接起來。每個字母重複 N 次的第一次要轉成大寫,其他的要轉成小寫。

例如:s 為 "cwAT", 期望回傳值為 "C-Ww-Aaa-Tttt"

Step 1, 新增測試案例,s 長度為 1,結果應轉大寫 

【新增測試代碼】s_is_b_should_return_B()

Step 2, hard-code 實作生產代碼,針對第一個字母 ToUpper() 轉大寫後回傳

【生產代碼】

【重構測試代碼】封裝 assertion 行為,AccumShouldBe()

Step 3, 新增測試案例,s 長度為 2,應處理第二個字母重複 2 次,第一次轉大寫,並使用連接號相加

【測試代碼】

Step4, hard-code 處理 s[0]s[1] 轉大寫與連接號串接

【生產代碼】

【重構生產代碼】將 char 呼叫 ToString()ToUpper() 重構,擷取方法至 ToUpperChar()

Step 5, 新增測試案例,s 長度仍為 2,但 s 第二個字母為大寫。在重複時,應該轉為小寫。

【新增測試代碼】

【調整生產代碼】增加 ToLower() 處理,以通過測試案例

【重構生產代碼】重構擷取方法,將 char 轉成小寫也封裝成 ToLowerChar()

Step 6, 新增測試案例, s 長度為 3

【新增測試代碼】

【調整生產代碼】hard-code 處理 s[2] 的部分,等等再來重構商業邏輯

【重構生產代碼】使用 GetRepeatLetters() 取代原本 hard-code 的重複字母

【重構生產代碼】使用 Loop  取代 s[0], s[1], s[2] hard-code index

【重構生產代碼】以 LINQ 的 Aggregate() 取代 for loop 不斷串接 result 的邏輯。

【重構生產代碼】將每個 char 透過 Select() 轉成對應的重複字串,再用連接號連接每個 element

最終版本生產代碼
    public class Accumul
    {
        public static String Accum(string s)
        {
            var result = s.Select((c, i) => ToUpperChar(c) + GetRepeatLetters(c, i));
            return string.Join("-", result);
        }

        private static string GetRepeatLetters(char letter, int repeatTimes)
        {
            return string.Concat(Enumerable.Repeat(letter.ToString().ToLower(), repeatTimes));
        }

        private static string ToUpperChar(char letter)
        {
            return letter.ToString().ToUpper();
        }
    }

結論

同樣的題目,練習不只一次,有時思路會很不一樣。這一題 Codewars 一開始其實是三個人以 TDD 一起做 coding dojo 的過程,我們最後一起完成了這一題,但我當天晚上重新練習了一次,讓整個過程更貼近精簡的 TDD 堆砌出生產代碼。

附上兩個版本的 commit history 供各位朋友參考:

  1. 3 人 coding dojo 版本的 commit history
  2. 本文 TDD 過程的 commit history 版本

生產代碼的主邏輯最後其實可以一行代碼就搞定,但 TDD 延展出來的思路還是本文練習的重點,而且可以看到每一行都是其來有自,為了滿足某個需求而驅動出來的代碼。

這一道 kata 題目實在是相當單純,不論是拿來練習 LINQ to Objects 或是 TDD 或是 coding dojo 的重構與 pair programming,都單純到挺合適的。而且隨時可以進行「需求異動」來增加變化與難度,推薦給大家。

blog 與課程更新內容,請前往新站位置:http://tdd.best/