https://gitee.com/cxf1404445126/PersonalProject-Java/tree/mastergit
PSP2.1 | 我的開發流程 | 預估耗費時間(分鐘) | 實際耗費時間(分鐘) |
---|---|---|---|
Planning | 計劃 | 30 | 40 |
· Estimate | 明確需求和其餘相關因素,估計每一個階段的時間成本 | 30 | 40 |
Development | 開發 | 600 | 680 |
· Analysis | 需求分析 (包括學習新技術) | 60 | 100 |
· Design Spec | 生成設計文檔 | 60 | 50 |
· Design Review | 設計複審 | 30 | 15 |
· Coding Standard | 代碼規範 | 30 | 15 |
· Design | 具體設計 | 100 | 80 |
· Coding | 具體編碼 | 180 | 180 |
· Code Review | 代碼複審 | 30 | 20 |
· Test | 測試(自我測試,修改代碼,提交修改) | 70 | 180 |
Reporting | 報告 | 80 | 90 |
· | 測試報告 | 30 | 30 |
· | 計算工做量 | 20 | 20 |
· | 並提出過程改進計劃 | 30 | 40 |
FIleDeal類中的方法編程
WordDeal類中的方法數組
關鍵函數流程圖ide
public int getCharCount() // 統計文件字符數(ascll碼(32~126),製表符,換行符,) { char c; for (int i = 0; i < text.length(); i++) { c = text.charAt(i); if (c >= 32 && c <= 126 || c == '\r' || c == '\n'|| c == '\t') { charNum++; } } return charNum; }
這個函數是用於統計字符的數量,主要實現方法就是將逐個字符進行遍歷,判斷它是否在有效字符內,若爲有效字符則進行記錄。函數
public int getWordCount() // 統計單詞總數(單詞:以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。) { String t = text; t = t.replace('\n', ' '); t = t.replace('\r', ' '); t = t.replace('\t', ' '); String[] spWord = t.split(" +"); // 對字符串進行分詞操做 for (int i = 0; i < spWord.length; i++) { if (spWord[i].length() < 4) { // 判斷長度是否大於等於4 continue; } else { int flag = 1; // 判斷字符串的前四位是不是英文字母 char[] ch = spWord[i].toCharArray(); for (int j = 0; j < 4; j++) { if (!(ch[j] >= 'A' && ch[j] <= 'Z' || ch[j] >= 'a' && ch[j] <= 'z')) { flag = 0; } } if (flag == 1) { wordCount++; } } } return wordCount; }
這個函數是用於判斷單詞的個數,由於文件中存在換行符製表符等分割符,因此方便分割字符,先統一將它們都替換成了空格,而後以空格爲標誌進行分割,接着按照單詞的要求進行單詞的判斷便可。性能
public int getLineCount() { // 統計有效行數 String[] line = text.split("\r\n"); // 將每一行分開放入一個字符串數組 for (int i = 0; i < line.length; i++) { // 找出無效行,統計有效行 if (line[i].trim().length() == 0) continue; ValidLine = ValidLine + 1; } return ValidLine; }
該函數用於統計有效行數,只須要將文件的每一行進行分割,存入字符數組,而後判斷有無空行,即對應的字符去掉空格後長度是否爲0,而後記錄下有效行數並返回。單元測試
public Map getWordFreq() // 統計單詞詞頻(單詞:以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。) { wordFreq = new HashMap<String, Integer>(); String t = text; t = t.replace('\n', ' '); t = t.replace('\r', ' '); t = t.replace('\t', ' '); String[] spWord = t.split(" +"); // 對字符串進行分詞操做 for (int i = 0; i < spWord.length; i++) { if (spWord[i].length() < 4) { // 判斷長度是否大於等於4 continue; } else { // System.out.println(spWord[i]+" "+spWord[i].length()); int flag = 1; // 判斷字符串的前四位是不是英文字母 char[] ch = spWord[i].toCharArray(); for (int j = 0; j < 4; j++) { if (!(ch[j] >= 'A' && ch[j] <= 'Z' || ch[j] >= 'a' && ch[j] <= 'z')) { flag = 0; } } if (flag == 1) { // 將字符串轉化爲小寫 spWord[i] = spWord[i].trim().toLowerCase(); if (wordFreq.get(spWord[i]) == null) { // 判斷以前Map中是否出現過該字符串 wordFreq.put(spWord[i], 1); } else wordFreq.put(spWord[i], wordFreq.get(spWord[i]) + 1); } } } return wordFreq; }
該函數用於單詞詞頻的統計,以前的部分先按照單詞的判斷方法進行判斷一個詞是不是單詞,而後使用一個Map進行存儲,具體方法爲先判斷Map中以前是否存過該數據,若沒有存過,則將其存入並置value值爲1,若存過,則將該值的value值加1。學習
public List sortMap(Map wordCount) { // 對單詞詞頻的Map進行排序 List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(wordCount.entrySet()); Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) { //對Map中內容進行排序,先按詞頻後按字典順序 if (o1.getValue() == o2.getValue()) { return o1.getKey().compareTo(o2.getKey()); } return o2.getValue() - o1.getValue(); } }); return list; }
這個函數用於對Map詞頻進行排序,這裏使用了Map類型的List來存放詞頻信息,而後經過重寫Comparator以及排序函數來實現相應要求的排序,最後返回排完序的List。測試
此次的單元測試使用了Junit4,由於數量以及類似關係,僅展現WordDeal類中的一個函數的相關測試。編碼
這裏採用的是白盒測試法進行測試,白盒法考慮的是測試用例對程序內部邏輯的覆蓋程度,要求是要儘量的使其覆蓋程度更大一些。
對於白盒法的覆蓋標準有以下幾個:
語句覆蓋:是一個比較弱的測試標準,它的含義是:選擇足夠的測試用例,使得程序中每一個語句至少都能被執行一次。它是最弱的邏輯覆蓋,效果有限,必須與其它方法交互使用。
斷定覆蓋(也稱爲分支覆蓋):執行足夠的測試用例,使得程序中的每個分支至少都經過一次。斷定覆蓋只比語句覆蓋稍強一些,但實際效果代表,只是斷定覆蓋,還不能保證必定能查出在判斷的條件中存在的錯誤。所以,還須要更強的邏輯覆蓋準則去檢驗判斷內部條件。
條件覆蓋:執行足夠的測試用例,使程序中每一個判斷的每一個條件的每一個可能取值至少執行一次;條件覆蓋深刻到斷定中的每一個條件,但可能不能知足斷定覆蓋的要求。
斷定/條件覆蓋:執行足夠的測試用例,使得斷定中每一個條件取到各類可能的值,並使每一個斷定取到各類可能的結果。斷定/條件覆蓋有缺陷。從表面上來看,它測試了全部條件的取值。可是事實並不是如此。每每某些條件掩蓋了另外一些條件。會遺漏某些條件取值錯誤的狀況。爲完全地檢查全部條件的取值,須要將斷定語句中給出的複合條件表達式進行分解,造成由多個基本斷定嵌套的流程圖。這樣就能夠有效地檢查全部的條件是否正確了。
條件組合覆蓋:執行足夠的例子,使得每一個斷定中條件的各類可能組合都至少出現一次。這是一種至關強的覆蓋準則,能夠有效地檢查各類可能的條件取值的組合是否正確。它不但可覆蓋全部條件的可能取值的組合,還可覆蓋全部判斷的可取分支,但可能有的路徑會遺漏掉。測試還不徹底。
根據上面的函數流程圖,設計瞭如下四個文件,使其儘量的覆蓋全部的分支。
** 測試數據:**
測試代碼:
@Test public void testGetCharCount() throws IOException { FileDeal fd = new FileDeal(); String text1 = fd.FileToString("text/text1.txt"); String text2 = fd.FileToString("text/text2.txt"); String text3 = fd.FileToString("text/text3.txt"); String text4 = fd.FileToString("text/text4.txt"); WordDeal wd1 = new WordDeal(text1); WordDeal wd2 = new WordDeal(text2); WordDeal wd3 = new WordDeal(text3); WordDeal wd4 = new WordDeal(text4); int cn1 = wd1.getCharCount(); int cn2 = wd2.getCharCount(); int cn3 = wd3.getCharCount(); int cn4 = wd4.getCharCount(); assertEquals(0, cn1); assertEquals(0, cn2); assertEquals(80, cn3); assertEquals(73, cn4); }
各個函數的測試結果:
效能分析中選用了一篇大小爲2MB左右的英文小說,在測試代碼中重點測試了字符串處理類中的五個函數的運行時間。性能測試的代碼以下:
public class Perf_analy { FileDeal fd; String text1; WordDeal wd1; Map<String, Integer> wf1; List lis1; @Before public void setUp() throws Exception { // 初始化數據 fd = new FileDeal(); text1 = fd.FileToString("LittlePrince.txt"); wd1 = new WordDeal(text1); wf1 = wd1.getWordFreq(); lis1 = wd1.sortMap(wf1); } @Test public void testGetCharCount() throws IOException { // 測試統計字符數量函數 long startTime = System.currentTimeMillis(); wd1.getCharCount(); long endTime = System.currentTimeMillis(); System.out.println("getCharCount函數的運行時間爲" + (endTime - startTime) + "毫秒"); } @Test public void testGetWordCount() throws IOException { // 測試統計單詞數量函數 long startTime = System.currentTimeMillis(); wd1.getWordCount(); long endTime = System.currentTimeMillis(); System.out.println("getWordCount函數的運行時間爲:" + (endTime - startTime) + "毫秒"); } ……………… ……………… ……………… }
最後運行獲得的每一個函數的運行時間以下圖所示:
能夠看出相比於其餘函數,統計單詞個數的函數的運行時間要多得多。因而查看相應函數進行效能的改進:
public int getWordCount() // 統計單詞總數(單詞:以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。) { String t = text; t = t.replace('\n', ' '); t = t.replace('\r', ' '); t = t.replace('\t', ' '); String[] spWord = t.split(" +"); // 對字符串進行分詞操做 for (int i = 0; i < spWord.length; i++) { if (spWord[i].length() < 4) { // 判斷長度是否大於等於4 continue; } else { int flag = 1; // 判斷字符串的前四位是不是英文字母 char[] ch = spWord[i].toCharArray(); for (int j = 0; j < 4; j++) { if (!(ch[j] >= 'A' && ch[j] <= 'Z' || ch[j] >= 'a' && ch[j] <= 'z')) { flag = 0; } } if (flag == 1) { wordCount++; } } } return wordCount; }
對於上面代碼,在改進時考慮到在分詞操做時可使用\\s來進行空白字符的匹配,這樣就能夠省去使用replace()函數所須要的時間,再一個就是在循環判斷前四個字符是否爲英文時使用了效率更高charAt代替了toCharArray(),改進後的代碼以下:
public int getWordCount() // 統計單詞總數(單詞:以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。) { String t = text; String[] spWord = t.split("\\s"); // 對字符串進行分詞操做 for (int i = 0; i < spWord.length; i++) { if (spWord[i].length() < 4) { // 判斷長度是否大於等於4 continue; } else { int flag = 1; // 判斷字符串的前四位是不是英文字母 char c; for (int j = 0; j < 4; j++) { c = spWord[i].charAt(j); if (!(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) { flag = 0; } } if (flag == 1) { wordCount++; } } } return wordCount; }
再次運行效能分析文件,結果以下,能夠看出運行時間上已經有了明顯的減小。
此次的實驗其實暴露出了個人不少不足的地方,以前的做業我通常都是以代碼爲核心,我的習慣一拿到題目就立刻開始寫代碼,但此次要求是要按照流程來進行一步步的操做。最開始的對PSP表格的估計就耗費了蠻多時間,由於以前沒有過按照規範來作的項目,因此對每個模塊應該用多少時間在觀念上仍是比較模糊,最後致使實際和預估時間相差還蠻大的。
在整個做業中錯誤處理和單元測試部分是我以爲最難的部分了,在上面花費的時間也是全部部分中最多的,可是最後的完成的效果仍是不太滿意,對於異常,我一貫是不多會考慮,在寫代碼的時候大部分也是按照理想的輸入狀態來,因此在對異常處理的部分仍是有不少不足。至於單元測試,可能對於測試樣例的設計仍是不太熟悉,因此會有冗餘現象。
不過此次做業給個人收穫仍是很大的,學到了比較規範的項目流程,發現了本身不少細節地方的欠缺,還了解到不少新知識。