个人第四次作业:结对编程
阅读与准备作业
|
本作业属于的课程 |
https://edu.cnblogs.com/campus/xnsy/2019autumnsystemanalysisanddesign |
|
本作业的要求 |
|
|
队友博客 |
|
|
队友学号 |
201731062520 |
1、Github项目地址:
https://github.com/jie958654064/WordCount
2、PSP表格
|
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
|
Planning |
计划 |
35 |
|
Estimate |
估计这个任务需要多少时间 |
65 |
|
Development |
开发 |
50 |
|
Analysis |
需求分析 (包括学习新技术) |
15 |
|
Design Spec |
生成设计文档 |
10 |
|
Design Review |
设计复审 (和同事审核设计文档) |
10 |
|
Coding Standard |
代码规范 (为目前的开发制定合适的规范) |
5 |
|
Design |
具体设计 |
20 |
|
Coding |
具体编码 |
90 |
|
Code Review |
代码复审 |
45 |
|
Test |
测试(自我测试,修改代码,提交修改) |
60 |
|
Reporting |
报告 |
100 |
|
Test Report |
测试报告 |
45 |
|
Size Measurement |
计算工作量 |
20 |
|
Postmortem & Process Improvement Plan |
事后总结, 并提出过程改进计划 |
15 |
|
|
合计 |
585 |
3、计算模块接口的设计与实现过程
3.1接口与设计类图展示如下

3.2分工实现过程
这里我和我的队友将单词统计这个程序分为了两个部分进行分别开发最后再通过将我队友的代码封装成类库用他的方法,这里我们的分工是我的队友主要设计StatisticalWordCount项目,主要是计算文章的总字符个数、总文本的单词书、以及行数字母的频数、以及将单词频数进行排序等功能,最后他将他的功能进行封装成dll类库供我调用他的方法。
我设计的是CalculateWordCount项目,主要是设计文件的cmd读入命令、添加引用调用他封装的dll对单词的所有统计方法,读取单词文件、以及最后将结果输出到文本中、以及最后设计的一个可展示的界面,将cmd命令转入到form上进行用户自输入,以实现到导入单词文本文件或者用户自动输入文本提交,点击导出按钮输出统计结果。
3.3部分代码
统计接口:Statistical
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WordCount
{
public interface Count
{
//计算总字符个数
int count_Char(string inpath);
//计算文本中总单词数
int count_Word(string inpath);
//计算文本总行数
int count_Line(string inpath);
//计算文本中每个单词出现的次数
Dictionary<string, int> count_Dictionary(string inpath);
//将字母按频数降序排序
Dictionary<string, int> sort_Dictionaryby_desc(Dictionary<string, int> my_dic);
//将结果写入文件
void Write_to_txt(string inpath, string outpath);
};
public class Statistical
{
static void Main(string[] args)
{
}
}
}
实现统计接口:count
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace WordCount
{
public class count:Count
{
//将结果写入文件,inpath为读入文件路径(以下inpath均为读入文件路径),outpath为写入结果文件路径
public void Write_to_txt(string inpath, string outpath)
{
count mycount = new count();
int count_word = mycount.count_Word(inpath); //调用计算总单词数方法,结果保存在count_word
int count_char = mycount.count_Char(inpath); //调用计算总字符数方法,结果保存在count_char
int count_line = mycount.count_Line(inpath); //调用计算总行数方法,结果保存在count_line
StreamWriter sw = null;
//调用计算单词出现次数并排序的方法,将结果保存到dictionary字典中
Dictionary<string, int> a = mycount.sort_Dictionaryby_desc(mycount.count_Dictionary(inpath));
if (outpath == null)
{
sw = new StreamWriter(@"C:\Users\杰\Desktop\out.txt"); //在默认位置创建写文件流
}
if (outpath != null)
{
sw = new StreamWriter(outpath); //在outpath路径创建写文件流
}
//把结果写入文件
Console.SetOut(sw);
Console.WriteLine("字符数:" + count_char);
Console.WriteLine("单词数:" + count_word);
Console.WriteLine("行数:" + count_line);
Console.WriteLine("单词出现次数如下:");
//遍历a字典里面的每一条信息
foreach (KeyValuePair<string, int> pair in a)
{
int value = pair.Value;
string key = pair.Key;
Console.WriteLine("{0}: {1}", key, value);
}
sw.Flush();
sw.Close();
}
//计算并返回总字符个数
public int count_Char(string inpath)
{
//在inpath路径创建读文件流
StreamReader sr = new StreamReader(inpath);
string s;
char[] char_array;
int allchar = 0;
//读取文件的每一行
while ((s = sr.ReadLine()) != null)
{
//将读取的每一行送入char数组中
char_array = s.ToCharArray();
for (int j = 0; j < char_array.Length; j++)
{
//计算每一个字符
allchar++;
}
}
return allchar;
}
//计算每个单词出现的次数,结果传入字典并返回,字典中的key是单词的值,value是单词出现的次数
public Dictionary<string, int> count_Dictionary(string inpath)
{
//在inpath路径创建读文件流
StreamReader sr = new StreamReader(inpath);
string s;
Dictionary<string, int> dictionary = new Dictionary<string, int>();
//读取文件的每一行到字符串s
while ((s = sr.ReadLine()) != null)
{
//将字符串s按空格分割,即划分每一个单词
string[] words = Regex.Split(s, " ");
//计算每行各个单词数
foreach (string word in words)
// 判断字典是否包含该单词,若包含,该单词出现次数加一,若不包含,将该单词添加到字典
if (dictionary.ContainsKey(word))
{
dictionary[word]++;
}
else
{
dictionary[word] = 1;
}
}
return dictionary;
}
//计算并返回总行数
public int count_Line(string inpath)
{
//在inpath路径创建读文件流
StreamReader sr = new StreamReader(inpath);
string s;
int line = 0;
//读取文件总行数
while ((s = sr.ReadLine()) != null)
{
line++;
}
return line;
}
//计算总单词个数
public int count_Word(string inpath)
{
count doCount = new count();
Dictionary<string, int> dictionary = doCount.count_Dictionary(inpath);
int allword = 0;
//遍历字典里面的每一个单词,结果为总单词数
foreach (KeyValuePair<string, int> dic in dictionary)
{
allword += dic.Value;
}
return allword;
}
//将字母按出现次数降序排序
public Dictionary<string, int> sort_Dictionaryby_desc(Dictionary<string, int> my_dic)
{
List<KeyValuePair<string, int>> my_List = new List<KeyValuePair<string, int>>(my_dic);
//按value比较两个单词,并按value大小排序
my_List.Sort(delegate (KeyValuePair<string, int> s1, KeyValuePair<string, int> s2)
{
return s2.Value.CompareTo(s1.Value);
});
my_dic.Clear();
//遍历整个字典,并按value值为字典排序
foreach (KeyValuePair<string, int> pair in my_List)
{
if (pair.Key != null && pair.Key != ":" && pair.Key != "," && pair.Key != ".")
my_dic.Add(pair.Key, pair.Value);
}
return my_dic;
}
}
}
3.4运行过程:
这里我在网上找了一篇文章,存入了K:\text.txt文件中,之后通过cmd使用命令CalculateWordCount.exe -i k:\text.txt -m 3 -n 10 -o k:\output.txt去读它:

4、代码复审
4.1、设立的代码规范
我跟我队友之前合作过很多次,包括一些比赛或者是外面接的一些活,所以配合的还是很有默契,也有一定的代码能力,所以题目下来了我们就仔细读了一下要求确定了我们一起遵守的代码规范如下:
1.一个文件中只放一个类,类名同文件名,不要在一个文件中写好几个类,这样看的清楚。
2.不要在一个文件中写多于500行的代码,除了那些比较大的实体类。其实我还想说超过500行看起来就有点累,但是在我们的系统中超过10000行的代码比比皆是。
3.一个方法的代码不要超过100行,其实我想说超过50行的方法看起来就有点累。但是在我们的系统中超过200行代码的方法比比皆是。
4.存储过程的代码也不要超过100行,不要在存储过程中写过多的业务逻辑,那是找死,但是在我们的系统中我还真的见过10000多行的存储过程,好宏伟啊!
5.避免写超过5个参数的方法,如果有请使用一个类或者结构来传。
6.一个方法只有一个return result; ,不要多次return结果,最好给返回结果赋值,最后return result;
7.不要给很简单的代码加注释,会有噪音的,会让人误解的,因为你写的大多数情况下很片面。
8.记录日志的时候不要到处都记,有条件的情况下针对客户一次操作(比如下单)只记录一条日志。
4.2相互检查过程
在他写好统计单词的方法封装完dll,以及我按照提前约定写好相应的接口和调用他那边的方法之后,我们就开始相互检查对方的代码,相互提出意见,基本满足我们提前的约定规范,遇到的问题他那边忘记把类公用,导致封装之后我这边一直添加引用找不到方法,最后改了之后两者就成功融合了。
5、计算模块接口部分的性能改进
5.1改进一:
之前设计的时候我队友将StatisticalWordCount,包括统计字符个数、总行数、字母频数出现整个冗余在main主函数里面,建议他改了之后设计一个抽象Statistical类,最后将具体词频统计的各个功能进行分离,使用工厂方法模式,将代码重构,方便了之后如果要增加其他功能,可直接添加其他具体操作词频的具体工厂即可,就无须整个改动代码。
StatisticalWordCount改进之后的类图:

5.2改进后性能测试:
整体性能分析:

具体产品角色检测性能:

6、 计算模块部分单元测试展示
这里我分别设计了四个测试分别检测我队友写的dll类库以及我自己写的计算的模块和检验扩展功能的字符自动输入和导入功能是否正常使用。
测试计算文章总行数:

测试计算每个单词出现的次数:

测试计算文章总的字母个数:

测试将输入写入文件:

最终效果:

7、 计算模块部分异常处理说明
针对在计算模块的异常处理主要是集中在我队友对词频统计那个模块和我读入文件中,这里主要遇到了并解决了三种异常处理。首先是对词频统计的数组集合做处理越界元素类型以及其他非检查异常。第二种异常就是,处理的是在加载库的时候类的访问运行异常处理;第三种就是针对文件的读出或者写入的IO做运行异常处理。
解决的话我就是用的常见的c#中提供try 和catch块提供得一种结构化的异常处理方案, try catch本身并不会影响系统的性能,在没有发生异常的时候try catch 是不会影响系统性能的。受影响的时候是发生异常的时候。
关键字 try catch finally。先执行try里面的语句,如果抛出异常就会被catch捕获。无论出不出现异常都会执行finally里面的语句。另外不常用的throw关键字:当问题出现时,程序抛出一个异常。
8、描述结对的过程
结对顾名思义我们就是首先查资料进行分工,这里我和我的队友将单词统计这个程序分为了两个部分进行分别开发最后再通过将我队友的代码封装成类库用他的方法,这里我们的分工是我的队友主要设计StatisticalWordCount项目,主要是计算文章的总字符个数、总文本的单词书、以及行数字母的频数、以及将单词频数进行排序等功能,最后他将他的功能进行封装成dll类库供我调用他的方法。
我设计的是CalculateWordCount项目,主要是设计文件的cmd读入命令、添加引用调用他封装的dll对单词的所有统计方法,读取单词文件、以及最后将结果输出到文本中、以及最后设计的一个可展示的界面,将cmd命令转入到form上进行用户自输入,以实现到导入单词文本文件或者用户自动输入文本提交,点击导出按钮输出统计结果。
为了争分夺秒每次我们都会在每次有课之前提前到争取时间进行商议结伴编程:

9、PSP表格记录下你在程序的各个模块上实际花费的时间
|
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
|
Planning |
计划 |
35 |
45 |
|
Estimate |
估计这个任务需要多少时间 |
65 |
60 |
|
Development |
开发 |
50 |
60 |
|
Analysis |
需求分析 (包括学习新技术) |
15 |
15 |
|
Design Spec |
生成设计文档 |
10 |
15 |
|
Design Review |
设计复审 (和同事审核设计文档) |
10 |
10 |
|
Coding Standard |
代码规范 (为目前的开发制定合适的规范) |
5 |
5 |
|
Design |
具体设计 |
20 |
30 |
|
Coding |
具体编码 |
90 |
120 |
|
Code Review |
代码复审 |
45 |
60 |
|
Test |
测试(自我测试,修改代码,提交修改) |
60 |
80 |
|
Reporting |
报告 |
100 |
100 |
|
Test Report |
测试报告 |
45 |
50 |
|
Size Measurement |
计算工作量 |
20 |
20 |
|
Postmortem & Process Improvement Plan |
事后总结, 并提出过程改进计划 |
15 |
10 |
|
|
合计 |
585 |
950 |
10、附加功能
结合老师的博客要求增加的功能,这里进行设计我这里对老师发布的要求的理解“提供可供用户交互的按钮和,实现-i -m -n -o 这四个参数的功能”应该就是设计一个可以供用户与系统交互的界面,也就是实现我们之前设计的cmd读入-i -m -n -o命令的效果。设计效果以及最终测试如下:
首界面:

10.1首先演示第一种自动导入:
直接导入文件:



10.1首先演示第二种文本框输入:
我先把测试文件复制粘贴过来到文本框里,就不导入文件了,直接输入mn点击开始统计,最终存入文件和显示在屏幕上:

10.3检验是否成功

最终于前面设计的基础cmd运行效果一样,证明设计扩展成功。
11、代码提交
这里我们git总共分了三次提交,第一次就是在写好我和队友个自己功能模块后进行提交。第二个版本就是,我将队友的功能添加好类库引用拼接好之后实现cmd 的单词词频统计后的提交。第三次就是在整合好前面的功能后,将第二个版本生成类库供窗体应用程序添加引用,最后实现用户的交互。
第一个版本:


第二个版本:

第三个版本:


总结
因为和队友之前就又过很多次合作,所以这次合作的还是比较顺利,对于本次的结对编程又一次体会到了两个人按照一定的约束规则进行合作而产生1+1>2的的效果,也真正感受到了结对编程的好处与魅力,在这次过程中通过代码互审找出我和队友的不足并及时改正,并通过这样的过程使自己不对总结经验和自己的不足并不断提升。
通过这次也对技术也有了一点的深刻认识,特别是这次对异常处理,加上之前在做C#项目的一些比赛或者私活中时,我也思考过如何有效地在项目团队中实践异常的处理。 我觉得首先,异常处理应该是系统设计规约的一部分出现在系统设计文档中,而不仅仅是一种技术实现。
作为设计文档的一部分,异常处理应该着眼于系统容错性和稳定性。然后在根据这个规约,再来具体讨论和选择异常处理中使用的各种技术细则。 比如,在设计功能服务时,必须在功能服务的调用接口处有异常处理,否则客户端传过来的任何有害数据都可能让服务器挂掉。 而且对异常的处理在系统的设计中,必须有明确说明,不能随便在哪个模块中处理异常。 以上是自己对异常处理的个人看法。