寒假作业2/2
这个作业属于哪个课程 | 2021软件工程实践|W班 (福州大学) |
---|---|
这个作业要求在哪里 | 寒假作业2/2 |
这个作业的目标 | 1.阅读构建之法并提问2.WordCount编程 |
其他参考文献 | Java正则表达式 | 单元测试和性能测试 |
阅读构建之法并提问
PSP表格相关
PSP的预估时间与实际耗时都需要由工程师亲自填写,并且能够根据预估时间与实际耗时的不同,反映出一些问题
- 我认为,软件开发过程中,由于BUG的存在,往往时间是最难以去确定的,这会导致预估与实际时间有偏差,难以保证两者时间相同
- 在这次实际使用中,我感觉在编程时较为难去计算实际时间,不免会有一些碎片化时间难以去记录,而且在编程中如果在意耗时,反而降低效率,所以对于PSP表格这种衡量方式有点不理解
结对编程相关
讲义第3章b节,详细介绍了结对的编程模式。根据作者的介绍,有许多优秀的计算机领域公司的创始人均为两人,二人合作也确实造就了公司的成功,说明了结对的工作方式,确实有利于提升效率
- 然而,实际上是现如今大部分公司均是采取团队编程的方式,很少听说结对编程的工作模式(我是在讲义中第一次听说)。较少采用结对编程的原因,讲义中并没有系统地说明
- 我认为,可能是公司的项目工作量对于两人来说确实太大,结对的方式,虽然能提高效率质量,但是在较大项目面前,战线不可避免地比团队编程拉得长。进而我想,是否能有种折中的方案,在团队编程中,将项目分化,分化后的工作再进行结对的模式?这样的方式是否会更加有效?
团队编程相关
讲义中提到,团队成员有着不同的角色,但是都希望个人能投入工作,积极参与
- 实际上,在大学阶段,不同个体之间的编程水平确实会有差别,我们无法强求平等。但是往往会因这种原因,导致个体的积极性不高,最终导致双方认知上的偏差,产生争吵,如何平衡是一个问题
- 我认为,或许过于追求对于项目贡献的相当是不太妥当的。适合的人,做适合的事。尽管客观有所不同,但是只要都有付诸努力,也能提高参与感
代码规范相关
书中提及到好的代码规范,有助于阅读和理解,并且对于项目的开发同样也有好处
- 我也是一个对于格式会有强迫的人,认可作者的观点,也是有在打代码中遵循规范。
- 然而,要有好的代码规范,就一会一定程度上降低自己的开发效率。在学习过程中,也有时限较为紧张的时候。那么,在有时间压力的情况下,是否应该对于代码规范适当放松?
绩效管理相关
博客讲义中提到,对于软件开发人员的绩效的认与评价一直未有一个合理明确的标准。在文末提到了某互联网公司的一个业绩与价值观的评价体系
- 讲义虽以简单、话糙理不糙的说法,解释了这一评价体系。绩效方面较好理解好与差的关系,但是价值观层面却没有给出比较准确的标准。是为人价值观方面?还是公司的价值追求与个人价值追求是否相符?
冷知识
404错误,想必大家都很熟悉了。是指用户浏览网页时,服务器因某种原因无法正常提供信息或无法回应所返回的页面,即「找不到该页面」,又被称为「互联网的最后一个页面」。
关于404说法的由来众说纷纭,其中有一种说法称,404源于「404房间」。
相传互联网的第一架服务器,架设在欧洲核研究组织的404号房。如果要打开网页,就得向404房的Berners Lee提交申请,如果他没在房间内,就会出现「404 not found」。
WordCount编程
Github项目地址
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟 | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 1.5 Days | 2 Days |
Development | 开发 | ||
· Analysis | · 需求分析(包括学习新技术) | 90 | 120 |
· Design Spec | · 生成设计文档 | 15 | 20 |
· Design Review | · 设计复审 | 5 | 10 |
· Coding Standard | · 代码规范(为目前的开发指定合适的规范) | 10 | 10 |
· Design | · 具体设计 | 30 | 30 |
· Coding | · 具体编码 | 120 | 180 |
· Code Review | · 代码复审 | 10 | 5 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 180 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 10 | 20 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 时候总结,并提出过程改进计划 | 20 | 30 |
Total | 合计 | 440 | 615 |
解题思路
- 创建一个
Lib
类,在该类里面实现词频统计的各项需求 - 将基本的四项功能划分为四个方法,分别实现
- 需求涉及到字符串检验,学习并运用Java的正则表达式进行检验
- 需求设计词频统计,key-value形式,学习并运用
map
- 学习并使用效率高的IO方法
代码规范
接口设计与实现
接口设计
-
分为两类,在工具类
Lib
中具体实现,WordCount
通过调用Lib
类的方法进行文件处理 -
按需求总体分为4个主要功能函数,其余若干判断、I/O函数
-
能根据需求不同,在
handleFile()
中增减功能private int characterNum; private int wordNum; private int lineNum; private final String inputFile; private final String outputFile; private Map<String, Integer> map; public List<Map.Entry<String, Integer>> wordsRank; /**Lib构造函数 * @param inputFile 输入文件地址 * @param outputFile 输出文件地址 */ public Lib(String inputFile, String outputFile) /** *调用各功能的函数 */ public void handleFile() /** * 将文件读取进字符串 * @return 文件内容字符串 */ public String readFile() /** * 计算字符数 * @param str 文件字符串 */ public void countCharacter(String str) /** * 计算有效行数 * @param str 文件字符串 */ public void countLine(String str) /** * 计算单词数,并加入map * @param str 文件字符串 */ public void countWord(String str) /** * 单词排序 */ public void sortWords() /** * 写入输出文件 */ public void setOutputFile()
功能实现
- 输出单词统一为小写
- 在添加进
map
时,执行toLowerCase()
方法
- 在添加进
-
统计文件的字符数
-
不考虑中文字符,且任意ASCII码均算做一个字符
直接返回字符串长度即可
public void countCharacter(String str) { characterNum = str.length(); }
-
-
统计文件的单词总数
-
单词以分隔符(非字母数字字符,空格)分割
调用
split
方法,将字符串以分隔符分割String[] list = str.split("[^A-Za-z0-9]");
-
单词以至少4个英文字母开头,跟上字母数字,不区分大小写
将分割后字符串各自匹配单词正则
String WordPattern = "^[A-Za-z]{4}[A-Za-z0-9]*"; public boolean isWord(String str) { return Pattern.matches(WordPattern, str); }
-
-
统计文件的有效行数
-
任何包含非空白符的行,都需要统计
调用
split
方法,将字符串以"\n"
分割,将分割后字符串匹配有效行正则String LinePattern = "[\\s\\S]*\\S+[\\s\\S]*"; String[] list = str.split("\n"); public boolean isLine(String str) { return Pattern.matches(LinePattern, str); }
-
-
统计文件词频
-
按频率降序,频率相同的单词,优先输出字典序靠前的单词
tree map
默认对key
进行字典序排序,无法对value
进行排序将键值对放入
List
中再重写比较器Comparator
,实现对value
排序综合起来,实现不同频降序,同频按字典序升序
private Map<String, Integer> map; public List<Map.Entry<String, Integer>> wordsRank; public void sortWords() { wordsRank = new ArrayList<>(map.entrySet()); Comparator<Map.Entry<String, Integer>> comparator = (o1, o2) -> o2.getValue()- o1.getValue(); wordsRank.sort(comparator); }
-
性能改进
- I/O使用用缓冲流,提高读写效率
- 1500W字符检测,耗时27S
for (int i = 0; i < 5000000; i++) {
writer.write("hs.(* word123 \n fqAJS \n \t%^& \n");
}
单元测试
单元测试使用JUnit5测试框架进行测试
-
测试判断有效行
void isLine() { final String test = " "; assertEquals(false, lib.isLine(test1)); } final String test1 = " "; //false final String test2 = "\t"; //false final String test3 = " \t"; //false final String test4 = " \t\r asad \t "; //true final String test5 = " \t asad"; //true final String test6 = "asde \t\r"; //true
要求:包含任何非空白字符的行,都需要统计
用例:1.单个空格、2.单个空白字符、3.多个空白字符、
4.多空白字符包含可见字符、5.多个空白字符在前、6.多个空白字符在后
考虑:1.处理时以
split("\n")
分割,故测试用例中没有\n
字符; 2.考虑了无效行的多种情况
3.考虑有效行多种情况
-
测试判断单词
void isWord() { final String test = "abc"; assertEquals(false, lib.isWord(test1)); } final String test1 = "abc"; //false final String test2 = "abcd"; //true final String test3 = "abc123"; //fasle final String test4 = "abcd123ab"; //true final String test5 = "ABcd123ac"; //true final String test6 = "abcd@123##$$123%^%*[]"; //false
要求:至少以4个英文字母开头,后跟任意个数字母数字符号,且不分大小写
用例:包含少于4个字母,字母与数字混合,
大小写混合,字母数字与其他符号混合
-
测试统计字符数
void countCharacters() throws IOException { BufferedWriter writer = new BufferedWriter(new FileWriter(inputfile)); final String test = "Ab2$\r\n"; for (int i = 0; i < 10; i++) { writer.write(test); } writer.flush(); writer.close(); lib.countCharacter(lib.readFile()); assertEquals(test.length() * 10, lib.getCharactersNum()); } //结果相同
用例:包含了大小写字母、数字、非空白字符、空白字符
-
测试统计有效行数(函数模式与上相同)
final String test = "a1m23\n \n\t\n\r\t\n \r*123&(pas\r\t\n"; final int lineNum = 2; assertEquals(lineNum, lib.getLineNum()); //结果相同
用例:包含了字母数字、空白字符、多个空白字符、
多个非空白字符与多个非空字符的组合
-
测试统计单词数(函数模式与上相同)
final String test = "absd alskdf AsIs123\nasd\nidol][75asd\n"; final int wordNum = 4; assertEquals(wordNum, lib.getWordNum()); //结果相同
用例:包含了被空白字符、非字母数字符号分割的情况
-
测试单词排序(函数模式与上相同)
final String[] list = { "zaad","zaad","zaad", "aaab","aaab","aaab", "aaaa","aaaa","aaaa", "bbbb222","bbbb222", "dddd444", "dddd445"}; lib.countWord(lib.readFile()); lib.sortWords(); //有多个对比函数,此处仅举例,其余类似 assertEquals("aaaa", lib.wordsRank.get(0).getKey()); assertEquals(3, lib.wordsRank.get(0).getValue()); /* 排序后,列表为 aaaa:3 aaab:3 zaad:3 bbbb222:2 bbbb222:2 dddd444:1 dddd445:1 */
用例:1.同频,不同字典序
2.不同频
-
覆盖率截图
Lib中没有测试的方法/行
- Lib用于给WordCount调用处理文件的函数
handleFile()
和输出函数setOutFile()
优化覆盖率
- 尽量一个功能一个方法
- 但是也不必刻意追求覆盖率,关键是方法的正确性
- Lib用于给WordCount调用处理文件的函数
异常处理
-
Lib
中的异常均为I/O,交由系统处理 -
WordCount
中的main()
对于未满两个传入参数的异常,做出输出异常信息,并直接返回if (args.length < 2) { System.err.println("Require 2 Parameters"); return; } Lib lib = new Lib(args[0], args[1]); lib.handleFile();
心路历程与收获
收获
- 学习并使用Git和Github
- 学习并使用JProfiler进行性能测试
- 学习并使用JUnit框架,进行单元测试
- 学习并使用Java的正则表达式
- 了解了阿里巴巴的代码规范,规范了自己的代码习惯
- 更加熟练地使用Idea进行开发
不足
- 因为在开发时,没能理清需求和合理规划设计模块,导致开发效率不高,因此导致PSP表格的时间有不小误差
- 下次开发时,应该顺着PSP表格的步骤走,合理规划、合理设计、看清需求,提高自己的开发效率