這個作業屬于哪個課程 | 2021春軟體工程實踐/S班 |
---|---|
這個作業要求在哪裡 | 軟工實踐寒假作業(2/2) |
這個作業的目标 | 學會使用git和GitHub 重新複習好Java語言基礎知識,完成WordCount程式設計 學會對自己的代碼進行單元測試以及性能分析 |
其他參考文獻 | [第二屆建構之法論壇] 預教育訓練文檔(Java版) 0基礎的git教程 Mac Intellj idea 修改jvm參數 廖雪峰的git教程 正規表達式 ... |
任務一:閱讀《建構之法》并提問
一、提出問題(講義)
問題 1
- 在第五章團隊中的角色和合作中提到“開發項目的時候才覺得實際情況和書上講的都有一些出入,偏偏一些重要的出入書上沒有提。我們很多人是邊看asp.net的書, 邊開發asp.net的項目,這相當于一邊看醫學書一邊動手術。”
- 在我看來這個是初期項目開發者都會遇到的問題,其實在一定程度上确實影響了自身的開發效率,那面對這樣的情況應該要怎麼處理呢?同時技術是不斷的革新的,是以就算技術有一定的長進在日後的開發過程中這樣的情況應該也是沒法避免的。
- 我的觀點是:其實在我看來,就目前的我們來說基本上都是處于邊學邊開發的情況,前期效率低一點應該是無傷大雅的,在不斷的學習過程中增長自己技術,進而也會在學習的過程中不斷的提高自己學習新知識的能力,這樣學習效率就會提高,進而開發效率也會進一步的提高。
問題 2
- 在第五章的團隊不同的投入中作者提到團隊中有一類人為
:“他們有漂亮的羽毛,,能說會道,聯系廣泛,能提出很多建議,很多點子但是他們不執行,除了一些人雲亦雲的觀點和一些關于架構的空談之外,他們沒有其他投入,一旦項目失敗, 他們就會飛到另一個項目中去。”鹦鹉
- 這種類型在團隊其實是很影響隊員效率的,那應不應該适當的提醒她呢?或者應該怎麼處理這樣的情況呢?
- 我的觀點是:假設他完全不了解項目,對于不認可的觀點不予理會就好,不要被他左右,這樣也不會影響自己的項目進度;如果她有一定了解的話,又或許在旁觀者的角度可以提出我們核心人員不一樣的觀點。在我自己的項目經曆中,我們的産品是在每次疊代更新的時候都需要有一些新的點子,對于已經特别熟悉項目的開發人員來說就已經想不出什麼好的點子了,是以這個時候我就會去詢問身邊人的意見,在第一次看我們的産品時有什麼建議,我想這個也是“鹦鹉”這個角色的存在意義吧。
問題 3
- 在第九章創新方面作者提到了兩種如何在一個創新性的市場上後來居上的觀點,分别是“改變遊戲規則”和“轉換目标使用者”。
- 其實我認為“居上”最多也就是幾乎齊頭并進,但是還是無法超越之前的産品,這樣的想法是否正确呢?
- 對于這個觀點,我覺得最好的例子就是拼多多的崛起,在淘寶已經占據了絕大部分市場佔有率的時候,拼多多以一個“砍一刀”的規則重新進入了大衆的視野,同時商品定位比較親民,他利用了大衆心理制定的政策,果不其然,确實很成功。同時,在後續的使用過程中我們也先後發現了其他app也出現過類似的闆塊。這其實就正好契合作者所提到的這兩個創新的辦法,但是在市場上看淘寶還是大部分使用者的第一選擇,因為前者可以向新的創意學習,可能兩者的起點就是不同,但是不可否定的是拼多多的創新确實做的很成功。
問題 4
- 在第七章設計階段分析中作者提到對于需求分析不應該條條框框的完成需求内的要求,但是也不可以一味追求“最大的擴充性”。
- 是以在需求分析和設計的時候我們應該保留什麼樣的彈性呢?
- 我思考了一下之前的項目開發經曆好像基本上主要還是條條框框的按照功能需求去完成了,沒有過多的去考慮比如性能方面的問題,感覺在接下來的實踐中應該更加注重一點拓展性的考慮。
問題 5
- 在七章開發階段的日常管理中作者提到“要盡量減少非開發時間,不要動不動就開“全體會議”。團隊成員們自我時間管理也很重要。”
- 這個全體會議其實是很經典的問題,感覺其實當團隊積極參與度不高的時候,全體會議就顯得很沒有效率,但是有時候又是必要的需要告知大家,或者希望大家參與讨論,應該怎麼處理呢?
- 我的觀點是在一些重要的開發節點可以和隊友集中讨論,有的僅需要部分隊員悉知的隻要小範圍讨論這樣效率更高,一些簡短的公告可能以文字的形式提醒會更有效率。
二、附加題:冷知識
在程式中bug一詞用于技術錯誤。這一術語最初由愛迪生在1878年提出的,但當時并沒有流行起來。在這的幾年之後,美國上将Grace Hopper在她的日志本中,寫下了她在Mark II電腦上發現的一項bug。不過實際上,她說的真的是“蟲子”問題,因為一隻蛾子被困在電腦的繼電器中,導緻電腦的操作無法正常運作。她寫道“這是我在電腦上發現的第一個bug”。
在現在看來,在開發的過程中這個“蟲子”倒也是最令人頭疼的問題...感覺這個文章的冷知識都寫的挺有趣!
參考文獻:
作為程式猿必須知道的十條冷知識
任務二:WordCount程式設計
一、項目Github位址
WordCount項目GitHub位址
二、PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
• Estimate | • 估計這個任務需要多少時間 | 20 | 35 |
Development | 開發 | ||
• Analysis | • 需求分析 (包括學習新技術) | 180 | 240 |
• Design Spec | • 生成設計文檔 | 30 | 45 |
• Design Review | • 設計複審 | 15 | 10 |
• Coding Standard | • 代碼規範 (為目前的開發制定合适的規範) | ||
• Design | • 具體設計 | 40 | |
• Coding | • 具體編碼 | 300 | 370 |
• Code Review | • 代碼複審 | ||
• Test | • 測試(自我測試,修改代碼,送出修改) | 100 | 170 |
Reporting | 報告 | ||
• Test Report | • 測試報告 | 90 | 120 |
• Size Measurement | • 計算工作量 | ||
• Postmortem & Process Improvement Plan | • 事後總結, 并提出過程改進計劃 | ||
合計 | 850 | 1150 |
三、解題思路描述
1、git初使用
閱讀了一邊作業需求後,首先要做的就是對git的使用和學習,首先由于原先沒有具體使用過GitHub,剛剛開始入手學習的時候還是有一點吃力,主要是詳細閱讀了廖雪峰的git教程以及查閱各種資料熟悉git的使用方法,然後根據作業的步驟一步一步了解相關指令完成基礎的git使用的學習,這樣項目才開始步入正軌,最後在使用中感覺教程中所推薦的SourceTree軟體來進行檔案的管理和送出很便捷,使用的時候不用通過再指令行指令操作,更加友善。
2、WordCount編碼思路
- 在閱讀了項目需求後首先先确定了一下自己所要使用的語言,感覺Java語言對字元串的處理更加靈活,是以最後使用了Java語言來編寫。
- 接下來總結該項目主要需要實作一下功能:
- 要實作檔案的讀寫功能;
- 統計檔案字元數;
- 統計檔案的單詞總數;
- 統計檔案的有效行數;
- 統計檔案中各單詞的出現次數。
- 同時,在作業中提示到一個好的項目應該實作接口的封裝,是以我就思考可以根據功能對功能進行劃分,封裝成IO工具類以及統計工具類,主程式單獨用一個類進行測試,來編寫程式。
- 接下來就是根據劃分好的子產品,開始逐漸編寫上述的功能。
四、代碼規範制定連結
codestyle.md
五、設計與實作過程
1、類的設計
- 首先,設計了對IO讀寫進行了封裝,實作兩大功能:
- 實作從檔案中讀取字元流并轉化為字元串存儲,這樣更加友善後續對檔案中的資料進行統計,以字元流進行讀取則保證了計數的正确性;
- 實作将結果輸出到檔案的功能,參數中的資料主要通過計數工具類中的計數方法擷取得到。
/* IO工具 */
public class IOTool {
/* 從檔案讀取字元流 */
public StringBuilder fileInputToString(String filePath) throws IOException {}
/* 将結果輸出到檔案中 */
public void OutputToFile (String filePath, int sumCharacter, int wordsNum, int lines, List<Map.Entry<String,Integer>> list) throws IOException {}
}
- 接下來對計數的功能封裝成
CountTool類
,來實作對檔案中的資料的統計。
其中在編寫代碼的過程中,統計單詞數和統計頻次部分都需要對字元串進行相同的處理(篩選出有效的單詞,加入字元數組),是以為了提高代碼的重用性将該功能單獨寫成一個方法。
/* 用于字元統計工具 */
public class CountTool {
/* 統計總字元數 */
public int characterCount(StringBuilder str) {}
/* 統計單詞數 */
public int wordsCount(StringBuilder str) {}
/* 轉化成小寫且用空格分割字元串,傳回字元串數組 */
public String[] changeStr(StringBuilder str) {}
/* 統計有效行 */
public int invaluableLines(String filePath) throws IOException {}
/* 統計單詞頻次 */
public HashMap<String, Integer> wordsSortCount(StringBuilder str) {}
/* 對哈希表進行排序 */
public List<Map.Entry<String,Integer>> sortMap(HashMap<String,Integer> map) {}
}
- 兩個類之間函數的關系如下:
- 首先,通過IO類中的
方法從檔案中讀取字元流轉化為fileInputToString
類型的字元串;StringBuilder
- 然後将字元串傳入計數工具類中的各個方法進行統計,并傳回結果;
- 最後将統計結果通過參數傳入
方法,輸出到檔案。OutputToFile
- 首先,通過IO類中的
2、關鍵性函數的具體實作思路
- IO方面
(1)為了統一檔案的編碼為
UTF-8
,在編寫檔案字元流的讀寫的時候,注意設定了編碼格式。
//UTF-8編碼進行讀取
FileInputStream file = new FileInputStream(filePath);
InputStreamReader reader = new InputStreamReader(file,"UTF-8");
(2)同時,在輸出的時候将
String類型
轉碼成UTF-8格式進行輸出
//轉碼成UTF-8
String context = "..."
...
byte[] bytes = context.getBytes("UTF-8");
file.write(bytes);
- 統計方法實作
(1)統計檔案的字元數
根據題目要求“隻需要統計Ascii碼,漢字不需考慮”,剛剛開始編寫的時候想的還複雜了,還将字元串轉化為字元數組,然後周遊累加計算字元數,在調試的時候發現,上述的代碼是多餘的,其實總數就是按字元流讀取後傳回字元流的長度。是以僅需要以下語句即可以實作計數功能:
/* 統計總字元數 */
public int characterCount(StringBuilder str) {
//傳回字元流的個數
return str.length();
}
(2)統計檔案的單詞總數
該功能最關鍵的就是在于如何從字元串當中提取有效的單詞,并将它進行分割計數,剛剛開始對于有效單詞的篩選感覺實作起來有點複雜,在構思的時候感覺有好幾個
if語句
在腦海裡閃過,感覺很容易判斷出錯。
後來,在同學的提點下知道可以使用正規表達式去篩選滿足要求的字元串,由于之前對該知識還沒有使用過,于是就查閱了正規表達式的資料,得出正确的單詞形式應該滿足正規表達式為
[a-z]{4}[a-z0-9]*
。
最後功能的實作思路如下:
由于最後輸出的形式為小寫,是以第一步先把字元串全部轉化為小寫;
接下來用正規表達式
[^a-z0-9]
将不是字母和數字的符号都換成空格;
然後利用
split
方法,以空格為分割符号把字元串進行分割;
最後就用有效單詞的正規表達式對進行篩選和統計即可。
(展示對字元串進行處理的方法,該方法詞頻統計也需要使用到)
/* 轉化成小寫且用空格分割字元串,傳回字元串數組 */
public String[] changeStr(StringBuilder str) {
//轉化為小寫
String result = str.toString().toLowerCase();
//利用正規表達式篩選出不是數字和字母的符号,轉化為'.'
String regex = "[^a-z0-9]";
result = result.replaceAll(regex, " ");
//用空格來分割單詞
String[] resultArray = result.split(" ");
return resultArray;
}
(3)統計檔案中各單詞的出現次數
單詞的詞頻統計關鍵也是在于分割單詞并存儲,是以和上述統計單詞數的方法一樣也是先調用
changeStr方法
來分割單詞,由于是鍵值對的形式是以使用
HashMap
來存,然後就是理清修改
HashMap
的值和加入新的鍵值對的邏輯。
在調試的時候發現在這裡兩次出現了邏輯錯誤,主要就是對是否加入哈希表判斷位置出錯。
(4)有效行的統計
由于要對行進行統計,是以讀取字元流就判斷起來就有點繁瑣,是以在該方法又重新按行從檔案中讀取資料,再次利用正規表達式
\\s*
判斷該行是不是空白行,再進行記錄。
//記錄有效行數(任何包含非空白字元的行,都需要統計)
//利用正規表達式指代空白字元
String regex = "\\s*";
//統計行數
while ((line = br.readLine()) != null) {
sumLines++;
//判斷是不是空白字元行
if (line.matches(regex)) {
sumLines--;
}
}
六、性能改進
1、拼接字元串性能提升
因為程式需要對字元串進行拼接,在測試中發現利用
StringBuilder
進行字元串的拼接會對程式的性能有一定程度的提升,同時主要是當時測試的時候跑10000字元的資料,出現了記憶體溢出的現象,是以對資料類型把資料類型從
StringBuffer
改成了
StringBuilder
,同時也測試了直接用String的方法,同樣的資料已經跑不動了,電腦的風扇不停轉,顯然性能太差。
最後用測試跑10w字元的項目時間大約在
500ms
左右。
2、IO操作
如果多次對檔案進行讀取會影響程式的性能,是以程式對IO工具進行了封裝,讀取一次檔案轉化成字元串,其他函數直接對字元串處理會大大提高程式的效率,但是由于編寫的讀檔案的功能是按字元流讀取,是以在計算有效行的方法上就不太友善,是以在計算有效行的時候,我又按行讀取了一次檔案。感覺對性能會有影響,但是目前還沒想到怎麼直接用字元串判斷有效行的方法。
/* 統計有效行 */
public int invaluableLines(String filePath) throws IOException {
//UTF-8編碼進行讀取
FileInputStream file = new FileInputStream(filePath);
InputStreamReader reader = new InputStreamReader(file, "UTF-8");
BufferedReader br = new BufferedReader(reader);
...(具體實作)
}
七、單元測試
1、字元統計測試
主要是為了測試在大資料情況下程式性能和正确性,同時在測試的字元串中加了很多符号來判斷代碼的正确性。
@org.junit.Test
public void characterCount() {
String str = "shjdu\t\r\nhsj[]ks";
StringBuilder strTest = new StringBuilder();
for (int i = 0; i < 10000000; i++) {
strTest.append(str);
}
CountTool count = new CountTool();
int result = count.characterCount(strTest);
Assert.assertEquals(result,str.length()*10000000);
}
2、單詞數統計測試
@org.junit.Test
public void wordsCount() {
//有5個合法的單詞
String str = "aaa=-aAaa123 kkkk12??\n\taaaa12?\rfile?123file\naaAaa123";
StringBuilder strTest = new StringBuilder();
for (int i = 0; i < 10000; i++) {
strTest.append(str);
}
CountTool count = new CountTool();
int result = count.wordsCount(strTest);
Assert.assertEquals(result,50000);
}
測試結果

3、行數和詞頻測試
因為行數需要傳入檔案,以及詞頻是顯示輸出,是以直接進行統一的測試的展示,可以看到對空行進行了忽略,同時隻會展示前10的單詞。
寫的測試例子如下:
windows95 windows98 windows200
123file flie123
windows95 windows98][]???aaa123 file123 bbbb cccc
Dddd fffff ggggg vvvvvv fffff jjjjjj kkkkk
4、覆寫率截圖
- 當測試的有效單詞數未超過10個且沒有空白行的時候,測試的覆寫率為98%;
- 主要是由于要求僅輸出10個單詞,是以加了判斷語句,則未到10個的時候,則不會執行。
//判斷單詞數是否等于10了
if (num == 10)
break;
- 對空白行進行判斷,則沒有空白行的時候不會進行下述第二個語句
//判斷是不是空白字元行
if (line.matches(regex)) {
sumLines--;
}
- 有效單詞數超過10個的時候,且有空白行,測試覆寫率100%。
八、異常處理說明
主要的異常處理是針對檔案的IO方面,系統自動抛出異常,其餘就沒有過多的判斷。
九、心路曆程與收獲
首先,在看到本次作業的時候,就感覺有一點一頭霧水,不知道該如何下手,在反複閱讀了幾遍作業要求之後,根據作業的步驟才慢慢有了思路。最難開頭的還是學會對于git和GitHub的使用,因為之前都沒接觸過git,各種指令和安裝操作有點繁瑣,在操作的過程中還出現了一些問題,經過各種查資料以後才終于弄懂了git,最後在摸索中感覺
SouceTree軟體
來管理确實很友善。同時在使用的時候也感覺到git在後續的團隊項目環節也起到了很重要的作用,之前寫在團隊寫一些小項目的時候,我和我隊友也感覺人工合成項目代碼實在是太繁瑣并且不可取的工作了,是以對于git的學習還是受益匪淺的。
接下來就是對于項目的編寫,太久沒有用java了,對于有點知識點還有一點遺忘了,是以正好通過這次的項目重新複習了一遍java,也為當時對字元串這一塊的學習查缺補漏了一下。
同時第一次學習對項目進行了單元測試和性能測試,在這個過程中也更加直覺的感受到不同資料類型的優缺點。
這次作業雖然過程中有很多困難,但是總體來說還是收獲頗豐,對項目開發有了更完善更全面的認識,也為後續的工作打好了基礎。