一、開頭
博客作業地址 | 鏈接 |
---|---|
Github地址 | 鏈接 |
同伴博客 | 鏈接 |
二、PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | 20 | 30 |
· Estimate | · 估計這個任務需要多少時間 | 800 | 800 |
Development | 開發 | 300 | 400 |
· Analysis | · 需求分析 (包括學習新技術) | 120 | 200 |
· Design Spec | · 生成設計文檔 | 30 | 30 |
· Design Review | · 設計復審 (和同事審核設計文檔) | 20 | 30 |
· Coding Standard | · 代碼規范 (為目前的開發制定合適的規范) | 20 | 10 |
· Design | · 具體設計 | 30 | 20 |
· Coding | · 具體編碼 | 300 | 500 |
· Code Review | · 代碼復審 | 30 | 60 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 100 | 20 |
Reporting | 報告 | 60 | 30 |
· Test Report | · 測試報告 | 30 | 30 |
· Size Measurement | · 計算工作量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 事后總結, 並提出過程改進計划 | 30 | 30 |
合計 | 1080 | 1410 |
三、計算模塊接口的設計與實現過程
接口的設計
首先拿到一個題目當然是要先理清題目的意思啦,這次題目的要求是設計統計一個文件中的幾項指標,其中包括:
-
統計文件的字符數
-
輸出的單詞統一為小寫格式
-
統計文件的單詞總數,單詞:至少以4個英文字母開頭,跟上字母數字符號,單詞以分隔符分割,不區分大小寫。
-
統計文件的有效行數:任何包含非空白字符的行,都需要統計。
-
統計文件中各單詞的出現次數,最終只輸出頻率最高的10個。頻率相同的單詞,優先輸出字典序靠前的單詞。
-
按照字典序輸出到文件txt:例如,windows95,windows98和windows2000同時出現時,則先輸出windows2000
在統計過程中,對字符數是有要求的,要求如下:
- 只需要統計Ascii碼,漢字不需考慮
- 空格,水平制表符,換行符,均算字符
- 英文字母:A-Z,a-z
- 字母數字符號:A-Z,a-z,0-9
- 分割符:空格,非字母數字符號
- 例:file123是一個單詞,123file不是一個單詞。file,File和FILE是同一個單詞
- 輸出的單詞統一為小寫格式
這次的項目對我們來說是一種挑戰,因為即使是在第一次作業中使用過了c#進行一些編程,但是在那次項目中我感覺自己還是在“面向過程編程”,在這次中希望自己能好好提升,增加對“面向對象的理解”。
在這次設計中,我和我的同伴經過討論,決定將每一個功能拆分成一個類,最后在主函數中調用。這樣的話不僅便於我們分工協作,同時也讓代碼更加具有獨立性。
類的結構圖設計:
流程圖設計
具體類:
算法關鍵:
這次的項目中運用到算法的部分不是很多,我們主要集中在對單詞的頻率排序部分,以及判斷是否滿足條件部分使用。
在這次項目中,我們運用了大量正則表達式,正則表達式有以下優點:
1、靈活性、邏輯性和功能性非常的強;2、可以迅速地用極簡單的方式達到字符串的復雜控制;
在這次項目中,運用正則表達式極大的優化了我們的時間和精力;
如何體現“Design by Contract”、“Information Hiding”、 “Interface Design”、 “Loose Coupling”等原則
Information Hiding:信息隱藏,在這次項目中我們體現在將數值用變量代替,很好的保護了信息;
Interface Design:接口設計有六大原則,在這次項目中因為自己的代碼還不夠成熟,我們主要強調在接口名字規范,接口之間獨立。
Loose Coupling:低耦合度,我們這次體現在增加接口上。
實現過程;
首先是基礎功能的實現:
代碼展示
//打開文件並統計字符個數部分類
class asccount
{
//打開文件並統計字符個數
public static int agefile()
{
int num = 0;
try
{
string str = File.ReadAllText(@path.s);
num = Regex.Matches(str, @".").Count;
num = num + linescount.lines() - 1;
}
catch (Exception e)
{
Console.WriteLine("請輸入正確的文件路徑");
}
return num;
}
}
//讀取行數類
class linescount
{
public static int lines()//統計文件中的行數
{
string str = File.ReadAllText(@path.s);
int nr = Regex.Matches(str, @"\r").Count + 1;
return nr;
}
}
//統計字頻數類
class wordcount
{
public static int sum1(Dictionary<string, int> dic1Asc)
{
//將每個單詞按字典順序寫入文件中
string outpath = "";
try
{
outpath = @"D:\xe.txt";
File.Exists(outpath);
}
catch (Exception e)
{
Console.WriteLine("無法找到改文件");
}
//string outpath = @"C:\Users\李星晨\Desktop\output.txt";
StreamWriter sw1 = new StreamWriter(outpath);
int sum = 0;
foreach (KeyValuePair<string, int> entry in dic1Asc)
{
string word = entry.Key;
int frequency = entry.Value;
sw1.Write(word + "\r\n");
sum = sum + frequency;
}
sw1.Close();
return sum;
}
//統計字頻數
public static Dictionary<string, int> Countword()
{
string str = File.ReadAllText(@path.s);
Dictionary<string, int> frequencies = new Dictionary<string, int>();
string[] words = Regex.Split(str, @"\W+");
int j = 0;
//判斷是否是單詞
string[] newwords = ynword.ynword1(words,ref j);
string[] newwords1 = new string[j];
for (int i = 0; i < j; i++)
{
newwords1[i] = newwords[i];
}
foreach (string word in newwords1)
{
if (frequencies.ContainsKey(word))
{
frequencies[word]++;
}
else
{
frequencies[word] = 1;
}
}
return frequencies;
}
}
//判斷是否符合題目要求類
class ynword
{
public static string[] ynword1(string[] words,ref int w)
{
char[] newword = new char[words.Length];
int k = 0;
string[] newwords = new string[words.Length];
for (int i = 0; i < words.Length; i++)
{
char[] wordss = words[i].ToCharArray();
for (int j = 0; j < wordss.Length; j++)
{
if (wordss[j] >= 'A' && wordss[j] <= 'Z')
{
wordss[j] = Convert.ToChar((Convert.ToInt32(wordss[j]) + 32));
}
}
if (wordss.Length >= 4)
{
if ((wordss[0] >= 'a' && wordss[0] <= 'z') && (wordss[1] >= 'a' && wordss[1] <= 'z') && (wordss[2] >= 'a' && wordss[2] <= 'z') && (wordss[3] >= 'a' && wordss[3] <= 'z'))
{
newword = wordss;
string s = String.Join("", newword);
newwords[k] = s;
k++;
}
}
}
w = k;
return newwords;
}
}
展示部分代碼,因為在git上有運行過程就不展示全部代碼了,下面展示運行結果:
vs中運行結果:
輸出到文件中的運行結果,按字典順序對單詞進行排序:
這一點我看好多人都是都是把輸出結果保存在文件中,我和我的同伴商量后還是覺得題目的要求是按字典順序對單詞進行排序,所以結果如下:
新加功能實現過程
新加功能時在命令行中輸入參數並執行,這一點有很多解決方法,我選擇的下面你這種,是我百度最簡單方便的一種啦:
static void Main(string[] args)
{
string s = "";
int m = 0;
int n = 0;
//設置默認文件夾
string outpath = @"D:\xe.txt";
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
//輸出幾個高頻詞
case "-m":
m = int.Parse(args[i + 1]);
break;
//輸入文件路徑
case "-i":
s = args[i + 1];
break;
//輸出文件路徑
case "-o":
outpath = args[i + 1];
break;
//輸出n個單詞的個數
case "-n":
n = int.Parse(args[i + 1]);
break;
}
}
執行結果如下圖:
運行的時候是將每一種參數都進行過嘗試的,這里展示兩張圖:
- 讀取文件,寫入文件:
- 使用-m -n參數
四、代碼復審過程
代碼規范
首先得有代碼規范,才會有代碼復審。
代碼規范分兩個:
1.是代碼風格規范。主要是文字是的規定。
2.是代碼設計規范,牽涉帶程序設計。模塊之間的關系。
在這次項目前,我和我的同伴主要考慮是代碼風格規划,雖然只有兩個人,但是是非常重要的部分;
代碼風格的原則是:簡明,易讀,無二義性。既然只有兩個人,我們便兩個一起制定了符合兩個習慣,着重將兩個人習慣不一樣的制定了標准。
標准如下:
- 縮進:tab鍵;
- 行寬不得大於100字符;
- 不要把多條語句放在一行上;
- 命令用小駝峰;
- 需要寫注釋
代碼復審
代碼復審我認為是結隊編程中最重要的一步,結隊編程最后的結果取決了能力較高的那個人,我覺得也是在這一步中體現出來嘚;
代碼復審我們采用先自審再復審,復審階段我們考慮到對方平常生活中都是不拘小節的人,所以擔心對方錯過很多需要審查的地方,所以我們做了一個核查表,一式兩份,站在同等標准上去審查對方的代碼:
審查目的 | 結果 |
---|---|
代碼是否有編譯錯誤 | 無 |
代碼是否符合代碼規范 | 大部分符合 |
是否有邏輯、算法錯誤 | 無 |
設計考慮是否周全 | 有些地方不夠周全 |
代碼可讀性如何 | 一般,注釋較少 |
代碼容易維護嗎 | 較容易 |
是否有異常處理 | 有 |
是否有無用代碼 | 有 |
通過表格我們就可以很清楚的了解到審查結果啦,比如我們對一些無用的代碼進行了改進,加快了運行效率。
通過代碼復審,我發現了很多自己的問題,比如在命令上面,因為總是自己寫代碼所以會有拼音和英文夾雜的情況,自己一直沒太在意過,這次經過同伴的提點發現是一個非常不好的習慣,這樣別人看自己的代碼的時候會很費力。
還有就是注釋部分,這次代碼的主要設計者是我的同伴,但是她的代碼注釋極少,在這點上非常不符合代碼規范,我們一起增加了注釋。
代碼審查教會我站在別人的角度看代碼,對我提神很大;
五、性能改進
在代碼互審后,我們的代碼有了很大的提升。這時候進行性能分析:
CPU使用率:
性能報告
可以看到我們主函數中占的CPU較多,但是也在接受范圍內;
六、單元測試
這一次是單元測試我們是將所有類都進行了測試,但是因為有點類的參數依賴於另一個類,所以在測試時我們對代碼進行了微小的改變,便於測試。
我們這一次已經進行了三次測試,路徑如下:
第一次測試
文件圖:
先測試簡單的,文件中字符數6個,單詞1個,行數為2;
字符數的測試截圖:
行數的測試截圖:
可是看到都是測試通過的。
七、異常處理部分
我和我的同伴認為在這次項目中,輸入輸入文件的異常處理是最重要的;
輸入文件:
try
{
string str = File.ReadAllText(@path.s);
num = Regex.Matches(str, @".").Count;
num = num + linescount.lines() - 1;
}
catch (Exception e)
{
Console.WriteLine("請輸入正確的文件路徑");
}
輸出文件:
string outpath = "";
try
{
outpath = @"D:\xe.txt";
File.Exists(outpath);
}
catch (Exception e)
{
Console.WriteLine("無法找到改文件");
}
八、結對過程
一拍即合,就結對啦。
九、附加功能
這部分因為時間的關系是沒能完成的,只完成了部分,貼張圖意思意思。
十、總結和收獲
這次項目真的挺難的,c#感覺真的噩夢,雖然之前很簡單了解過但是代碼能力真的挺差的,而且一直感覺掌握不了c#的真諦。
而且這次結對過程也是困難滿滿,一開始我們還是想以前一樣分工,但是這次作業是一步扣一步,上一步沒完成沒法進行下一步,所以我們后期都是一起編程,就像書上說的,結對編程就像跳舞,一開始兩個人確實是不適應的 ,但是慢慢的就會好起來。而且后期很多工作,如單元測試、性能分析都是一起做的,效率更高,同時也更有趣。
我認為像兩個人分工寫代碼,確實有着一起寫來的方便。一起寫不懂就問,而且一起監督,事半功倍。我記得有一個下午我看我同伴的代碼,半天也沒搗鼓出來,但是第二天我去問她,十分鍾就解決了疑惑。
所以我認為結對編程時1+1>2的,但是是否一直大於2,我覺得不是,如果是兩個不熟悉的人,要花大量的時間去熟悉對方,我覺得是得不償失的。
(ps:說出來可能沒人相信,截止日期前三個小時的時候,我的同伴來看我的博客,告訴我我把團隊作業粘貼到個人作業提交,我當時覺得沒事,不就是重新復制粘貼一次嗎,當我打開電腦的時候,我的文檔存檔它....不知道為什么...只保存到了第三點....,我當時真的覺得人生太艱難了,然后又重新寫了一次,這次的作業真的太痛苦了o(╥﹏╥)o)