這個作業屬于哪個課程 | 2021春軟體工程實踐|S班 (福州大學) |
---|---|
這個作業要求在哪裡 | 軟工實踐寒假作業(2/2) |
這個作業的目标 | 閱讀《建構之法》、學習使用git以及github、WordCount程式設計、撰寫部落格 |
其他參考文獻 | CSDN、部落格園、知乎...... |
目錄
- part1:閱讀《建構之法》并提問
- 問題一
- 問題二
- 問題三
- 問題四
- 問題五
- 附加題
- part2:WordCount程式設計
- Github項目位址
- PSP表格
- 解題思路描述
- 代碼規範制定連結
- 設計與實作過程
- 性能改進
- 單元測試
- 異常處理說明
- 心路曆程與收獲
讀了56~57頁“專和精的關系”,我想問的是究竟該往哪個方向發展:是成為一個什麼都會一點的全棧工程師,還是成為隻精通某一種語言的工程師?哪個方面會更吃香?
我自己是覺得可以成為精通一門語言的工程師比較好,把這門語言學到深,真正去了解這門語言,使用的時候就會得心應手。當然,這不意味着不要去學習其他語言、技術,隻是不需要學得那麼深入,會基本文法,能夠使用就行,自身是需要精通一門語言的,才有底氣。
讀了74頁的注釋規範,這裡說“應該隻用ASCILL碼,不能用中文字元”我有點不大了解?
我的觀點是認為注釋的作用就是為了讓其他程式員,甚至是自己看得懂代碼寫的是什麼功能,是易讀的。如果在歐美國家,用ASCII碼當然沒問題。但是在我們自己看來,英文注釋反而大大阻礙了其可讀性,我遇到英文注釋,可能還需要借助翻譯工具,這樣就浪費了我的時間。而且寫中文注釋可以同英文代碼更好的區分開來,哪個是注釋,哪個是代碼。
讀了84~85頁,我認識到結對程式設計的必要之處,那我想問如何進行有效的結對程式設計?若是其中一個人能力不足,或者總在偷懶,又因為項目要到時間了,另一個人隻能被迫完成大部分内容,這樣的結對程式設計反而無法帶來好處,那麼如何避免這種情況的出現呢?
我自己的能想到的解決方法是,找個能力相近的有責任心的人一起結對程式設計。不知道還有沒有更好的解決方法?
靈活開發的一個原則是“可用的軟體是衡量項目進展的主要名額”,我想問的是“可用的軟體”的具體含義指的是什麼?指的是僅實作功能需求的軟體,但是仍存在一些bug,還是指的是無bug的軟體?如何判斷一個軟體是可用的?有沒有什麼具體的标準?
我的認知可用的軟體是指實作功能需求且目前暫未發現bug的軟體,有bug的軟體雖然可能還是可用的,但是bug遲早是要解決的,我更願意把無bug的軟體稱之為“可用的軟體”。
讀到第八章收集使用者需求時,我有一個問題:軟體開發時是滿足大部分人所需要的需求,還是盡量滿足所有使用者各式各樣的需求,即對小衆使用者的需求是否要滿足?比如手機的旁白模式,這個對大多數人是用不到的,但市面上基本所有的手機都實作了這個功能,因為他對一些人來說這是非常必要的。
是以,我的觀點是盡量滿足使用者的各種合理的需求,這無疑會增加程式設計的工作量,但是,這樣的軟體才能更好的服務使用者。
Android并不是Google的親兒子
在2003年,AndyRubin,Rich Miner,Nick Sears,和 Chris White 四人共同研發(Android就是用AndyRubin的昵稱來命名)直到2005年Android被Google收購,進而改變了這個手機作業系統的命運。是以Android并不是Google自行開發的産品,而是收購得來的。
參考連結
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 30 | 40 |
• Estimate | • 估計這個任務需要多少時間 | ||
Development | 開發 | 1320 | 1255 |
• Analysis | • 需求分析 (包括學習新技術) | 210 | 240 |
• Design Spec | • 生成設計文檔 | 60 | |
• Design Review | • 設計複審 | ||
• Coding Standard | • 代碼規範 (為目前的開發制定合适的規範) | 45 | |
• Design | • 具體設計 | 120 | 130 |
• Coding | • 具體編碼 | 720 | 600 |
• Code Review | • 代碼複審 | 50 | |
• Test | • 測試(自我測試,修改代碼,送出修改) | 100 | |
Reporting | 報告 | 115 | |
• Test Repor | • 測試報告 | ||
• Size Measurement | • 計算工作量 | ||
• Postmortem & Process Improvement Plan | • 事後總結, 并提出過程改進計劃 | ||
合計 | 1470 | 1410 |
一開始看到題目時,發現是在指令行運作程式,有點懵。查找資料後才明白args就是指令行傳入的參數,這麼一來就知道檔案的路徑,那麼問題就變得好解決了。
我的解題步驟如下:
1、讀入檔案并轉為字元串,于是直接百度Java讀取檔案的操作,最後選擇了一個字元一個字元的讀檔案。
2、統計總字元數:這個就很簡單了,字元串的長度就是總字元數。
3、統計單詞總數:在查找資料之後,發現正規表達式是個很不錯的選擇,于是花時間學習了一下正規表達式的用法。
4、統計有效行數:我選擇的方法是一行一行的重新讀檔案,周遊每一行的字元,若是遇到不是空白字元的字元,說明這一行是有效行,行數+1。
5、統計單詞頻數:map無疑是個很好的選擇,可以存儲key、value,其本身還自帶根據key、value進行排序的方法。
最後輸出結果到檔案則是查閱Java合适、且同時可把編碼格式設為UTF-8的輸出方法。
程式一共有兩個類:WordCount類:用于讀取指令行的參數,并根據檔案路徑構造File,隻有main函數
HandleTxt類:用于處理File的類,其中有Changestr方法:将檔案内容轉為String字元串;Returnnum方法:傳回檔案總字元數;Getwords方法:傳回單詞總數;Getlines方法:擷取文章的有效行數;GetTen方法:排序,并取前十個頻率最高的單詞的Map;Ouputxt方法:輸出結果至output.txt。
在WordCount建立一個HandleTxt的對象,在HandleTxt的構造函數中調用處理檔案的方法。
Changestr實作
選擇一個字元一個字元地讀取檔案,同時将得到的字元串轉為小寫。其中發現了Windows回車是\r\n,是兩個字元。
關鍵代碼
StringBuffer sb=new StringBuffer();
FileInputStream fileInputStream=new FileInputStream(in);
while ((n=fileInputStream.read())!=-1)
{
char ch=(char)n;
sb.append(ch);
}
txt=sb.toString();
txt=txt.toLowerCase();
Returnnum實作
由于在Changestr方法中得到了檔案内容的字元串,是以隻要擷取字元串長度就得到了檔案總字元數
public int Returnnum()
{
return txt.length();
}
Getwords實作
傳回單詞總數選擇用正規表達式進行比對。
Pattern pattern=Pattern.compile("(^|[^a-z0-9])([a-z]{4}[a-z0-9]*)");
Matcher matcher=pattern.matcher(txt);
while (matcher.find())
{
words++;
}
Getlines實作
選擇重新一行一行讀檔案,若遇到行中存在為非空白字元的字元,行數加一
String content=br.readLine();
StringBuilder sb=new StringBuilder();
while (content!=null)
{
for(int i=0;i<content.length();i++)
{
if(content.charAt(i)!='\t'&&content.charAt(i)!='\n'&&content.charAt(i)!=' '
&&content.charAt(i)!='\r')
{
line++;
break;
}
}
content=br.readLine();
}
GetTen實作
同樣用正規表達式進行比對,結果選擇使用Map進行存儲和排序,并取排序後的前10個
while (matcher.find())
{
if (Legalchar(txt.charAt(matcher.start()-1)))
{
word=matcher.group();
if (WordsMap.containsKey(word))//Map中已含有此單詞
{
WordsMap.put(word,WordsMap.get(word)+1);
}
else
{
WordsMap.put(word,1);
}
}
}
//排序結果取前十個存放到result中
WordsMap.entrySet().stream().sorted(Map.Entry.<String, Integer> comparingByValue().reversed()
.thenComparing(Map.Entry.comparingByKey())).limit(10)
.forEachOrdered(x->result.put(x.getKey(),x.getValue()));
Ouputxt實作
使用PrintWriter,設定編碼為UTF-8,将字元串輸出到b.txt
PrintWriter pw=new PrintWriter(new OutputStreamWriter(new FileOutputStream(out),
"UTF-8"));
Iterator<Map.Entry<String, Integer>> it=GetTen().entrySet().iterator();
sb.append("characters: "+Returnnum()+"\n"+"words: "+Getwords()+'\n'
+"lines: "+Getlines()+'\n');
while(it.hasNext())
{
Map.Entry<String, Integer> entry=it.next();
sb.append(entry.getKey()+": "+entry.getValue()+"\n");
}
pw.print(sb.toString());
pw.flush();
pw.close();
運作3,251,088 位元組的檔案,耗時7309ms。![]()
軟工實踐寒假作業(2/2)
改進方法:在擷取行數的方法中,一旦讀到非空白字元,直接break,無需周遊整行。
if(content.charAt(i)!='\t'&&content.charAt(i)!='\n'&&content.charAt(i)!=' '
&&content.charAt(i)!='\r')
{
line++;
break;
}
測試returnnum方法
測試能否正确讀入\n\t@$等字元,字元數是否有錯。
String str="dskjsh kf"+"\n\t\r"+"fdk233458884*@@$$^QJ BCJKL";
HandleTxt lib=new HandleTxt(str);
assertEquals(lib.Returnnum(),str.length());
測試getwords方法
測試其能否正确判斷單詞,其中隻有"assd "、"\nfile123"為單詞。
String str="assd "+"*1sded34"+" ws123"+"\nfile123";
HandleTxt lib=new HandleTxt(str);
assertEquals(lib.Getwords(),2);
測試getTen
測試函數能否正确存儲單詞,并排序。
String str=" asdd23 ASdd23 aSdd23 dfgrrrr ghjth cxccxd *zsdsds zsdsds zsdsds zsDsds\n"+" ddfg123 fgfdg234\n"+"\n" +
" Sfgh\n" +
"\n" +
"dsdaasdas asdd\n";
str=str.toLowerCase();
HandleTxt lib=new HandleTxt(str);
Map<String,Integer> map=new HashMap<>();
map.put("zsdsds",4);
map.put("asdd23",3);
map.put("asdd",1);
map.put("cxccxd",1);
map.put("ddfg123",1);
map.put("dfgrrrr",1);
map.put("dsdaasdas",1);
map.put("fgfdg234",1);
map.put("ghjth",1);
map.put("sfgh",1);
assertEquals(lib.GetTen(),map);
測試覆寫率截圖
可以使用讀入檔案的方式,去測試Changestr、Getlines方法,進而優化覆寫率。
隻對WordCount讀入的參數做了異常處理,其餘部分選擇直接throws Exception。
if(args.length==0)
{
System.out.println("沒有參數");
}
else if(args.length==1)
{
System.out.println("輸入參數隻有一個");
}
else if(args.length==2)
{
File in=new File(args[0]);
File out=new File(args[1]);
if(!in.exists())
{
System.out.println("輸入檔案不存在");
}
else if(!out.exists())
{
System.out.println("輸出檔案不存在");
}
心路曆程
- 一開始看過去覺得這個題目不難,之前寫過類似的項目。直到做了才發現,它是從指令行讀取參數,從哪裡擷取參數一開始就難到我了。之後的讀取檔案同樣也是個考驗,一開始我是選用readline讀取檔案,可是無法讀入\n,于是我選擇每讀一行添加一個\n,這樣導緻的結果是最後一行必定會有一個\n,不符合要求。然後我選擇了一個字元一個字元的讀取,結果被Windows的回車埋伏了一手,因為Windows的回車是兩個字元,導緻我一直以為代碼哪裡出了問題。
- 同時還學習了新工具git、github的使用,從一開始的拒絕到最後發現這真是個好工具。
- 同時還認識到大任務其實不可怕,隻要拆分成一個個的小任務,認真完成每一個小任務,當最後一個小任務完成時,大任務也便完成了。這次作業付出了許多精力和時間,但同樣的收獲也是滿滿的。
收獲
學會了使用git、github
一開始看到要用git、github,我的内心是拒絕的。因為我從未使用過這兩個工具,怕自己學不會。當看完教程,學會使用,并且在項目中使用後,才發現其功能的強大。能夠有效的控制版本,進行代碼管理,代碼的修改一目了然,做了什麼改動也有commit資訊,我已經愛上了這兩個工具了。
-
學會了單元測試
學會使用Junit進行測試,發現這個真友善,IDEA還能快捷鍵生成測試類。通過Junit,我就可以單獨友善地對我寫的某一個函數進行測試,排除bug。不需要再像往常一樣需要運作整個程式,使我的工作完成得更輕松,大大減少我花在調試上面的時間。