[TDD][Codewars] Mumbling
上回邀請朋友一起來進行 pair programming,隨機挑了一個 Codewars 的題目來練習:Mumbling。
題目描述:
【題目說明】給一個字串 s, 將第 n 個字母,重複 N 次,並用 "-" 串接起來。每個字母重複 N 次的第一次要轉成大寫,其他的要轉成小寫。
例如:s 為 "cwAT", 期望回傳值為 "C-Ww-Aaa-Tttt"
Step 1, 新增測試用例,s 長度為 1,結果應轉大寫
【新增測試代碼】
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 供各位朋友參考:
生產代碼的主邏輯最後其實可以一行代碼就搞定,但 TDD 延展出來的思路還是本文練習的重點,而且可以看到每一行都是其來有自,為了滿足某個需求而驅動出來的代碼。
這一道 kata 題目實在是相當單純,不論是拿來練習 LINQ to Objects 或是 TDD 或是 coding dojo 的重構與 pair programming,都單純到挺合適的。而且隨時可以進行「需求異動」來增加變化與難度,推薦給大家。