项目的源代码在Github上托管,可以在这里查看。
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 80 | 120 |
Estimate | 估计这个任务需要多少时间 | -- | 2100 |
Development | 开发 | -- | -- |
Analysis | 需求分析(包括学习新技术) | 360 | 300 |
Design Spec | 生成设计文档 | -- | -- |
Design Review | 设计复审(和同事审核设计文档) | 120 | 240 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 80 | 120 |
Design | 具体设计 | 120 | 120 |
Coding | 具体编码 | 1000 | 1000 |
Code Review | 代码复审 | 240 | 300 |
Test | 测试(自我测试,修改代码,提交修改) | 180 | 120 |
Reporting | 报告 | 240 | 180 |
Test Report | 测试报告 | 30 | 60 |
Size Measurement | 计算工作量 | 40 | 60 |
Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 100 | 100 |
合计 | 2240 | 2100 |
设计思路
WordCount程序是十分经典的程序,要求能正确地统计程序文件中的字符数、单词数、行数以及其它信息。
程序由C++编写,由于现存对中文等非英文字符的编码方式很多(UTF-8, UTF-16, GBK...),因此我们出于实际与效率的考虑并不支持对包含中文的文本文件进行统计,只支持对ASCII编码文件。
在该结对项目当中我所负责的部分主要是对读取文件的内容并统计其中的信息,在完成编写之后我将功能打包到了WC
模块之中以便于操作。模块的接口十分简单且隐藏了大部分细节,以降低模块之间的耦合度。
我的总体思路是这样的:打开一个文件之后,程序通过一个大的while循环模拟一个状态机,将文件中的字符一个一个作为状态机的输入,并将统计信息的变化(字符数+1、行数+1等操作)作为状态机的输出。在文本的所有字符皆由状态机处理完毕之后,我们便能得到最终的结果。
这样的设计主要有两个优点:
- 程序的整体结构十分简单,只需要顺序地一个一个读取文件中的字符,而不涉及回退、查看当前位置之后的字符等操作,降低了程序的复杂性,使bug不容易出现。
- 由于程序的整体结构已经确定,引入新的功能将十分简单。因为我们的程序整体是一个状态机的模式,而一个新的功能就相当于一个有新的输出的状态机,因此引入新的功能就相当于将已有状态机与新的状态机“并联”,我们几乎不需要更改已有的代码,而只需要添加新的代码就可以添加新的功能,这说明程序的可拓展性很强。
递归匹配通配符以及GUI的实现则主要由另一位组员实现,大致的思路为递归对文件夹下所有的文件进行文件名是否匹配通配符的判断,而GUI则使用QT框架完成,具体的细节这里就不再赘述。
性能改进
在最初的设计中我们每一次都使用fgetchar()
从文件中读取一个字符进行处理,这导致我们进行了大量的IO操作,效率十分低下,且占用了大量的程序执行时间。在进行性能分析之后我们发现,程序运行的绝大部分时间都是IO函数的操作,因此这成为了程序速度的瓶颈,于是我们对读取文件内容的方式做出了改进。

在改进之后,我们使用一个buffer
数组来作为数据的缓冲区,在一开始我们先尝试从文件中读取MAX_BUFFER_SIZE
个字符到buffer
数组中,然后一个一个地处理字符,并在处理完之后再次尝试读取MAX_BUFFER_SIZE
个字符... 直到文件读取完毕,所有字符都处理一次之后结束。

采用新的IO方式之后,程序的效率得到了很大的提升,因为免去了多余的系统调用的overhead,程序的执行时间也得到了很大的提升,从60ms变成了48ms,提升了15%。
总结
完成本项目之后,我最大的感想就是结对项目与个人项目是有很大的不同的。在个人项目中你不需要考虑团队中其他人的心情,可以随意自己决定模块的设计、项目的进度等事情。而在做结对项目的工程中,因为需要和同学一起做项目,因此模块的接口设计、项目的进度等事情都需要与同学协调。
由于需要和同学一起开发,对代码的可读性与规范性也有了很高的要求,如果代码质量差,如使用了大量的全局变量或模块间耦合度很高,则另一人在对代码进行更改时,由于对代码的整体结构不如写它的人熟悉,就很容易引入一些很难定位的bug,导致项目时间的浪费,造成不必要的麻烦。
总的来说,完成这个项目对我来说是一次十分宝贵的经验,我在其中学到了许多软件工程知识的应用,并对现代软件工程的许多概念有了更好的掌握,有很大的收获。
网友评论