這個作業屬于哪個課程 | 2021春軟體工程實踐¦S班 (福州大學) |
---|---|
這個作業要求在哪裡 | 軟工實踐寒假作業(2/2) |
這個作業的目标 | 閱讀《建構之法》并提出問題、使用GitHub送出項目、完成WordCount程式并測試 |
作業正文 | 正文 |
其他參考文獻 | 部落格園、CSDN、簡書、百度百科 |
GitHub連結
項目連結
目錄
- part1:閱讀《建構之法》并提問
- 問題1
- 問題2
- 問題3
- 問題4
- 問題5
- 附加題
- part2:WordCount程式設計
- Github項目位址
- PSP表格
- 解題思路
- 代碼規範制定連結
- 設計與實作過程
- 類
- Lib類中的函數
- 性能改進
- 單元測試
- 測試統計字元數函數
- 測試統計單詞與提取頻率最高的10個單詞函數
- 測試提取頻率最高的10個單詞
- 測試有效行函數
- 覆寫率截圖
- 異常處理說明
- 心路曆程與收獲
在書中61頁,作者用魔方的例子講訴了“精通”二字的含義。
但我們真的有必要精通一門程式設計語言嗎,以下摘自知乎平台的一份回答。
精通一門語言的最直接方法是研究和解析目智語言的編譯原則。想精通太難,而且作為一個大學畢業的學生,執着于精通一門語言其實意義并不是很大。
在我過去三年的學習中,不要說精通,就連掌握一門語言的标準都沒有達到。拿java來說,作為這次部落格作業的程式設計語言,我花了不少時間去重新學習了map,hashmap,treemap三種資料結構對應的方法,以及他們之間的關系。而精通java不僅要了解底層實作,還要有廣闊的技術視野,想必要花很多時間精力去學習。
我認為不必追求精通一門程式設計語言,但要不斷擴充自己的“學習區”,強化基礎知識,打牢基礎。
在書中93頁,作者主張在最外層,即行為和後果層給與他人回報。作者的理由是前兩層回報難以改變,而最外層的行為和後果是可以被改正和彌補的。
此外,作者也提到三明治式的回報是容易被人接受的。
但我認為這種方式有時不足以引起他人的重視。雖然三明治式的回報确實讓人容易接受,但也容易讓别人不以為然,認為他犯的的錯誤隻是小問題,拿過去的一次合作經曆說,有些隊員喜歡“劃水”,隊長不斷地催促他們完成自己的任務,最後上交的成果并不好。如果給與他人的回報,大多都在評論别人三種層次中的行為後果層,他或許隻是這一次改正,但再次出現問題時,他還是會重蹈覆轍,是以我會考慮進一步在行為動機層上告訴他問題所在,這樣會不會取得更好的效果呢?
在看第八章需求分析的時候,我突然萌生了一個問題,這裡的需求與軟體的發展有沖突怎麼辦?
例如手機上有一個誇克浏覽器,過去我因為其簡潔的界面一直把它當作手機浏覽器的首選,但是在以後的更新中,誇克浏覽器不但内置了語音助手,雲存儲,甚至有了它自己的ui。當然,浏覽器多功能化是它的發展趨勢,但與此同時也會與使用者需求相沖突。在視訊網站bilibili上我也多次看到,許多up主吐槽誇克浏覽器“忘記初心”,而且解除安裝了該浏覽器。如果說軟體的發展會影響對該軟體的需求,那我們應該怎樣平衡這種沖突呢?
26頁講述了單元測試的一些标準,好的單元測試在覆寫率,或是一些邊緣,異常情況下,都要有好的表現。正如書中所說,代碼覆寫率100%也不等同于100%正确。
但單元測試可否幫助提高程式的性能呢?還是說單元測試僅僅是用于驗證正确性?
單元測試中有時間工具,可判斷程式運作時間,有時我也會借此來估計程式的運作速度是否達到預期,而且在單元測試中,一些問題可能是由于資料結構不合理引發的,這時可以通過優化資料結構,在提高正确性的同時也提高了程式性能。
在49頁,作者提到了軟體工程師成長的第一點,是積累軟體開發相關的知識,提升技術技能。其中包括一些程式設計語言,裝置驅動程式,核心調試器的掌握以及對某一開發平台的掌握。
但是經過了大學三年,我發現自己好像學的太少了。對裝置驅動程式和開發平台沒有過多的了解,太久沒用的程式設計語言也會忘記一些知識。是以我對于第一階段的學習還遠沒有結束,我也不清楚自己要掌握了哪些具體技術之後,才算完成了初級軟體工程師的第一階段,以及我需要花費大概多少時間來學習這些技能。畢竟大四面臨畢業,怎樣在有限的時間内讓自己在軟體工程的領域内掌握足夠的知識?
藍牙的名字來源于10世紀丹麥國王Harald Blatand——英譯為Harold Bluetooth(因為他十分喜歡吃藍梅,是以牙齒每天都帶着藍色)。在行業協會籌備階段,需要一個極具有表現力的名字來命名這項高新技術。行業組織人員,在經過一夜關于歐洲曆史和未來無限技術發展的讨論後,有些人認為用Blatand國王的名字命名再合适不過了。Blatand國王将現在的挪威,瑞典和丹麥統一起來;他的口齒伶俐,善于交際,就如同這項即将面世的技術,技術将被定義為允許不同工業領域之間的協調工作,保持着個各系統領域之間的良好交流,例如計算,手機和汽車行業之間的工作。名字于是就這麼定下來了。
PSP2.1 | Personal Software Process Stages | 預估耗時 | 實際耗時 |
---|---|---|---|
Planning | 計劃 | 1h | 0.5h |
• Estimate | • 估計這個任務需要多少時間 | ||
Development | 開發 | 8h | |
• Analysis | • 需求分析 (包括學習新技術) | 3h | 2h |
• Design Spec | • 生成設計文檔 | 15min | |
• Design Review | • 設計複審 | 30min | |
• Coding Standard | • 代碼規範 (為目前的開發制定合适的規範) | ||
• Design | • 具體設計 | ||
• Coding | • 具體編碼 | 4h | 4.5h |
• Code Review | • 代碼複審 | ||
• Test | • 測試(自我測試,修改代碼,送出修改) | 1.5h | |
Reporting | 報告 | ||
• Test Repor | • 測試報告 | ||
• Size Measurement | • 計算工作量 | ||
• Postmortem & Process Improvement Plan | • 事後總結, 并提出過程改進計劃 | ||
合計 | 25h | 26h |
- 輸入方式:指令行
- 輸入内容:兩個文本的路徑(相對路徑或絕對路徑),其中,前一個文本為待讀取文本,後一個文本為待輸出文本
- 輸出内容:待輸出文本中的内容,包括:
- 統計字元數
- 統計單詞數
- 統計有效行數
- 統計最多的10個單詞及其詞頻
首先我先不考慮用指令行的輸入方式,而是手動輸入兩個檔案的路徑。輸入檔案路徑後,下一步要讀取文本并對文本進行分析統計,讀取文本不難,可建立一個用于打開檔案并分析檔案的類,在該類中寫一些靜态的方法(實踐中發現建立靜态方法并不是最優選擇)。下面對4種統計要求進行分析:
- 統計字元數:可在打開檔案讀取内容時順帶執行
- 統計單詞數:使用正規表達式比對(實踐中發現效率不高。已舍棄)
- 統計有效行數:使用正規表達式比對(實踐中發現效率不高。已舍棄)
- 統計最多的10個單詞及其詞頻:利用統計單詞數時的資料進行排序
最後将統計結果按照規定格式寫進輸出文本即可。
代碼規範
-
Wordcount
用于擷取指令行的文本路徑,主函數的運作以及調用Lib類。
-
Lib
用于讀寫文本并對文本内容進行分析統計。
- readFileByChars() —— 打開檔案并統計字元數,單詞數,有效行數,并儲存單詞及其頻率
利用FileReader逐個讀入字元并統計字元數
fr = new FileReader(inputFilePath);
int ch;
while ((ch = fr.read()) != -1) {
charsNumber++;
由于單詞至少有4個字母開頭,且之後可跟數字,是以可了解為單詞的開頭是4個字母,單詞是由于非字母數字結束的(單詞不會因為數字而結束,因為小于4個字母開頭構不成單詞),基于此設計算法。
單詞的判斷算法:
- 設定一個暫存字元串,規定暫存字元串隻有三種類型:為空,由1~3個字母組成,為一個單詞。(判斷暫存字元串是否為單詞的方法:長度是否大于三)
- 不斷讀入字元,判斷該字元的類型。
- 字元類型為字母,加入暫存字元串。
- 字元類型為非字母非數字,清空暫存字元串,判斷暫存字元串是否為單詞,若是則記錄。
- 字元類型為數字,判斷暫存字元串是否為單詞,若不是則清空暫存字元串,若是則加入暫存字元串。
- 讀入字元結束,判斷暫存字元串是否為單詞,若是則記錄。

添加單詞進hashmap(需判斷其是否存在)
if (tempWord.length() > 3) {
//若沒有則添加,若有則加一
word = tempWord.toString().toLowerCase();
wordsCount.merge(word, 1, Integer::sum);
由于換行一定有一個Enter鍵,隻需判斷在兩個Enter鍵之間是否出現過非空字元,基于此設計算法。
有效行的判斷算法:
- 設定一個布爾數a為假,代表該行沒有非空字元。
- 若字元不為回車,也不為空白字元,且該行沒有非空字元,則将a置為真。
- 若字元為回車,判斷該行有無非空字元,若有則有效行加一,不管結果如何a都置為假。
- 讀入字元結束,判斷該行有無非空字元,若有則有效行加一。
if (ch == 10) {
if (isValued) {
lineNumber++;
}
isValued = false;
} else {
if (!isValued && !isBlankCharFuc(ch)) {
isValued = true;
}
}
-
countWords() —— 對單詞按照頻率進行排序
利用stream可以同時實作:
- 按頻率排序
- 再按key值大小排序
- 截取前十個元素
wordsCount = getWordsCount()
.entrySet()
.stream()
.sorted(Map.Entry
.<String, Integer>comparingByValue()
.reversed()
.thenComparing(Map.Entry.comparingByKey()))
.limit(10)
.collect(Collectors
.toMap(Map.Entry::getKey
, Map.Entry::getValue
, (e1, e2) -> e2
, LinkedHashMap::new));
-
writeFile() —— 寫入檔案
周遊hashmap
for (Map.Entry<String, Integer> entry : wordsCount.entrySet()) {
outputString.append(entry.getKey())
.append(": ")
.append(entry.getValue())
.append('\n');
}
利用BufferedOutputStream寫入檔案
fos = new FileOutputStream(outputFilePath);
bos = new BufferedOutputStream(fos);
//設定編碼為utf-8
byte[] bytes = outputString.toString().getBytes(StandardCharsets.UTF_8);
bos.write(bytes, 0, bytes.length);
- getCharsNumber() —— 擷取字元數
- getWordNumber() —— 擷取單詞數
- getLineNumber() —— 擷取有效行數
- 一開始我想用靜态方法實作這些函數,但是每個方法都要傳遞參數,有的甚至要四五個參數,例如寫檔案時要給函數檔案路徑,單詞數,有效行數等等,而且靜态函數有時需要傳回多個值,綜上考慮後舍棄靜态方法,而是在類中添加了若幹成員變量。
- 将打開讀取檔案與統計字元,統計單詞,統計有效行一起執行,做到隻讀取一次檔案内容。之前我是把檔案内容存進String中,後來想到,萬一這個String很大很大,在統計單詞與有效行的時候需要一遍又一遍地周遊,十分花時間,幹脆隻讀取一次。
- 舍棄正則比對有效行數與單詞數。利用算法可以在讀取字元時順帶統計。
- hashmap排序的stream用法。
由于不考慮中文,僅用0~127号ascii碼測試,結果為128,符合預期
StringBuilder text= new StringBuilder();
for(int i=0; i<128; i++) {
text.append((char) i);
}
測試統計單詞:設計了一個字元串,其中包含5個合法單詞,并讓字元串重複添加五遍,總單詞數為25,符合預期
StringBuilder text= new StringBuilder();
String words="123a;a15;A81qqs;axSx1651;1888asadw;QDWQ156;1535;awwwwc;ACWa;acwa\n";
for(int i =0; i < 5; i++) {
text.append(words);
}
測試了單詞全小寫,相同頻率單詞的排序,結果符合預期
String[] answerKey={"acwa", "awwwwc", "axsx1651", "qdwq156"};
Integer[] answerValue = {10, 5, 5, 5};
int i=0;
for (Map.Entry<String, Integer> entry : testLib.wordsCount.entrySet()){
Assert.assertEquals(entry.getKey(), answerKey[i]);
Assert.assertEquals(entry.getValue(), answerValue[i]);
i++;
}
測試包括多行空行,單行空行,單行中僅包含空白字元,結果為4,符合預期
String text="\ncs\n61561\n\n\n156\n\t\n615\n\n";
提高覆寫率方法:可通過對各個函數都進行測試,有分支的情況如if需要将每種情況測試一遍。
包含兩個部分:指令行傳遞參數不為2,檔案讀寫錯誤
- 若參數不為2則退出程式:
if (args.length != 2) {
System.out.print("該程式隻接受兩個參數,待讀取檔案與待寫入檔案,程式關閉。。。");
System.exit(0);
}
- 待讀寫檔案不存在則顯示錯誤資訊
catch (IOException e) {
e.printStackTrace();
System.exit(0);
}
- 這次作業相較于上次作業來說,工作量明顯增加了,作業的要求寫得十分詳細,但即便如此我做起來仍然有些費勁,感覺把java重新學了一遍。
- 之前我沒有這麼系統的用github送出一個項目,這次作業加深了我對項目管理的認識。
- 重新讀了一遍《建構之法》,對軟體工程的了解更加深刻了,但還沒有用于實踐,希望能夠盡快學以緻用。
- 我之前用的ide是eclipse,現在換成了idea,對很多功能不是很了解,例如JUnit4的使用,通過這次作業讓我學習很多。