第三次作业--结对编程

一、作业地址

Github项目地址:https://github.com/gentlemanzq/WordCount.git(用的是同伴的)

作业链接:https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/homework/2882

结对同伴博客地址:https://www.cnblogs.com/gentlemanzq/

我的博客地址:https://www.cnblogs.com/Ysml/

二、结对过程描述


 

2.1 总体情况分析

  首先,根据题目要求,将代码分解为几个模块,确立好每个模块的具体函数内容。其次,根据个人的编程能力,将不同模块分配给

相应的人 进行完成。分配任务后,两人一起完成,其中一人完成自己相应模块的编程,另外一个便审查代码,指出错误之处或者不同

的思路与见解,最后两人相互讨论,选取最优的编程方法。

2.2 存在的问题

  由于两人的编程水平存在差异,有时存在无法理解同伴编写代码的具体意思,又因为思路的不同,存在代码具体位置的争论。

2.3 解决方法

  对存在的问题,单独拉出来,由编程的人对不理解的人进行讲解,其次对相应调用函数的不认,通过百度进行了解认识。

附结对照片:

 

三、PSP表格


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

 四、解题思路


  拿到题目后,先仔细阅读题目,我们根据题目要求,构建了一个大概的框架,将题目要求分解成不同的模块。然后根据模块的内容

找出学过相对应的代码内容。随后思考代码的可行性,是否存在漏洞。遇到不会编写的代码,上网查找资料,图书馆借阅书籍查找相关问题。

五、设计实现过程


  通过审读题目要求,可以将整个程序分为如下部分:

1.统计文件字符数函数

2.统计有效行数函数

3.统计文件中各单词的出现次数函数,并只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。

4.将程序输出结果写入txt文件函数  (几个函数相互独立)

  (当对代码进行封装时,就可以将以上每个函数封装成类,需要用时,可以直接调用。)

统计文件中各单词的出现次数函数,并只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。为关键函数,流程图如下:

 

  

算法关键、独特之处:提取单词的时候,一并统计了频率。并且在输出排序时,调用Orderby算法,可以直接按频率和字典序输出结果

PS:增加客服需求新功能时,只需将一些变量变成由客户输入即可。

 

六、代码规范与互审


 

代码规范:

       1.   不要冗余无用代码,过于冗余的代码可以清理一下,一些已经注释掉的代码可以删除

  2、不变的值,尽量写个常量类。

  3、尽量使用if{}else,不要一直if去判断。

  4、减少循环调用方法;减少IO流的消耗资源。

  5.   当一行代码太长时,将其截断成两行写。

  6.   常用缩进和换行,使代码层次清晰,明了。

  7.   注释的量不应该少于代码量的三分之一。ps(变量统一使用例如/// <param name="s">文件读入路径</param>的注释方式)

  8.   定义变量名字和方法名字的时候尽量使用英文缩写,或者拼音缩写,便于识别。

  9.   对泛型进行循环时,都采用foreach而不使用for。

  11. 对于功能函数写入一个function文件夹中,便于以后功能升级。

  12. 一屏原则:一个方法体的代码幅应该在一屏比较和合理;逻辑复杂的代码可以抽离出方法体; 

(这只是一部分,当然还有很多,就不一一列举了)

代码互审:

 在编写统计字符个数时,最初代码如下:

public static int agefile()//打开文件并统计字符个数
{
       string fileName = @"D:\新建文本文档 (3).txt";
       FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
       byte[] buf = new byte[fs.Length];
       fs.Read(buf, 0, buf.Length);
       fs.Close();
       return buf.Length;

}

 

 经过代码复审后,发现打开文件和统计字符个数时过于繁杂,便有了以下代码:这样一来,代码精简很多,占用空间更少。

 public static int agefile()//打开文件并统计字符个数
{
            string str = File.ReadAllText(@"C:\Users\LiuQi\Desktop\新建本文档案(3).txt");
            int num = Regex.Matches(str, @".").Count;
            return num + lines() - 1;
}

 

   其次,在统计单词出现频率和按要求输出时时,最开始傻傻的认为按需求一步步编写代码就能解决。但是实际操作起来却

发现存在很多问题,许多简单情况下能够操作,在复杂情况下,存在很多很多的限制,不能轻易解决。
例如:在按字典表输出时,按原思路就是按顺序判断然后输出,做到一半都发现太麻烦了,就去百度了一下有没有简单算法,

便发现C#自带字典数排序,只需调用即可。

 七、模块接口部分的性能改进


 

   1、改进计算模块性能上所花费的时间:45分钟

   2.、改进思路:将统计行数,字符数的函数封装成一个类,到时候可以根据用户需求只需改变参数即可。

   3、耗时最多的函数:(运用字典数统计字符总数和其出现的频率)

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 k = 0;
            string[] newwords = ynword.ynword1(words,ref k);
            string[] newwords1 = new string[k];
            for (int i = 0; i < k; i++)
            {
                newwords1[i] = newwords[i];
            }
            foreach (string word in newwords1)
            {

                if (frequencies.ContainsKey(word))
                {
                    frequencies[word]++;
                }
                else
                {
                    frequencies[word] = 1;
                }
            }
            return frequencies;
        }

 

八、单元测试与部分异常处理

 


在没有对函数进行封装时,我们对各自对对方写的代码进行了单元测试

1、首先针对逻辑上第一个调用的计算行数的功能模块linescount进行测试(函数如下):

public class linescountTests
    {
        [TestMethod()]
        public void linesTest()
        {
            path.s = @"D:\se.txt";
            int x = 0;//第一次测试时输入5,第二次输入0
            Assert.AreEqual(x, linescount.lines());
           // Assert.Fail();
        }
    }

 

测试结果:第一次在记事本输入两行测试成功,但在输入0行时,测试失败。此处出现问题,当没有输入文本时,行数没有进行判断,所以出现错误。

2.测试asccount类(统计有多少个字符)封装的类如下:

public class asccountTests
    {
        [TestMethod()]
        public void asccountsTest()
        {
            path.s = @"D:\se.txt";
            int num = 8;
            Assert.AreEqual(num, asccount.asccounts());
            //Assert.Fail();
        }
    }

 

测试结果:当文本不输入字符时,num=0,出现错误。思考后,发现是没有判断为0的情况,出现错误。

3.其余类经过测试并未发生什么问题。

4.功能改进后的主函数:

static void Main(string[] args)
        {
            int temp = 0;
            int max = 0;
            int len = 0;
       //如果命令行有参数执行
            if (args.Count() != 0)
            {
                for (int i = 0; i < args.Length; i++)
                {
                    if (args[i] == "-i") path.s = args[++i];//-i 命令行
                    else if (args[i] == "-n") max = Convert.ToInt32(args[++i]);//-n 命令行
                    else if (args[i] == "-o") path.outputpath = args[++i];//-o 命令行
                    else if (args[i] == "-m")
                    {
                       len = Convert.ToInt32(args[++i]);
                    }
                }
                if (path.s == null || path.outputpath == null)//路径为空则不存在
                {
                    Console.WriteLine("路径不正确,文件不存在");
                }
            }
        //命令行无参数,执行
            else
            {
                Console.WriteLine("不输入参数,请手动输入读入文件路径");
                string s=  Console.ReadLine();
                path.s = s;
                max = 10;
                Console.WriteLine("请手动输入输出路径");
                string s1 = Console.ReadLine();
                path.outputpath = s1;
            }
            Dictionary<string, int> frequencies = function.wordcount.Countword();//调用wordcount中方法统计单词
            Dictionary<string, int> dic1Asc = frequencies.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);//按照字典序进行排序
            int sum = function.wordcount.sum1(dic1Asc);//计算出单词总数量
            Console.WriteLine("字符数:"+asccount. asccounts());//计算出字符数量
            Console.WriteLine("单词总数:" + sum);
            Console.WriteLine("行数:"+linescount. lines());//计算出行数
            //先按照出现次数排序,如果次数相同按照字典序排序
            Dictionary<string, int> dic1Asc1 = frequencies.OrderByDescending(o => o.Value).ThenBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);
            foreach (KeyValuePair<string, int> entry in dic1Asc1)
            {
                if (temp == max)
                    break;
                string word = entry.Key;
                int frequency = entry.Value;
                temp++;
                Console.WriteLine("{0}:{1}", word, frequency);
            }
            Console.ReadKey();
        }

 

九、心路历程与收获

  首先这次的作业由于已经有了上次Github的操作经验,再一次使用时,觉得不在陌生,基础操作都能够完成。在说说代码,初一看感觉并不是很难,但是在

实际操作过程中,却是发现存在各种各样的小问题。(结对编程时,两人对换行符所占字符数发生了奇异,改来改去,发现最开始的代码是对的,而重新写的

代码也是对的,白白的纠结了很多时间。)有些小问题是编写代码时发生的,有些却是知识点漏区产生的。随后就在这些小问题中,浪费了打把的时间,但是

个人看来,效率还是远远大于单人操作。编写代码时,同伴会在一旁发表自己的见解,也会帮忙指出忽略的错误之处,大大提高了效率,因此我觉得1+1>2。

转载于:https://www.cnblogs.com/Ysml/p/10641341.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值