福大軟工1816 · 第二次作業 - 個人項目
Deadline: 2018-09-12 23:00pm
零、任務
實現一個能夠對文本文件文件中的單詞的詞頻進行統計的控制臺程序。
一、編碼要求
- 按照[附錄1]提示,Fork github 項目到自己的倉庫,在Github倉庫中新建一個學號為名的文件夾,完成項目後正確發起一個Pull Request,並確保自己的代碼最終成功簽入(成功簽入將在源倉庫中看到自己學號為名的文件夾)。
- 在開始實現程序之前,在PSP表格[附錄2]記錄下你估計在程序開發各個步驟上耗費的時間,在你實現程序之後,在PSP表格記錄下你在程序的各個模塊上實際花費的時間。
- 使用C++ 或者Java語言實現,C++請使用Visual Studio Community 2017進行開發,運行環境為64-bit Windows 10。
- 提交的代碼要求經過Code Quality Analysis工具的分析並消除所有的警告。
- 完成項目的首個版本之後,請使用性能分析工具Studio Profiling Tools來找出代碼中的性能瓶頸並進行改進。
- 使用Github[附錄3]來管理源代碼和測試用例,代碼有進展即簽入Github。簽入記錄不合理的項目會被助教抽查詢問項目細節。
- 使用單元測試[附錄4]對項目進行測試,並使用插件查看測試分支覆蓋率等指標;寫出至少10個測試用例確保你的程序能夠正確處理各種情況。
二、博客撰寫要求
- 在文章開頭給出Github項目地址。
- 給出PSP表格。
- 解題思路描述。即剛開始拿到題目後,如何思考,如何找資料的過程。
- 設計實現過程。設計包括代碼如何組織,比如會有幾個類,幾個函數,他們之間關系如何,關鍵函數是否需要畫出流程圖?單元測試是怎麽設計的?
- 記錄在改進程序性能上所花費的時間,描述你改進的思路,並展示一張性能分析圖(由VS 2017的性能分析工具自動生成),並展示你程序中消耗最大的函數。
- 代碼說明。展示出項目關鍵代碼,並解釋思路與註釋說明。
- 結合在構建之法中學習到的相關內容與結對項目的實踐經歷,撰寫解決項目的心路歷程與收獲。
三、需求
實現一個命令行程序,不妨稱之為WordCount。
第一步、實現基本功能
輸入文件名以命令行參數傳入。例如我們在命令行窗口(cmd)中輸入:
//C語言類 WordCount.exe input.txt //Java語言 java WordCount input.txt
則會統計input.txt中的以下幾個指標
- 統計文件的字符數:
- 只需要統計Ascii碼,漢字不需考慮
- 空格,水平制表符,換行符,均算字符
- 統計文件的單詞總數,單詞:至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。
- 英文字母: A-Z,a-z
- 字母數字符號:A-Z, a-z,0-9
- 分割符:空格,非字母數字符號
- 例:file123是一個單詞, 123file不是一個單詞。file,File和FILE是同一個單詞
- 統計文件的有效行數:任何包含非空白字符的行,都需要統計。
- 統計文件中各單詞的出現次數,最終只輸出頻率最高的10個。頻率相同的單詞,優先輸出字典序靠前的單詞。
按照字典序輸出到文件result.txt:例如,windows95,windows98和windows2000同時出現時,則先輸出windows2000
- 輸出的單詞統一為小寫格式
輸出的格式為
characters: number
words: number
lines: number
<word1>: number
<word2>: number
...
第二步、接口封裝
在寫了一些代碼開胃之後,大家都完成了一份滿足WordCount基本功能的代碼。
大家的代碼都各有特色,如果現在我們要把這個功能放到不同的環境中去(例如,命令行,Windows圖形界面程序,網頁程序,手機App),就會碰到困難:代碼散落在各個函數中,很難剝離出來作為一個獨立的模塊運行以滿足不同的需求。
同時我們也看到,不同的代碼解決不同層面的問題:
- 有些是計算數據的(例如統計單詞)
- 有些是控制輸入的(例如scanf,cin,圖形界面的輸入字段)
- 有些是數據可視化的(例如printf,cout,println,DrawText)
- 有些則更為特殊,是架構相關的(例如main函數,並不是所有的程序都需要某個特定格式的main)
這些代碼的種類不同,混雜在一起對於後期的維護擴展很不友好,所以它們的組織結構就需要精心的整理和優化。
我們希望把基本功能裏的:
- 統計字符數
- 統計單詞數
- 統計最多的10個單詞及其詞頻
這三個功能獨立出來,成為一個獨立的模塊(class library, DLL, 或其它)。這樣的話,命令行和GUI的程序都能使用同一份代碼。為了方便起見,我們稱之為計算核心"Core模塊",這個模塊至少可以在幾個地方使用:
- 命令行測試程序使用
- 在單元測試框架下使用
- 與數據可視化部分結合使用
把計算核心在單元測試框架中做過完備的測試後,我們就可以在算法層級保證了這個模塊的正確性。
但我們知道軟件並非只有計算核心,實際的軟件是交付給最終用戶的軟件,除了計算核心外,還需要有一定的界面和必要的輔助功能。
這個Core模塊和使用它的其他模塊之間則要通過一定的API來交流。
API應該怎麽設計呢?
為了方便起見,我們可以從下面的最簡單的接口開始(僅舉例,你的代碼裏可能沒有這個函數):
int countChar(File *file)
這個函數表示輸出一個文件指針,返回這個文件的字符數。
假設我們用Core封裝了這個接口,那麽我們的測試程序可以是這樣:
File *in = fopen("input.txt","r");
int count = 100;
Assert(countChar(in) == count);
當然,這樣的測試程序並不充分,希望大家測試時不要像這樣偷懶。
四、測試須知
組織目錄
助教在測試時,將運行自動測試程序編譯源文件並運行,進行批量測試,因此請保證項目的組織目錄符合要求.
Java
對於使用Java語言的項目有以下兩點要求:
- 【以學號為名的文件夾中】的目錄下必須有src文件夾
- 在src目錄下必須有名為Main.java文件,且Main.java中包含 public static void main(String[] args) 方法
一個Java項目的示例組織目錄如下所示:
031602111 (文件夾名字為學號)
|- src
|- Main.java(主程序,可以從命令行接收參數)
|- lib.java(包含其它自定義函數,可以有多個,對名字不做要求)
C++
對於使用C++ 語言的項目有以下兩點要求:
【以學號為名的文件夾中】的目錄下必須有src文件夾,在src文件夾中是可在VS2017下編譯運行的解決方案,解決方案的名字必須為 WordCount,一個C++工程示例組織目錄如下所示:
031602111 (文件夾名字為學號)
|- src
|- WordCount.sln
|- WordCount
|- stdafx.cpp
|- stdafx.h
|- WordCount.cpp
|- WordCount.vcxproj
助教在測試時,將自動按照指定編譯環境編譯源代碼,並利用命令行進行批量測試。
錯誤處理
本次自動測試會加入各種各樣出錯情況的測試,要求開發者程序不能崩潰,並且能夠盡可能精確報錯。你可以有“容錯性”的出錯設計,但必須輸出必要的提示或說明。
五、評分規則
博客評分規則
- 在文章開頭給出你所Fork的同名倉庫的Github項目地址。(1‘)
- 在開始實現程序之前,在下述PSP表格記錄下你估計將在程序的各個模塊的開發上耗費的時間。(5‘)
- 計算模塊接口的設計與實現過程。 設計包括代碼如何組織,比如會有幾個類,幾個函數,他們之間關系如何,關鍵函數是否需要畫出流程圖?說明你的算法的關鍵(不必列出源代碼),以及獨到之處。(20‘)
- 計算模塊接口部分的性能改進。 記錄在改進計算模塊性能上所花費的時間,描述你改進的思路,並展示一張性能分析圖(由VS 2017/JProfiler的性能分析工具自動生成),並展示你程序中消耗最大的函數。(3‘)
- 計算模塊部分單元測試展示。 展示出項目部分單元測試代碼,並說明測試的函數,構造測試數據的思路。並將單元測試得到的測試覆蓋率截圖,發表在博客中。(5‘)
- 計算模塊部分異常處理說明。 在博客中詳細介紹每種異常的設計目標。每種異常都要選擇一個單元測試樣例發布在博客中,並指明錯誤對應的場景。(4‘)
- 在你實現完程序之後,在附錄提供的PSP表格記錄下你在程序的各個模塊上實際花費的時間。(2‘)
六、附錄
1. Fork項目並創建文件夾
- 打開鏈接,點擊Fork按鈕,將倉庫 personal-project 拷貝到自己的同名倉庫中,如下圖所示:
- 拷貝成功後,可以看到自己已經擁有了一個同名倉庫
- 在對應語言下創建以學號為名的文件夾,確保所有本地的改動都已 push 後,可以在自己的倉庫中向源倉庫(personal-project)發起Pull Request:
- 點擊Create pull request,提交請求,此後只需等待倉庫主人通過審核後,你的代碼就可以成功合並進源倉庫(personal-project)
- 如在發起 pull request 後代碼發生新的變化,可重復發起 pull request ,助教將合並最新的修改到源倉庫
2.PSP表格
PSP是卡耐基梅隆大學(CMU)的專家們針對軟件工程師所提出的一套模型:Personal Software Process (PSP, 個人開發流程,或稱個體軟件過程)。
PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘) Planning 計劃 · Estimate · 估計這個任務需要多少時間 Development 開發 · Analysis · 需求分析 (包括學習新技術) · Design Spec · 生成設計文檔 · Design Review · 設計復審 · Coding Standard · 代碼規範 (為目前的開發制定合適的規範) · Design · 具體設計 · Coding · 具體編碼 · Code Review · 代碼復審 · Test · 測試(自我測試,修改代碼,提交修改) Reporting 報告 · Test Repor · 測試報告 · Size Measurement · 計算工作量 · Postmortem & Process Improvement Plan · 事後總結, 並提出過程改進計劃 合計
一個功能完備的程序不是一蹴而就的。通過將詞頻統計的需求劃分為4個部分,可將一個大任務劃分為可操作的小任務,同時最好按照任務難度或緊急程度指定各個任務的完成次序。因此,在動手開發之前,要先估計將在程序各模塊開發所需耗費的時間,以及完成整個項目所需的時間,將這個[估計值]記錄下來,寫成PSP 的形式。
PSP的目的是:記錄工程師如何實現需求的效率,和我們使用項目管理工具(例如微軟的Project Professional,或者禪道等)進行項目進度規劃類似。
有關PSP的更多內容,請自行閱讀鄒欣老師的博客
3.Github
請閱讀鄒欣老師的博客:源代碼管理,了解源代碼管理的10個實踐問題。
本次作業要求使用Github進行源代碼管理,代碼有進展即簽入Github。簽入記錄不合理的項目會被助教抽查詢問項目細節。
對代碼簽入的具體要求如下:根據需求劃分功能後,每做完一個功能,編譯成功後,應至少commit一次。本例中,至少應區分基本功能和擴展功能,即分別針對基本功能、擴展功能,編譯成功後,總共至少應commit兩次。具體的功能劃分,請自行定義,並在撰寫博客時體現出來,遵循自己對需求的功能劃分來提交代碼即可。
對Commit不是很熟悉的話,請閱讀阮一峰的博客:Commit message 和 Change log 編寫指南,了解更多細節。
4.單元測試
請根據自己以往積累的測試經驗,在編碼完成之後,提交產品之前,設計測試用例,並編寫單元測試,對自己的項目進行測試。
首先,至少應采用白盒測試用例設計方法來設計測試用例,其他測試方法不限。其次,要設計至少10個測試用例,確保你的程序能夠正確處理各種情況。最後,結合測試評估的要求,對自己的測試設計進行評價,這些測試用例能滿足該程序測試的要求嗎?
另一個重要的措施是要把單元測試自動化,這樣每個人都能很容易地運行它,並且可以使單元測試每天都運行。每個人都可以隨時在自己的機器上運行。團隊一般是在每日構建中運行單元測試的,這樣每個單元測試的錯誤就能及時被發現並得到修改。
推薦閱讀鄒欣老師關於單元測試和回歸測試的博客
福大軟工1816 · 第二次作業 - 個人項目