軟件工程實踐報告,軟工實踐作業(二)

 2023-12-25 阅读 25 评论 0

摘要:PDFGitHub PSP表格需求分析解題思路代碼規范設計說明 總體設計簡述類圖及流程圖模塊設計 計數模塊 模塊說明類說明 CharCounterWordCounterLineCounterWordsFrequencyCounter關鍵代碼異常處理性能分析單元測試代碼覆蓋率感想參考鏈接PSP表格 PSP2.1Personal Software Process

PDF
GitHub

  • PSP表格
  • 需求分析
  • 解題思路
  • 代碼規范
  • 設計說明
    • 總體設計簡述
    • 類圖及流程圖
    • 模塊設計
      • 計數模塊
        • 模塊說明
        • 類說明
          • CharCounter
          • WordCounter
          • LineCounter
          • WordsFrequencyCounter
  • 關鍵代碼
  • 異常處理
  • 性能分析
  • 單元測試
  • 代碼覆蓋率
  • 感想
  • 參考鏈接

PSP表格

PSP2.1Personal Software Process Stages預估耗時(分鐘)實際耗時(分鐘)
Planning計劃6568
· Estimate· 估計這個任務需要多少時間6568
Development開發510558
· Analysis· 需求分析 (包括學習新技術)150180
· Design Spec· 生成設計文檔3024
· Design Review· 設計復審2011
· Coding Standard· 代碼規范(為目前的開發制定合適的規范)105
· Design· 具體設計3010
· Coding· 具體編碼120158
· Code Review· 代碼復審4026
· Test· 測試(自我測試,修改代碼,提交修改)120144
Reporting報告9065
· Test Report· 測試報告4018
· Size Measurement· 計算工作量2014
· Postmortem & Process Improvement Plan· 事后總結, 并提出過程改進計劃3033
合計665691

需求分析

基本功能點:

  • 程序可通過命令行讀取輸入文件;
  • 程序可統計文件的字符數,具體要求:
    • 只需要統計Ascll碼,漢字不需考慮;
    • 空格,水平制表符,換行符,均算字符
  • 程序可統計文件的單詞數,具體要求:
    • 單詞4:至少以4個英文字母1開頭,跟上字母數字符號2,單詞以分隔符3分割,不區分大小寫
  • 程序可統計文件的有效行數,具體要求:
    • 任何包含非空白字符的行,都需要統計;
  • 程序可統計文件的單詞詞頻,具體要求:
    • 最終只輸出頻率最高的10個
    • 頻率相同的單詞,優先輸出字典序靠前的單詞;
  • 按照字典序輸出結果至文件result.txt,具體要求:
    • 輸出的單詞統一為小寫格式
    • 需按格式5輸出.

非功能性需求:

  • 對三個核心功能統計字符數統計單詞數統計最多的10個單詞及其詞頻進行封裝;
  • 使用Github進行源代碼管理,代碼有進展即簽入Github。根據需求劃分功能后,每做完一個功能,編譯成功后,應至少commit一次;
  • 至少應采用白盒測試用例設計方法來設計測試用例,并設計至少10個測試用例.

備注:

軟件工程實踐報告、1、英文字母:A-Z,a-z;


2、字母數字符號:A-Z, a-z,0-9;

3、分割符:空格,非字母數字符號;

4、例:file123是一個單詞,123file不是一個單詞。file,File和FILE是同一個單詞;

5、輸出格式示例:
characters: number
words: number
lines: number
<word1>: number
<word2>: number
...

解題思路

看到這個題目后,我其實第一想法是用MapReduce....這也算是MapReduce的Hello World了。不過題目是在單機上測試,所以用分布式框架毫無意義(之后應該會補充基于MapReduce的WordCount)。

這次需要實現的功能其實主要是兩個部分:字詞計數文件讀寫。下面進行具體描述:

對于文件讀寫,因為很多計數處理在讀文件時可以一起完成,所以我選擇將文件讀取放進計數模塊中。而寫文件則獨立出來,避免過多功能寫在一起顯得太臃腫。

對于核心的計數模塊,其實字符和行數還是比較好實現的。但在字符計數中也碰到了一個問題,用readLine讀取文件時,無法將換行符讀取進來,更改成read一個一個讀就沒問題了。對于單詞的讀取,我一開始想直接用split進行切分,但又有些擔心正則的效率。。經過測試,最后還是選擇了stringTokenizer進行切分,正則用來匹配。不過官方并不推薦用stringTokenizer,,但簡單切分還是蠻好用的。

軟件工程期末大作業、關于怎么做詞頻排序,我起初想了幾個方案:轉換為list直接sort、建堆、BFPTR加快排。實測BFPTR加快排還是會比堆快一點的。但最終實現時,我還是用了sort,寫起來干凈方便。。其實也是有些地方沒修好,因為很少用java寫算法,所以雖然能跑起來,但中間冗余部分還是有點多,看著非常別扭,于是棄用了。很難說這樣扯出來的代碼性能究竟怎么樣,因為時間有限,所以沒有再進行對比測試,之后修復好還是得多試試。


代碼規范

代碼規范我用的是實驗室的代碼規范:阿里巴巴的碼出高效,并加上了一些補充。


設計說明

  • 總體設計簡述
  • 類圖及流程圖
  • 模塊設計
    • 計數模塊
      • 模塊說明
      • 類說明
        • CharCounter
        • WordCounter
        • LineCounter
        • WordsFrequencyCounter

總體設計簡述

整體由一個計數模塊提供字詞計數功能,分為字符計數、單詞計數、行數計數、詞頻計數四個部分.


類圖及流程圖

類圖

此處輸入圖片的描述

工學交替實踐報告。流程圖

此處輸入圖片的描述


模塊設計

  • 計數模塊
    • 模塊說明
    • 類說明
      • CharCounter
      • WordCounter
      • LineCounter
      • WordsFrequencyCounter

計數模塊

  • 模塊說明
  • 類說明
    • CharCounter
    • WordCounter
    • LineCounter
    • WordsFrequencyCounter

模塊說明

通過傳入文件名,提供統計字符總數、單詞總數、總行數和總詞頻的功能.


類說明

  • CharCounter
  • WordCounter
  • LineCounter
  • WordsFrequencyCounter

CharCounter

(1) countChar(String fileName):long
功能:計算字符數
輸入:fileName:文件名
輸出:文件總字符數


WordCounter

(1) countWord(String fileName):long
功能:計算單詞數
輸入:fileName:文件名
輸出:文件總單詞數


LineCounter

寒假工算社會實踐嗎,(1) countLine(String fileName):long
功能:計算行數
輸入:fileName:文件名
輸出:文件總行數


WordsFrequencyCounter

(1) countWordsFrequency(String fileName):long
功能:計算單詞詞頻
輸入:fileName:文件名
輸出:各單詞詞頻

(2) topTenFrequentWords(HashMap<String, Long> wordMap):ArrayList<HashMap.Entry<String, Long>>
功能:求出頻率最高的10個單詞
輸入:wordMap:各單詞詞頻
輸出:頻率最高的10個單詞


關鍵代碼

詞頻計算器部分,使用StringTokenizer分詞,然后用regex匹配,存入HashMap中,再轉換為ArrayList進行排序。

/*** 詞頻計算器,包括計算文件中各單詞詞頻,只輸出頻率最高的10個.* 頻率相同的單詞,優先輸出字典序靠前的單詞.** @author xyy* @version 1.0 2018/9/12* @since 2018/9/11*/
public class WordsFrequencyCounter {/*** 讀取并計算文件詞頻.** @param fileName 文件名* @return 各單詞詞頻*/public static HashMap<String, Long> countWordsFrequency(String fileName) {InputStreamReader inputStreamReader = null;BufferedReader bufferedReader = null;String in = null;String regex = "[a-zA-Z]{4,}[a-zA-Z0-9]*";String delim = " ,.!?-=*/()[]{}\\\"\\';:\\n\\r\\t“”‘’·——…()【】{}\\0";String word = "";HashMap<String, Long> wordMap = new HashMap<String, Long>(16);//讀入文件try {inputStreamReader = new InputStreamReader(new FileInputStream(fileName));} catch (FileNotFoundException e) {System.out.println("找不到此文件");e.printStackTrace();}if (inputStreamReader != null) {bufferedReader = new BufferedReader(inputStreamReader);}//計算單詞詞頻try {while ((in = bufferedReader.readLine()) != null) {in = in.toLowerCase();//根據分隔符分割StringTokenizer tokenizer = new StringTokenizer(in, delim);while (tokenizer.hasMoreTokens()) {word = tokenizer.nextToken();//匹配單詞if (word.matches(regex)) {if (wordMap.get(word) != null) {wordMap.put(word, wordMap.get(word) + 1);} else {wordMap.put(word, 1L);}}}}} catch (IOException e) {e.printStackTrace();} finally {try {inputStreamReader.close();} catch (IOException e) {e.printStackTrace();}}return wordMap;}/*** 求頻率最高的10個單詞** @param wordMap 各單詞詞頻* @return 頻率最高的10個單詞*/public static ArrayList<HashMap.Entry<String, Long>> topTenFrequentWords(HashMap<String, Long> wordMap) {ArrayList<HashMap.Entry<String, Long>> wordList =new ArrayList<HashMap.Entry<String, Long>>(wordMap.entrySet());Collections.sort(wordList, new Comparator<HashMap.Entry<String, Long>>() {public int compare(Map.Entry<String, Long> o1, Map.Entry<String, Long> o2) {if (o1.getValue() < o2.getValue()) {return 1;} else {if (o1.getValue().equals(o2.getValue())) {if (o1.getKey().compareTo(o2.getKey()) > 0) {return 1;} else {return -1;}} else {return -1;}}}});return wordList;}
}

Main部分,建立線程池,并行運行四個任務,然后輸出至文件。

/*** 主函數類,包括提交計數任務、打印結果.** @author xyy* @version 1.0 2018/9/12* @since 2018/9/11*/
public class Main {public static void main(final String[] args) {ExecutorService executor = Executors.newCachedThreadPool();//計算字符數Future<Long> futureChar = executor.submit(new Callable<Long>() {public Long call() {return CharCounter.countChar(args[0]);}});//計算單詞數Future<Long> futureWord = executor.submit(new Callable<Long>() {public Long call() {return WordCounter.countWord(args[0]);}});//計算行數Future<Long> futureLine = executor.submit(new Callable<Long>() {public Long call() {return LineCounter.countLine(args[0]);}});//計算單詞詞頻Future<ArrayList<HashMap.Entry<String, Long>>> futureWordFrequnency = executor.submit(new Callable<ArrayList<HashMap.Entry<String, Long>>>() {public ArrayList<HashMap.Entry<String, Long>> call() {return WordsFrequencyCounter.topTenFrequentWords(WordsFrequencyCounter.countWordsFrequency(args[0]));}});//輸出至文件try {FilePrinter.printToFile("result.txt",futureChar.get(), futureWord.get(), futureLine.get(), futureWordFrequnency.get());executor.shutdown();} catch (ExecutionException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}
}

異常處理

寒假工實踐報告2000字。對于各個異常情況都會打印異常信息,如讀取文件時,如果找不到對應文件:

try {inputStreamReader = new InputStreamReader(new FileInputStream(fileName));
} catch (FileNotFoundException e) {System.out.println("找不到此文件");e.printStackTrace();
}

性能分析

可見最大開銷來源于多線程并行以及單詞計數部分。

此處輸入圖片的描述

此處輸入圖片的描述

此處輸入圖片的描述


單元測試

暑假工實踐內容及成果,單元測試框架用的是JUnit4。
我總共設計了十一個單元測試,其中Main一個,三個字詞計數部分各三個,詞頻計數部分一個。

單元測試測試項被測試代碼
CharCounterTest分別測試普通字符、換行符和空格CharCounter.java
WordCounterTest分別測試普通單詞、特殊單詞和大小寫單詞WordCounter.java
LineCounterTest分別測試普通行、空白行和混合行LineCounter.java
WordFrequencyCounterTest測試混合單詞WordFrequencyCounter.java
MainTest測試空白文件Main.java

此處輸入圖片的描述


代碼覆蓋率

檢測覆蓋率使用的是IDEA的Coverage,截圖如下:

此處輸入圖片的描述

因為異常處理并沒有單獨提出來,而是當場處理了,所以總的代碼覆蓋率并不高。尤其是功能比較簡單的字詞行計數部分,許多代碼都用來處理讀寫文件異常了。

軟功是什么。此處輸入圖片的描述

此處輸入圖片的描述

此處輸入圖片的描述


感想

這次最大的感想就是差點沒趕上deadline。。雖然時間預估看上去沒有出現太多問題,但這實際上算是用工程質量的下降換來的,有許多地方沒有達到原先預想的水平。因為之前有了幾次做小項目的經驗,所以我很重視需求分析和設計文檔,事前也做了許多學習,但實際上手時,還是遇到比較多的問題。很多問題還是源于我對java編程和各個工具的使用還不夠熟練,特別是異常處理和單元測試部分,非常不滿意。。
也因為還不熟練,很多知識需要當場查閱學習,浪費了很多時間。最后實際編碼時間其實不長,一次編碼中也遺留了一些小問題,到測試時才再一一解決。

通過這次的作業,我也對單元測試有了個大概的理解。之前做測試都是手動編寫一些樣例進行測試,就像做算法一樣。不過比較糟糕的是我是在編碼結束后才編寫單元測試的。。在學習相關內容時,我才了解到單元測試最好在設計時就寫好,或者至少也應該跟程序一起寫了。而且我編寫的單元測試也比較簡單,有許多用法還在學習。

網工和軟工哪個好,還有一點就是對GitHub的使用,其實也是對代碼的管理。我之前是不常用Git的,常常是按自己的習慣在本地進行保存和版本管理。做實驗室的項目時,也沒有很好地利用svn,經常是完成了幾個部分才一起提交,但并這不符合實際軟件工程的要求。而且我還學會了怎么更好地書寫commit message,對比之前慘不忍睹的提交記錄。。。

這次也算是第一次像點樣子的完成了整個軟件開發的工程,深感自己在編碼和時間把控上還非常不足,希望在之后的結對和組隊中能夠有所提高。


參考鏈接

git commit 規范指南
現代軟件工程講義 2 開發技術 - 單元測試 & 回歸測試
在IntelliJ IDEA中查看代碼覆蓋率結果
IDEA 單元測試覆蓋技巧
Java 比較字符串之間大小
BFPRT算法O(n)解決第k小的數
Java的簡單單元測試例子
Java正則表達式的語法與示例
正則表達式匹配解析過程探討分析(正則表達式匹配原理)

轉載于:https://www.cnblogs.com/S031602240/p/9613762.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/194936.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息