第四次作业:结对编程
作业链接 | 作业链接 |
---|---|
GIT地址 | GIT地址 |
结对人博客地址 | 结对人博客地址 |
一、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 35 |
Estimate | 估计这个任务需要多少时间 | 20 | 35 |
Development | 开发 | 910 | 1270 |
Analysis | 需求分析 (包括学习新技术) | 50 | 60 |
Design Spec | 生成设计文档 | 40 | 55 |
Design Review | 设计复审 (和同事审核设计文档) | 20 | 25 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 15 | 15 |
Design | 具体设计 | 40 | 60 |
Coding | 具体编码 | 600 | 900 |
Code Review | 代码复审 | 50 | 50 |
Test | 测试(自我测试,修改代码,提交修改) | 40 | 50 |
Reporting | 报告 | 30 | 35 |
Test Report | 测试报告 | 20 | 20 |
Size Measurement | 计算工作量 | 20 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 40 | 60 |
合计 | 1020 | 1415 |
二、实现过程
1.实现思路
思考:看完冗长的题目后,我们发现了需要有4个基础功能(字符统计、单词统计、行数统计、单词频率统计)、2个新功能( 词组统计、自定义输出)。2个新功能需要在基础功能上才能实现,所以我们首先需要完成基础功能。然后将新功能所属的类作为子类,基础功能所属类作为父类,通过继承能做到一部分代码复用,并且让代码结构更加清晰,编码时间也一定程度上减少了不少。同时,在程序运行时用户需要输入参数,我们将参数的输入和读取放在主函数里。
寻找资料:我们意识到在反复读取文档,匹配信息时可能需要用到一些算法,于是在网上搜索了一番,学习了正则表达式。由于很久都没有写C#代码了,我们又上网看了一些讲解C#的视频,在菜鸟教程复习了很多语法知识。
2.实现过程
以下是项目中存在的类、方法以及变量
3.程序结构图
4.程序流程图
5. “Design by Contract”、“Information Hiding”、 “Interface Design”、 “Loose Coupling”
Design by Contract:我们的代码设计确保了在正确的输入下,能够得到正确的输出,完全遵照了契约式设计
Information Hiding:我们的代码做到了信息隐藏,两个没有交集的模块并没有多余的信息访问。
Interface Design:我们的代码中遵守了接口的六大原则,即单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则、开闭原则
Loose Coupling:我们的代码通过多个接口实现了松耦合。
三、代码规范
- 缩进方式使用Tab缩进,大小为4个空格
- 函数、变量命名时使用英文命名,禁止使用拼音命名
- 函数名首字母大写,如WordCount是正确的命名方式,而不是wordCount
- 命名时不采用缩写,使用全称,如Imformation是正确的命名方式,而不是Info
- 每条语句单独占一行,每个花括号单独占一行
- 同类变量可以在一行定义多个
- 变量采用驼峰命名法,类名以及函数名采用首字母大写的方式,命名不加入下划线
- 对每个函数加入该函数功能说明的注释,对于实现特定功能的代码段也要加入该功能说明注释,对于较为复杂和容易使人产生疑惑的代码段和变量名也要添加注释。
四、代码互审
刘洋伙伴对我的代码进行了复审及修改,并做出了巨大的改善,大大提高了代码效率,算是对我的代码的一个大改进。在拿到伙伴的代码后,我尝试运行刘洋伙伴的代码,却发现一个问题。不适用命令行运行直接在program中运行时,没有问题,但是使用命令行运行时,会出现“未将对象引用设置到对象的实例”问题,致使数据打印不出来,因为在程序中运行没有错误,只是在命令行运行中出现错误,又不知道怎么调试,只能去网上找相同的问题和对应的解决办法。但是发现这个问题比较普遍,情况很多,根本无法定位到具体是代码哪里出了差错,这样就很迷茫,像无头苍蝇般,没有方向。这样就只能去网上了解这个错误出现的机制是什么,总结了很多知识后,总结出是因为栈没有被开辟,程序无法读取,所以出错。那这样的话应该就是数组的问题了,从头到尾排查代码,最终在一个数组处发现问题,这个数组声名了之后没有进行new操作,使程序无法读取,对此数组进行new之后,命令行代码能继续运行了。
结对伙伴王雷的代码互审: 刘洋的博客
五、关键代码
详情看伙伴的博客:刘洋的博客
六、运行结果:
直接运行:
带命令行操作运行:
文档写入结果:
七、单元测试
测试结果:
部分测试代码:
对于CountWord函数的测试:
public void CountWordTest()
{
WordBasis.wordbasis = new WordBasis();
string[] word = { "sada", "fewa", "reht", "tyyn" };//测试方法得到的结果单词字符长度是否超过规定的4
string a = File.ReadAllText(@"D:\test.txt");
for (int i = 0; i < 4; i++)
{
Assert.AreEqual(word[i], WordBasis.CountWord(a)[i]);
}//测试CountWord方法得到的结果单词前四位是否都为字母
string b = File.ReadAllText(@"D:\test1.txt");
for (int i = 0; i < 4; i++)
{
Assert.AreEqual(word[i], WordBasis.CountWord(b)[i]);
}
}
对于CountChar函数的测试:
public void CountCharTest()
{
WordBasis.wordbasis = new WordBasis();
int Num = 12;//测试CountWord得到的结果字符数
string a = File.ReadAllText(@"D:\test2.txt");
Assert.AreEqual(Num, WordBasis.CountWord(a));//测试CountWord得到的结果字符数是否符合要求
string b = File.ReadAllText(@"C:\test3.txt");
Assert.AreEqual(Num, WordBasis.CountWord(b));
}
对于CountLine的测试:
public void CountLineTest()
{
WordBasis.wordbasis = new WordBasis();
int Num = 4;//测试得到的行数(不含空行)是否符合要求
string content5 = File.ReadAllText(@"D:\test2.txt");
Assert.AreEqual(Num, WordBasis.CountLine(content5));//测试得到的结果行数(含空行)是否符合要求
string content6 = File.ReadAllText(@"D:\test2.txt");
Assert.AreEqual(Num, WordBasis.CountLine(content6));
}
八、性能分析
记录在改进程序性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2017的性能分析工具自动生成),并展示你程序中消耗最大的函数.
性能分析结果:
可以看到消耗最大的函数是这个读取文件的函数,之前在写代码的时候在ReadFile函数里所写的循环里每循环一次就会重新定义一个变量,耗费了不少资源,通过改进代码再次测试得到下面的结果,整体效能提高了20%左右。
九、附加题
用户交互界面绘制
十、结对过程照片
十一、心得体会
这次编程收获颇多,也确确实实发现1+1>2。刚开始写代码的时候,糊成一团,全部写在main里面,因为我们想着先一下子全写好了再慢慢分开比较思路清晰些。然后我们再main中写好了以后先是分成好几个方法,然后在分成好几个方法之后遇到了很多问题,有很多的变量搞不清楚啊之类的,好不容易搞清楚之后,之后又要把这些方法分成类给封装起来,这里问题更多了,很多数据的传递啊什么的都做得很乱,浪费了很多时间。如果一开始制定好计划,制定好有哪些类哪些方法,直接开始上手写,而不是写成一团再分,会节省很多很多时间,下次再写项目一定要细细分开,做好文档准备,虽然这是一个小项目,写文档有些小题大做,但是可以写个小文档,能大大增加写代码的效率。这个项目说难不是很难,说大也不是很大,如果纯写的话也就三四天就搞定了,但是布置了个人作业的同时也布置了一个团队作业,一个周的时间两个项目一起冲击我们,有点小接受不了,光是团队项目,整数据库整了两三天,然后一刻也没有休息,继续整结对编程,时常写代码写到半夜两三点,一个星期的高强度工作让我感受到了程序猿的辛苦,但这也是磨炼我们的方式,在这种磨炼下,我们一定会更早变强的。我变秃了,也变强了——琦玉老师