个人第四次博客作业——结对编程

橙三吉。 提交于 2019-12-01 07:18:18
Github项目地址 WordCount
合作同学作业链接 201731091317

Part 1. PSP表格

PSP2. Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 80
· Estimate · 估计这个任务需要多少时间 60 80
Development 开发 700 785
· Analysis · 需求分析 (包括学习新技术) 60 45
· Design Spec · 生成设计文档 30 45
· Design Review · 设计复审 (和同事审核设计文档) 30 30
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 25
· Design · 具体设计 40 40
· Coding · 具体编码 300 400
· Code Review · 代码复审 90 80
· Test · 测试(自我测试,修改代码,提交修改) 90 120
Reporting 报告 90 120
· Test Report · 测试报告 30 60
· Size Measurement · 计算工作量 30 40
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 20
合计 850 985

Part 2. 计算模块接口的设计与实现过程

一、解题思路

  • 字符统计:遍历判断字符是否为空,对不为空的字符进行累加,统计出字符的总数
  • 单词统计:先对单词的大小写进行统一,比如说:Word,WORD,Word是同一个单词,我们将大写字母转化成小写字母。再对单词的词数进行统计,将文件中的字符存入一个字符串数组中,对该数组中的字符串进行判断比较找出不同的则次数加1。
  • 行数统计:遍历判断是否为空行,对不是空行的行数进行累加,统计出非空的行数。
  • 单词频数统计:将单词和该单词的频数存入一个集合中<string,int>,分别表示单词和单词频数。对所有单词的频数进行字典序排序。最终输出一定数量的单词个数以及其频数。
  • 词组长度统计:将单词和其长度按照统计单词频数的方法存入一个集合中<string,int>,分别表示单词和词长。当要想得到一个长度的单词时,在其中查找,打印出该长度的所有单词。

所以,在设计过程中我们把本程序功能分为两大类:

1.基本功能。具体分为:

  1. 统计文件的字符数;
  2. 统计文件的单词总数;
  3. 统计文件的有效行数;
  4. 统计文件中各单词的使用频率;
  5. 按照字典序输出到文件txt。

2.新增功能。具体分为:

  1. 统计文件夹中指定长度的词组的词频;
  2. 输出用户指定的前n个高频单词及其频数:
    1. 参数-l设定读入的文件路径;
    2. 参数-m设定统计的词组长度;
    3. 参数-n设定输出的单词数量;
    4. 参数-o设定生成文件的存储路径。

二、代码组织

在实现过程中本程序包含了10个类,16个函数,如下表所示:

类名 函数 功能
Program Main Main:对于用户选择是否需要指令参数进行处理
select_way instruction nointrction the_error instruction:对于需要指令参数的情况进行处理。nointrction:对于需要指令参数的情况进行处理。the_error:对于异常情况进行处理
file_path 声明输入、输出文件路径变量
result print_char print_word print_lines print_wordsort print_selectedlong print_char:输出字符数。print_word:输出单词总数。print_lines:输出有效行数。print_wordsort:输出单词排序。print_selectedlong:查找规定长度的单词。
asccII_count ascciicount ascciicount:统计字符的个数
word_count word_sum word_frequency word_sum:打印频数高的单词。word_frequency:打印该单词的频数
line_count lines lines:统计文件的行数
ins_process ins_select ins_right ins_select:对于用户选择的指令参数进行处理。ins_right:对已选择的指令进行相应的赋值
uniform_character uncharater uncharater:统一字符大小写

函数之间的关系可用下图表示:

Part 3.代码复审

一、代码规范

1.命名约定

  • 对于类名和变量名,我们都采用“aa_bb”的命名方式,其中aa表示变量名,bb表示操作名。

2.注释约定

  • 将注释放在单独的行上,而不是放在代码行的末尾;

3.布局约定

  • 使用默认的代码编辑器设置(智能缩进,四字符缩进,制表符另存为空格)。
  • 每行只写一条语句;
  • 每行只写一条声明;
  • 如果连续行没有自动缩进,请缩进一个指标位(四个空格)。

二、代码互审

在代码互审过程中,我主要关注的是具体功能是否能够实现、代码是否符合规范、异常处理以及代码的结构。在代码互审的过程中我们遇到的问题以及改进方法如下:

  • 原来版本打开exe文件只能进行无指令操作,经过改进,我们可以进行选择,既能无指令操作又能有指令操作。
  • 原来版本的代码结构的方法比较简单,即就几个大的功能的类,没有细分功能的实现,改进后,我们的代码结构更有逻辑性,能容易理解,方便团队协作。
  • 原来版本的对异常的处理比较少,对于许多错误指令没有考虑,没有提示,改进后,我们会加入错误指令的提示。

Part 4.计算模块接口部分的性能改进

在改进计算模块性能的过程中,我们发现的问题以及改进思路如下:

1.对统一字符的处理,原来需要对一个单词的每一个字符用for循环来依次判断,再对产生的flag进行判断,而这样的对时间和空间的占用率较高,我们将去到for循环,对一个单词统一判断,减少判断次数和循环次数。提高代码的运行速度。
2.对于使用率较高的word_wore和word_len两个参数,原来是放在一个类里面,导致其他地方调用十分复杂,所以我们对两个参数进行封装重构,减少了代码的臃肿。

使用VS的性能探查器得到的效能分析报告如下图:

从上图中我们可以看到,result类中的print_char函数在总CPU耗时中占用了56%左右,由于print_char函数只是由打印语句和调用line_count中的lines函数构成,所以在总CPU耗时最多的函数等价为lines函数。

Part 5.计算模块部分单元测试展示

  1. 对word_count类中的word_sum方法进行测试:新建一个字典集合,向集合中添加一个单词,比较添加后的情况。

public void Test_word_sum()
{
//
// TODO: 在此处添加测试逻辑
//
Dictionary<string, int> frequen = new Dictionary<string, int>();
file_path.output_path=@"E:\GITwrod\WordCount\201731062322\WordCount\UnitTest_WordCount\testoutfile.txt";
string a = "word";
frequen.Add(a, 1);
//frequen.Add("word",1);
Assert.AreEqual(1, word_count.word_sum(frequen));
// Assert.Fail();
}

  1. 对字符统计的测试:读取文件内容(该文件为空文件),比较文件中的字符数与人工算出的字符数是否相等,相等则测试成功,否则失败。

public void TestMethod1()
{
//
// TODO: 在此处添加测试逻辑
//
file_path.input_path = @"E:\GITwrod\WordCount\201731062322\WordCount\UnitTest_WordCount\testfile.txt";
int num = 0;
Assert.AreEqual(num, asccII_count.ascciicount());
//Assert.Fail();
}
测试结果为:

3.对行数统计的测试:与测试字符数的方法类似,读取文件内容(该文件为空文件),比较文件中的行数与人工算出的字符数是否相等,相等则测试成功,否则失败。因为本程序是除去了空行的,所以当没有显示的字符(空格不显示)的时候为零行。

public void TestMethod1()
{
file_path.input_path = @"E:\GITwrod\WordCount\201731062322\WordCount\UnitTest_WordCount\testfile.txt";
int x = 0;
Assert.AreEqual(x, line_count.lines());
// Assert.Fail();
}
测试结果为:

Part 6.计算模块部分异常处理说明

在本程序中,异常处理主要是针对用户输入的异常处理:

1.在选择是否使用指令参数时的异常处理。如果用户输入了不存在或者错误的选项,则报错:
switch(cmd_num)
{
case 1: select_way.instruction();
break;
case 2: select_way.nointrction();
break;
default: select_way.the_error();
break;
}

public static void the_error()
{
Console.WriteLine("the error instruction");
Console.ReadKey();
}

2.在输入指令参数是的异常处理。如果用户输入了不存在或者错误的参数,则报错:

else
{
Console.WriteLine("the wrong instruction");
Console.ReadKey();
}

3.在没有输入输入文件路径时的异常处理。如果用户没有输入输入文件路径就要进行下一步,则报错:

if(x%2==0)
{
Console.WriteLine("need a input file");
Console.ReadKey();
}

Part 7.部分代码展示

1.对选择方式的实现,主要是对有指令和无指令的命令行操作进行判断,可以选择两种方式中的任意一种方式进行操作。有指令操作的话,会输出想显示的内容,但是必须输入读取的文件路径;无指令操作,会输出固定的内容(字符数,单词数,行数和10个高频单词),需要输入读取文件路径和输出文件路径。
public class select_way
{
illustration ill2 = new illustration();
result re1 = new result();
ins_process inp1 = new ins_process();

public void instruction(word_len len,word_more more)
{
ill2.illu_function();
Console.WriteLine("input your instructions");
string all_char = Console.ReadLine();
string[] ins_arr = all_char.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
int ind_sum = 0;
for (int i = 0; i < ins_arr.Length; i = i + 2)
{
ind_sum = ind_sum + inp1.ins_select(ins_arr, i,len,more);
}
inp1.ins_right(ind_sum,len,more);
}
public void nointrction(word_len len,word_more more)
{
Console.WriteLine("the input path:");
string inputpath = Console.ReadLine();
file_path.input_path = inputpath;
//result.Word_more = 10;
//result.Word_len = 0;
Console.WriteLine("the output path:");
string outputpath = Console.ReadLine();
file_path.output_path = outputpath;
//re1.WM.Wordmore = 10;
re1.print_char();
re1.print_word(len);
re1.print_lines();
re1.print_wordsort(more.Wordmore,len);
}
public void the_error()
{
Console.WriteLine("the error instruction");
Console.ReadKey();
}
2.对有指令时进行了哪些操作(即需要展示哪些内容和未设置的内容进行默认设置的操作)。我们先通过循环对每一组指令进行判断,实现对应的操作,通过16进制数对不同的操作返回不同的值来区分这些操作。最后将这些值加起来判断输入了哪些操作,需要对哪些参数进行更改、哪些参数进行默认设置。
public class ins_process
{
static string str1 = "-l";
static string str2 = "-m";
static string str3 = "-n";
static string str4 = "-o";
result re2 = new result();

public int ins_select(string[] array, int i,word_len len,word_more more)
{
int flag = 0;
if (array[i].Equals(str1, StringComparison.OrdinalIgnoreCase) == true)
{
file_path.input_path = array[i + 1];
flag = 1;
}
else if (array[i].Equals(str2, StringComparison.OrdinalIgnoreCase) == true)
{
len.Wordlen = Convert.ToInt32(array[i + 1]);
//result.WL.Wordlen = Convert.ToInt32(array[i+1]);
flag = 2;
}
else if (array[i].Equals(str3, StringComparison.OrdinalIgnoreCase) == true)
{
//result.WM.Wordmore = Convert.ToInt32(array[i + 1]);
more.Wordmore = Convert.ToInt32(array[i + 1]);
flag = 4;
}
else if (array[i].Equals(str4, StringComparison.OrdinalIgnoreCase) == true)
{
file_path.output_path = array[i + 1];
flag = 8;
}
else
{
flag = -16;
}
return flag;
}
public void ins_right(int x,word_len len,word_more more)
{
if (x > 0)
{
if (x % 2 == 0)
{
Console.WriteLine("need a input file");
Console.ReadKey();
}
else
{
if (x == 1)
{
//result.Word_more = 10;
file_path.output_path = "output.txt";
//result.Word_len = 0;
re2.print_char();
re2.print_word(len);
re2.print_lines();
re2.print_wordsort(more.Wordmore,len);
}
else if (x == 3)
{
//result.Word_more = 10;
file_path.output_path = "output.txt";
re2.print_char();
re2.print_word(len);
re2.print_lines();
re2.print_wordsort(more.Wordmore, len);
re2.print_selectedlong(len.Wordlen );
}
else if (x == 5)
{
file_path.output_path = "output.txt";
//result.Word_len = 0;
re2.print_char();
re2.print_word(len);
re2.print_lines();
re2.print_wordsort(more.Wordmore, len);
}
else if (x == 7)
{
file_path.output_path = "output.txt";
re2.print_char();
re2.print_word(len);
re2.print_lines();
re2.print_wordsort(more.Wordmore, len);
re2.print_selectedlong(len.Wordlen);
}
else if (x == 9)
{
//result.Word_more = 10;
//result.Word_len = 0;
re2.print_char();
re2.print_word(len);
re2.print_lines();
re2.print_wordsort(more.Wordmore, len);
}
else if (x == 11)
{
//result.Word_more = 10;
re2.print_char();
re2.print_word(len);
re2.print_lines();
re2.print_wordsort(more.Wordmore, len);
re2.print_selectedlong(len.Wordlen);
}
else if (x == 13)
{
//result.Word_len = 0;
re2.print_char();
re2.print_word(len);
re2.print_lines();
re2.print_wordsort(more.Wordmore, len);
}
else
{
re2.print_char();
re2.print_word(len);
re2.print_lines();
re2.print_wordsort(more.Wordmore, len);
re2.print_selectedlong(len.Wordlen );
}
}
}
else
{
Console.WriteLine("the wrong instruction");
Console.ReadKey();
}
}
}

Part 8.结果展示

1.对GUI界面的设计实现:

2.有指令和无指令时的黑白板输出:
测试的内容:

有指令的黑板运行结果:

有指令的白板运行结果:


无指令的黑板运行结果:

无指令的白板运行结果:

Part 9.描述结对的过程

结对编程(非摆拍)图片

Part 10.心得体会

1.分工:在本次项目中,我主要负责单词数的统计和主函数的编写;单数数的统计代码难度不是很大,用了集合之后还更加方便了,使得整体的编写速度大大提高。
2.结对的优点:在写代码的过程中有卡壳的时候,但是我的队友能及时的点醒我,同时这个时候我也意识到自己的注意力开始下降了,如果是我一个人做的话,可能会继续下降,但是有了队友,他的提醒使我又重新专注起来。自己代码写错了被发现还是一件挺尴尬的事情,所以户使得我们更加专注、高效的编写代码。当然在队友编程的时候可以稍微休息一下眼睛,但是可以从队友代码的书写中可以学到许多东西,还是要保持一定的注意力。
3.在代码中学习:本次项目中,更加熟练的应用集合和封装的知识。以前在写代码的过程中不怎么用集合的,封装也很少用,这一次有两个参数很重要,不重构的话,逻辑会很混乱。于是我们就用了重构,但是很久不写C#的代码,有些知识忘记了,封装的地方总是出错,但是通过我们的不懈努力,最终还是解决了。也学到不少知识。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!