這是《構建之法》實戰教學的一部分。適合做爲同窗們的第二個程序做業。html
第一個程序做業: 請看 「概論」 一章的練習,或者老師的題目,例如這個。算法
做業要求:編程
軟件工程的做業愈來愈有意思了, 咱們在第一個做業中,用各類語言實現了一個命令行的四則運算小程序。 咱們看看若是要把咱們的小程序升級爲能穩定運行,解決用戶問題的軟件,應該怎麼作。 小程序
建議在作下面的題目的時候,採用結對編程的方式, 在練習中,讓同窗們學會模塊化編程,信息隱藏,接口設計,TDD,等。服務器
你們寫了很多四則運算的練習,這些代碼都各有特點,你們寫的 「軟件」 也有必定的用處。 若是咱們要把這個功能放到不一樣的環境中去 (例如,命令行,Windows 圖形界面程序,網頁程序,手機App), 就會碰到困難, 由於目前代碼的廣泛問題是代碼都散落在 main() 函數或者其餘子函數中,咱們很難把這些功能完整地剝離出來,做爲一個獨立的模塊知足不一樣的需求。 架構
咱們看到,不一樣的代碼解決不一樣層面的問題,有些是內部數據的計算 (例如四則運算);有些是和用戶輸入相關的 (例如 scanf,cin,圖形界面的輸入輸出),有些是和數據的展示相關的 (例如 printf ,cout,println,DrawText),有些是和程序所在平臺的架構相關的(例如 main 函數,程序倒計時的實現等)。 這就須要咱們對軟件的架構作一些整理和優化。框架
建議你們把四則運算的計算功能包裝在一個模塊中 (這個模塊能夠是一個類 Class, 一個DLL,等等), 爲了方便起見,咱們叫它 「計算核心」 模塊, 這個模塊至少在兩個地方可使用:模塊化
測試程序,這個能夠是一個命令行的程序,或者是JUnit 的框架,或者是Visual Studio 單元測試的框架。這樣,咱們在算法層級保證了這個模塊的正確性。 函數
實際的軟件,這是交付給最終用戶的軟件,有必定的界面和必要的輔助功能。單元測試
那麼這個「計算核心」模塊和使用它的其餘模塊之間是什麼關係呢? 它們要經過必定的API (Application Programming Interface) 來和其餘模塊交流。 這個API 接口應該怎麼設計呢? (這是一個給有必定經驗和實力的同窗的題目), 爲了簡單,咱們能夠從下面的最簡單的接口開始:
Calc()
這個Calc 函數接受字符串的輸入(字符串裏就是運算式子,例如 「 5+3.5「, 「7/8 – 3/8 」, 「3 + 90 * (-0.3)「 等等),這個模塊的返回值是一個字符串,例如,前面幾個例子的結果就是 ( 」17.5「, 「 1/2」, 「-24「).
假設咱們用的是類,咱們的測試程序剛開始能夠是很是簡單的測試例子: (用僞代碼表示)
String result = Core.Calc(「1 + 1」) ;
Assert ( result == 「2」); //咱們斷言 1 + 1 的結果必定是 2.
而後同窗們實現本身 Core 的這個功能。
第一階段目標 - 能把計算的功能封裝起來,經過測試程序和API 接口測試其簡單的加法功能。
加法成功以後,而後咱們再作減法, 乘法,除法,咱們假設目前爲止都是兩個操做數的運算,仍是很容易實現的。 因爲同窗們已經在本身之前的程序中實現了各類算法,這時候只要把實現的算法搬過來就行了。 你們能夠不斷增長測試的數量,在每實現一個新的功能的時候,要保證之前運行正確的例子繼續是正確的, 經過這樣的 「迴歸測試「, 來保證本身實現的函數一直是正確的 (請看書中關於單元測試,迴歸測試的內容)。
第二階段目標 - 經過測試程序和API 接口測試其簡單的加減乘除功能。並能看到代碼覆蓋率。
而後,更歡樂的狀況出現了, 多個運算符的運算,帶負數的運算。
啊,等一下,若是咱們考慮這些狀況的話, 咱們這個模塊有一些參數要設置,例如,最多幾個運算符,能帶括號麼?數據範圍是多少,還要設置計算的精度(保留幾位小數,必須是用分數形式表示麼,等等), 這是由什麼API 來決定呢? 咱們能夠擴展 Calc() 的定義,讓它接受一個新的參數 「precision」, 或者咱們能夠啓用一個新的函數 Setting()。
若是我想表示:
最多4 個運算符
數值範圍是 -1000 到 1000
精度是小數點後兩位
怎麼經過API 告訴咱們的模塊呢? 咱們固然能夠用函數的參數直接傳遞,可是參數的組合不少,怎麼定義好參數的規範呢? 建議你們考慮用 XML 來傳遞這些參數。
增長了新的Setting() 函數以後,咱們要讓模塊支持這樣的參數,同時,還要保證原來的各個測試用例繼續正確地工做。
第三階段目標 - 經過測試程序和API 接口測試對於各類參數的支持。並能看到代碼覆蓋率。
這個時候,若是輸入是有錯誤的,例如 「1 ++ 2」, 在數值範圍是 -1000 .. 1000 的時候,傳進去 「10000 + 32768 * 3」, 或者是 「 248.04 / 0」 怎麼辦? 怎麼告訴函數的調用者 「你錯了」? 把返回的字符串定義爲 「-1」 來表示? 那麼若是真的計算結果是 「-1」 又怎麼處理呢?
建議這個時候,咱們要定義各類異常 (Exception), 讓 Core 在碰到各類異常狀況的時候,能告訴調用者 - 你錯了! 固然,這個時候,咱們一樣要進行下面的增量修改:
定義要增長什麼功能 - 例如:支持 「運算式子格式錯誤」 異常
寫好測試用例,傳進去一個錯誤的式子,指望能捕獲這個 異常。 若是沒有,那測試就報錯。
在 Core 模塊中實現這個功能
測試這個功能
同時測試全部之前的功能,保證之前的功能還能繼續工做 (沒有 regression)
確認功能完成,繼續下一個功能
第四階段目標 - 界面模塊,測試模塊和核心模塊的鬆耦合。
既然各組各組同窗都寫了高質量的各個模塊,並且模塊之間的關係是明肯定義的,一致的,那麼,小組A 的測試模塊就能夠測試小組B 的核心模塊;小組C 的用戶界面模塊就能夠和小組B 的核心模塊結合起來,正常運行。對吧?! 那咱們就讓兩個小組 (A,B) 在一塊兒,測試一下下面的狀況:
- A 的核心模塊, 加上B 的測試模塊和用戶界面模塊
- B 的核心模塊,加上A 的測試模塊和用戶界面模塊
兩組同窗分析合併以後出現了什麼問題,爲什麼會出現這樣的問題?如何改進? 而且改進各類模塊中的 bug
(請看北航同窗的做業心得:http://www.javashuo.com/article/p-riylxjvn-kp.html )
第五階段目標 - 經過增量修改的方式,改進程序, 完成對各類錯誤狀況的處理。
選擇兩組程序中高質量的模塊,增長必要的功能,把全部代碼簽入源代碼管理服務器, 同時,把這個軟件發佈出來。
(請看北航一個同窗的嘗試:http://www.javashuo.com/article/p-rzomntcj-kn.html)
第六階段目標 - 如何把這些模塊重用到別的相關項目中去。
例如,如今你們要作一個 【24點遊戲】:
有四個數字,經過加減乘除括號等四則運算,把四個數字組成一個算式,結果是 24。 這個遊戲有兩檔難度,入門級:數字在 1..10 之間; 高級:數字在 1..99 之間
可用命令行或本地GUI 或 網頁界面的方式實現這個遊戲
咱們要從頭開始寫全部的程序麼?這個題目須要的一些功能和咱們花了很長時間作的 「四則運算」 模塊有很多相似之處, 那麼,如何把現有模塊通過少許的改動,快速地構建成爲一個 24 點遊戲所需的模塊呢? 作到這些,並能總結一些規律,軟件工程課程就有點摸到 「工程」 的邊了!