寒假作業(2/2)
這個作業屬於哪個課程 | 軟件工程2021春軟件工程W班(FZU) |
---|---|
作業目標 | 1.閱讀《構建之法》並提問;2.完成詞頻統計個人作業 |
作業要求 | 作業鏈接 |
參考文獻 | csdn,java教程 |
任務一:閱讀《構建之法》並提問
代碼量是否等於樹葉量?
看了該篇文章后,我對於文章中對於代碼量問題老師給出的只要是能夠解決問題就是一個好的課題持疑惑態度,我認為一個好的即使簡略的項目要實現的功能也是需要較大的代碼量來維護的,文章中所給的老師所講的一個故事,說一顆樹的樹葉比作代碼量,有一棵樹上長滿果實,一棵樹上是長滿果實樹樹葉的三倍。我認為這是不太現實的。到開發階段,我認為一個好的編程人員都會有意識的去減少自己做重復的事件,也就是說代碼的復用等。對於文章所說長滿樹葉的樹我認為只能在新手階段能夠看到。我認為能夠進行開發軟件,設計課設的階段的人不會犯這樣的問題。因此直接將樹葉必做代碼量,我認為較為籠統,應說為樹葉是無效代碼量我認為更加合適。
普通的團隊開發要如何做到高效的代碼復審?
在閱讀了第三點團隊合作的幾篇文章后,我認為結對項目中,每一行代碼都由兩個人思考過,且雙方都十分熟悉代碼,所以不必過多的介紹就可以開始進行細致的復審。但是在多人團隊合作中,往往每個人都是負責一個模塊,在個人編程習慣不同下,代碼形式或者風格可能也較為不同,對於復審者來說,是一件非常復雜的事,如果是通過問問題方式來做,可能並不能夠了解代碼細節,很難發現邏輯上的錯誤及優化的空間等等,如果讓復審者去深入了解,又是不太現實的一件事情。往往在校園團隊合作上,時間統一更是一件較為復雜的事情,對於代碼的復審效率提高是一件讓我困惑的事情,照目前我得想法是只能夠弱化復審者的要求,讓自身模塊的編程人員也參與其中。才能做到較為高效率的代碼復審,但是往往bug不易讓自身找到,是一把雙刃劍。
如何去讓用戶能有更好的體驗?
閱讀了用戶體驗和用戶界面后該文章后,我對於如何去讓用戶能夠有更好的體驗感有了更進一步的認識,從開發者即我得角度來看,首先在開發一個功能時,我們需要提前去對於用戶會如何使用進行一個規划,如通過調查等方式或者自身和隊友來考慮等,讓用戶使用時候不會覺得這個功能略顯"幼稚",其次就是在於對界面的ui設計,不要為了不無目的的美觀而美觀,往往過度美化會適得其反,可以在測試階段先讓人員進行使用,看看體驗感如何,ui讓人覺得美觀舒服才是最重要的。最后就是對於功能的測試,測試要盡量完善,勿讓用戶體驗時遇到一些bug是我們應該去努力的方向。
團隊有部分人不做事時候,我們應該怎么調動人員情緒來做事?
在閱讀了其實還是人的問題這篇文章后,我對於老師在對於人的分類時把人分成了{做事的, 不做事的,不讓別人做事的,P4=做假的事的,P5=假裝做事的}大有所悟。在團隊分配時,不可必要的並不是每個人都是團隊角色中的"豬",對於所有事都是百分百的去努力完成,對於這五種分類,我們應該讓自身成為做事的人。並且在一個團隊中,如果有人是其余分類的人,我們應該如何去讓他向我們看齊呢,這是我在閱讀了后所提出的問題,對於這個問題我認為我在自身的經歷上來說,我得解答是讓他跟我一起辦事,以我的積極去帶動他的積極,這樣來讓其成為第一類人。我也會在后續更加的去認識和體會這一方面的解決方案。
一個團隊在創造階段才能產生一些有意義的東西么?
在團隊合作階段文章中提到的創造階段才能產生有意義的東西,這一點思路我覺得有一些異議。一個團隊從建立起來開始,就是一個創造的過程。萌芽階段成員互相認識,適應環境,承擔角色以及日常流程等,磨合階段成員們開展討論與構思,創造出更多想象和擴展的空間。規范階段則是完善代碼,完善能力。前三個階段所做的每一步其實都是創造的過程,也沒有人能夠否認這些階段是沒有意義的。所以不僅僅是創造階段成員們所做出的東西是有意義的,任何時候任何階段,成員們辛苦的付出和努力都是在創造有意義的東西,只不過創造階段顯現出來的東西更多。意義對於我而言我認為有過程就存在着意義,並非區分過程中的每個階段來看待有無意義。
附加題:
(來源於對bug詞義的意思起了興趣,很早的時候所查的一個故事)
美國的艾肯博士研制出了馬克2號計算機,在研制過程中,誕生了一個新詞“debug”來表示排除計算機故障,他的出現是這樣的:在盛夏的時候,美國水上研究中心使用馬克-Ⅱ計算機進行數據處理時,經常停止工作,其原因是,由於天氣炎熱加上機房無空調設備,致使大量飛蛾在機房中亂飛,這些飛蛾飛到正要閉合的繼電器觸點之間被繼電器觸電夾住,導致電路中斷,造成工作故障。只需要將飛蛾找出拿掉,就可以正常工作,因為飛蛾的英文是bug,所以也使得bug成為了代名詞。
任務二:設計一個程序,能夠滿足一些詞頻統計的需求
項目地址
Github解題思路描述
原先想用python做,能夠用到更加便捷的方法去使用,后面以為只能java/c++選擇了java,因為書沒帶上網查了一些技術的教程網站進行了學習和復習來完成課題。后續將所有功能設計成了一個工具類來進行調用。
首先拿到題目的時候,審了下題,將題目分成了幾個部分:
1.統計文件的字符數
2.統計文件的單詞總數
3.統計文件的有效行數
4.統計文件中各單詞的出現次數
5.文件存取
代碼規范
規范地址計算模塊接口的設計與實現過程
將所有功能合成一個工具類Utils進行使用,后續設計api接口時候可以進行搬運調用,並且寫了一個生成測試文本的程序用來測試。項目結構為:
核心功能部分代碼
根據遍歷來統計字符數
思路:一開始的想法是使用java中的正則表達式來匹配ASCII碼,后續完成后在無意間發現通過readline會導致換行丟失且出現錯誤,后續經過驗證后對自己的代碼進行了修改,在該函數中進行了文件的read操作,並計數進行統計。
BufferedReader bufferedReader = null;
try{
String pre_path=new File("").getAbsolutePath();
bufferedReader = new BufferedReader(
new InputStreamReader(new FileInputStream( pre_path+ "\\" + filepath),"utf-8"));
while((bufferedReader.read())!=-1)
{
char_num+=1;
}
}
catch(...)
if(bufferedReader!=null){
bufferedReader.close();
}
return char_num;
}
統計文件的單詞總數
思路:題意為獲取為字母開頭且長度大於4的單詞數,使用正則表達式匹配。首先通過String類自帶的split方法去拆開整篇文章使其形成一個數組,然后通過正則表達式去選擇出正確的單詞並作統計。
// 構造正則表達式,去根據空格拆分整篇文章
// temp字符串數組將保存所有的單詞
//處理特殊字符,以免被誤以為是單詞的一部分
String[] temps = words.toString() .split("[^a-zA-Z0-9]");
// 構造題意:以字母開頭且長度大於4的單詞
String regexs = "^[a-zA-Z]{4,}.*";
// 循環遍歷這個數組,利用正則表達式去匹配
for (int i = 0; i < temps.length; i++) {
if (temps[i].matches(regexs)) {
// 匹配成功,計數加1
word_num++;
//System.out.println(temps[i]);
}
}
return word_num;
統計文件的有效行數
思路:想通過獲取文件中非空行的方式來進行統計,使用BufferdReader來讀取文件,將要調用的文件放在src下.通過獲取絕對路徑的方式並與文件名拼接來查詢文件,遍歷行數累加。在構建文件對象的時候一開始使用了File直接來構建,發現其編碼有誤,不是UTF-8,且無法做修改,后續更換了InputStreamReader類型來讓指定文件類型。
String pre_path=new File("").getAbsolutePath();
try {
bufferedReader = new BufferedReader(
new InputStreamReader(new FileInputStream( pre_path+ "\\" + path),"utf-8"));
String line;
// 通過循環不斷整行讀取文件
// 同時記錄讀取次數即可
while ((line = bufferedReader.readLine()) != null) {
//匹配任意非空白字符
if (line.length() != 0 && !line.matches("\\s+")) {
line_num++;
}
}
} catch (...)
return line_num;
}
統計文件中各單詞的出現次數
思路:詞頻統計我用map來存儲鍵值對,首先還是做相同的正則匹配來獲取所有單詞,然后進行遍歷加數,以(單詞名,出現次數)鍵值對存入map中。在排序時構造匿名內部類來重寫compare方法,先比較值,如果值相同,則比較鍵序。並且為了判斷字典序再寫了一個方法來進行比較。
List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
// 構造匿名內部類
// 首先根據頻率比較,如果頻率相同,比較字典序
list.sort((Comparator<Map.Entry>) (o1, o2)
-> ((Integer) o1.getValue()).compareTo((Integer) o2.getValue()) != 0
? ((Integer) o2.getValue()).compareTo((Integer) o1.getValue()) : getCharNums((String)o1.getKey(),((String)o2.getKey())));
// 返回list 前十個 數據,也即出現的前十的高頻詞
return list.size() < 10 ? list.subList(0, list.size()) : list.subList(0, 10);
}
文件存取功能
思路:分別使用BufferedReader和BufferedWriter來進行文件的存取,增加了緩沖區對於性能有所提高,並且根據題意更換了File並使用InputStreamReader和OutputStreamWriter來替代,從而定義文件的默認編碼。
String pre_path=new File("").getAbsolutePath();
try {
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(new FileInputStream( pre_path+ "\\" + path),"utf-8"));
String temp;
while ((temp = bufferedReader.readLine()) != null) {
stringBuilder.append(temp).append("\n");
}
bufferedReader.close();
} catch(...)
return stringBuilder;
}
String pre_path=new File("").getAbsolutePath().toString();
try {
//生成的文件放在當前項目下
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream( pre_path+ "\\" + path),"utf-8"));
bufferedWriter.write(message);
bufferedWriter.flush();
bufferedWriter.close();
} catch (...)
}
性能改進
1.IO處理上,使用了BufferedReader和BufferedWriter類,從性能來看比沒有緩沖區的效率更好。
從全局的角度來考慮,在性能上我選擇了能夠避免使用正則表達式就避免,但不可全部舍去,可以節省代碼開發時間,保持了較高的效率。
如在統計字符數上我分別使用了兩個方法進行測試:
發現正則匹配性能較差,因此我只在統計單詞和詞頻時候進行使用正則表達式。
單元測試
測試字符統計數據代碼
對於統計字符進行了測試,包括ascii碼的所有進行了一次統計。如無誤則說明功能符合要求。
for(int i=0;i<128;i++){
stringBuilder.append((char)i);
}
message=stringBuilder.toString();
utils.writeTo("1.txt",message);
System.out.println("characters: "+utils.charNums("1.txt"));
測試單詞統計數據代碼
測試了一些可能出現的情況,並通過測試輸出的值來比對進行判斷功能是否符合。
//添加測試,包括基本可能出現的情況
stringBuilder.append("cold111").append("\n"); //是
stringBuilder.append("col111").append("\n"); //不是
stringBuilder.append("col]dl11").append("\n"); //不是
stringBuilder.append("col\nd111").append("\n"); //不是
stringBuilder.append("cola! sssb. aaaa, abbd\t dsds123").append("\n"); //5個都是
System.out.println("words: "+utils.wordNums(stringBuilder));
測試統計行數函數
測試了非空字符是否能夠被算入有效行數中,如不能則功能符合需求。
/**
* 測試功能3,非空字符全部算行,
* 1.txt內容:
* ssss444 563ff 11d fase11 windows95 windows98 windows2000
*
* 1
* \n
* 2
* \t
*/
public void testLinenums() {
try {
Utils utils=new Utils();
System.out.println("lines:"+utils.lineNums("1.txt"));
}
catch(...)
}
測試統計詞頻數據代碼
重點測試了詞頻的排序以及相同時是否能夠以字典序進行排序。如可以則功能符合需求
for (int i = 0; i < 9; i++) {
for (int j = 2000; j < 2010; j++) {
stringBuilder.append("windows").append(j).append(",\n");
}
}
for (int i = 0; i < 10; i++) {
stringBuilder.append("windows2021,\n");
stringBuilder.append("WINDOWS2000.\n").append("windows98!\n").append("windows95?\n").append("windosa,\n");
stringBuilder.append("sjy%dsd*,\n");
}
對於上述的測試代碼進行了測試,功能都是相符合的。
線性測試
分別測試了1000,10000,100000,1000000個單詞的時間.
測試發現時間復雜度和單詞量呈線性增長,且在使用了緩沖區后,運行時間分別為68ms,150ms,386ms,2464ms,耗時有了明顯的縮短。
覆蓋率
寫代碼前先進行規划,能夠提升覆蓋率
異常處理
只有簡單的對於IO流的異常處理,如try,catch。因為項目較小,無自定義異常處理。在程序入口如果運行參數有誤則會運行默認的測試方法。
PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(h) | 實際耗時(h) |
---|---|---|---|
Planning | 計划 | 0.5 | 0.5 |
Estimate | 估計這個任務需要多少時間 | 12 | |
Development | 開發 | 2 | 3 |
Analysis | 需求分析 (包括學習新技術) | 2 | 1 |
Design Spec | 生成設計文檔 | 0.5 | 0.5 |
Design Review | 設計復審 | 1 | 0.5 |
Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 1 | 0.5 |
Design | 具體設計 | 2 | 2 |
Coding | 具體編碼 | 2 | 2 |
Code Review | 代碼復審 | 0.5 | 1 |
Test | 測試(自我測試,修改代碼,提交修改) | 2 | 2 |
Reporting | 報告 | ||
Test Repor | 測試報告 | 1 | 2 |
Size Measurement | 計算工作量 | 0.5 | 0.5 |
Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 1 | 1 |
合計 | 16 | 16.5 |
總結
1.溫習了java所學的一些知識點,且了解到了一些新的特效會在后續的開發中學習和使用。
2.學會了使用psp表格來規划,對於后面大的項目更能夠有所幫助。
3.測試要做到盡量完善,給用戶更好的體驗。
4.對於自身寫代碼時候的一些問題還需要訂正,如沒有比較簡潔的代碼風格,還需要更加努力學習。
5.更加熟練的使用git,以前是直接用git bash來提交項目,現在我用了編譯器來調用git,更加的方便。且對於git如何使用有了更深的體會。
6.對於構建之法的學習,因為還沒有發書因此去到了老師的博客學習,通過了幾天的觀看,學到了不少的知識,且老師的書籍幽默風趣讓我深入其中,對於書中的故事所闡述的道理能夠有更好的認識。也領悟了很多的道理如團隊如何合作等,之后書籍下來后會繼續去閱讀和鑽研書本。