Coding道場:第一次

10/23日,我在部門內部進行了一次內部學習,使用目前流行的Coding Dojo(道場)方式,進行了TDD開發的演練。 演練的題目以下:
 
 
有關Coding道場的介紹,請自行百度一下,我就再也不多作介紹了。
 
從效果來看,基本達到了傳達TDD是什麼樣的開發方式的目的。尤爲是你們從最初滿腦子如何實現這個程序,怎樣去設計算法,逐漸轉變爲了先想如何測試,從最簡單的實現開始,最終演化成最終的設計。固然,目前爲止,參加人員也只是理解了TDD是一個什麼樣的開發方式而已,還談不到真正使用TDD進行開發。這須要一個更加長期的自我訓練和使用的過程。 使用TDD,最主要的是一種思惟方式的變化。
首先:要堅信全部的程序皆可測,若是不能測試,不是產品的特性致使,而是本身的能力不足致使,設計上有問題。所以必須從設計上加以改變,使得程序可測。如通常認爲曲線的顯示是否正常,是沒法使用自動測試的。換個角度:若是顯示只是一個數據-座標的轉換的話,測試的重點就變成了數據是否正確,而這一點是徹底可測的。
其次:雖然須要全局的考慮,可是要從簡單入手,演進式設計。
這一點,在本次道場演練中體現的就很明顯,這次道場開始,不少人的想法就是,創建某種算法,將須要的字符顯示出來。因而第一個函數就是:void DisplaySegmentDigital(String input),而後再寫那些子函數。如何測試這個函數?這是一個輸出到屏幕顯示的函數,它只能用眼來判斷,顯然不適合自動測試或者單元測試。因此,TDD不是一個先實現框架,再實現具體功能的作法。輸出到屏幕,只是最後的一個過程,也是一個簡單的過程,所以能夠沒必要做爲重點。重點在於顯示的數據是什麼?因此,函數就變爲了:String DisplaySegmentDigital(String input)。這個時候,這個函數再也不是向屏幕輸出,而是輸出一個字符串,再由另一個字符串顯示函數完成向屏幕的輸出。而原來這個函數就變得可測了。因而,第一個測試函數被寫出來了:
String strText = "910"; String strTextResult = "._.|_|..|.....|..|._.|.||_|";  
String strOutput = digitalSegment.DisplaySegmentDigital(strText);   
assertEquals(strOutput , strTextResult);
第一個測試順利經過,由於實現很是簡單:
public String DisplaySegmentDigital(String strText) {
    return "._.|_|..|.....|..|._.|.||_|";
}
接下來的困難是:下一個測試什麼?測試「3456」的輸出?OK,咱們先試試看,因而咱們想寫第二個測試:

String strText = "3456"; String strTextResult = "????????";git

問題接着出來了:這串問號該填什麼?這樣測試真的有意義麼?幾乎全部的人都直以爲發現這裏有問題。簡短的討論後,結論是應該測試每一個數字的顯示,而非一個字符串。因而,測試變爲:算法

String strText = "9"; String strTextResult = "._.|_|..|";   框架

String strOutput = digitalSegment.DisplaySegmentDigital(strText);   函數

assertEquals(strOutput , strTextResult);單元測試

實現變爲:學習

public String DisplaySegmentDigital(String strText) {測試

 if(strText == "9")spa

  return "._.|_|..|";設計

 else開發

  return null;

}

實現後,接着測試:

strText = "1"; strTextResult = ".....|..|";  

strOutput = digitalSegment.DisplaySegmentDigital(strText);  

assertEquals(strOutput , strTextResult);

實現也變爲:

public String DisplaySegmentDigital(String strText) {

 String[] strResult=new String[10];

 strResult[0]="._.|.||_|";

 strResult[1]=".....|..|";

 strResult[9]="._.|_|..|";

 return strResult[Integer.parseInt(strText)];

}

至此,很顯然咱們的算法也就天然而然的誕生了。可能與不少人本身開始的算法設計不太同樣,但也不該該差到哪裏:)。這就是TDD演進式設計。

但有個問題,._.|_|..|是什麼東東?我怎麼知道最終輸出是正確的。所以,咱們稍微改變了一下寫法:

String strText = "9";   String strTextResult = "._." +
                                                                 "|_|" +
                                                                  "..|";     

String strOutput = digitalSegment.DisplaySegmentDigital(strText);

assertEquals(strOutput , strTextResult);

strText = "1";   

strTextResult = "..." +

                        "..|" +

                        "..|";     

strOutput = digitalSegment.DisplaySegmentDigital(strText);      

assertEquals(strOutput , strTextResult);

實現變爲:

public String DisplaySegmentDigital(String strText) {

 String[] strResult=new String[10];

 strResult[0]="._." +

                     "|.|" +

                     "|_|";

 strResult[1]="..." +

                     "..|" +

                     "..|";

 strResult[9]="._." +

                    "|_|" +

                     "..|"; 

 return strResult[Integer.parseInt(strText)];

}

如今直觀多了。

爲何必定要變得直觀,其目的不單純是爲了程序的易讀性,更重要的是:測試不該該抄實現的代碼,實現也不要抄測試的代碼,不然後果很嚴重。寫測試代碼時,必須是含着測試的心態,含着使用者的心態去寫測試代碼,而非一門心思去想實現。若是這樣,TDD就失敗了。這也是爲何TDD要求先寫測試代碼,再寫實現代碼的緣由。由於咱們一旦先想到了實現,那麼接下來的測試,必然會跟着實現的邏輯走,從而違背「測試獨立性」的原則。實現發生錯誤,測試也沒法發現。

接下來須要整理一下代碼,顯然DisplaySegmentDigital這個函數名不是那麼準確,後來議論了一番,得出的名字是:GetDigitalDisplayContent。結束後,我想GetDigitalFont可能更好。

好了,有關第一次道場就寫到這裏,留一個小小的問題:到目前爲止咱們尚未設計顯示的算法。爲了便於輸出,目前的設計如何改進?

相關文章
相關標籤/搜索