软工实践第二次作业

作业链接#

https://edu.cnblogs.com/campus/fzu/FZUSoftwareEngineering1816W/homework/2085

1.Fork仓库的Github项目地址:#

https://github.com/wyz0918/PersonProject-Java

2.PSP表格#

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

3.计算模块接口的设计与实现过程#

一、相关类设计

  • 一个Main类,用于处理文件输入输出,Main类对象直接调用Lib类中的方法。
  • 一个Lib类作为工具类,用于计算字符数、行数、单词总数,出现次数等静态方法。

二、相关函数设计
1、Main类中的方法

  • readFile方法实现从控制端口读入文件名,并且返回文件对象。
  • writeFile方法实现把信息写入指定文件。

2、Lib类中的方法

  • countChars方法实现计算字符数。
  • countLines方法实现计算行数。
  • countWords方法实现计算单词数。
  • countTop10方法实现单词先从出现频率降序排列,再按照若相同频率按照字典序升序排列并且找出符合条件的前10个单词。

三、关键代码和算法

  • 单词的划分
    单词的划分需要String.split()方法来通过对空格和其他字符进行划分,并进行单词的匹配,可以使用正则表达式。若使用macher.find(),matcher.group()方法则会把不符合要求的单词计算在单词数里,比如123file。

  • 字符数的计算
    字符数因为包括换行符空格等,因此若用readline(),则会忽略换行符,因此需要用read(),把全部字符当成一个字符串并返回。

  • 行数的计算
    行数需要去掉都是空格的行,因此使用trim(),并且用isEmpty()判断。

  • 单词数的计算
    单词总数只需要计算满足正则表达式的单词即可,其中包括重复的单词。

  • 单词的排序
    单词的排序需要按照从出现频率降序排列,再按照若相同频率按照字典序升序排列,主要使用了Collection.sort()方法。

4.计算模块接口部分的性能改进#

改进计算模块性能上花费了120分钟左右。
Jprofiler性能测试:

从性能分析图可知String类开销较大
改进的思路:使用正则表达式减少判断的复杂度,使用hashmap提高查询速度。

5.计算模块部单元测试展示#

input.txt文件:

测试的思路:将input.txt文件作为参数输入,对照返回值是否等于预期。

一、countChars方法单元测试:

@Test
	public void testCountChars() {
		
		File f = new File("input.txt");
		assertEquals(Lib.countChars(f),44);
	}

二、CountLines方法单元测试:

@Test
	public void testCountLines() {
		
		File f = new File("input.txt");
		assertEquals(Lib.countLines(f),3);
	}

三、CountWords方法单元测试:

	@Test
	public void testCountWords() {
		
		File f = new File("input.txt");
		assertEquals(Lib.countWords(f),5);
	}

四、CountTop10方法单元测试:

	@Test
	public void testCountTop10() {
		
		File f = new File("input.txt");
		String[] str = {"file","abcd","apple","file123"};
		String[] str2 = {"2","1","1","1"};
		ArrayList<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>();
		list = Lib.countTop10(f);
		for (int i = 0; i < 10 && i < list.size(); i++) {

			assertEquals(list.get(i).getKey(),str[i]);
		}
		for (int i = 0; i < 10 && i < list.size(); i++) {

			assertEquals(list.get(i).getValue().toString(),str2[i]);
		}
	}

测试的思路:将input.txt文件作为参数输入,对照List中存储的值看是否符合预期的结果即可,我用了循环一个个进行比对,能比较全面地覆盖测试用例。

三、测试覆盖率截图:

6.模块部分异常处理说明#

  • 读入文件时若失败则抛异常
    代码:
public static int countChars(File f) {

		int characters = 0;
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(f));
			while (br.read() != -1) {
				characters++;
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				br.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		

错误对应的场景:当读入文件失败

7.运行测试#

输入文件:

输出文件:

8.收获与体会#

这是我第一次比较系统地开发一个项目,收获非常多,首先发现了前期的规划非常重要,当明确了需求和思路,进行相关代码的设计后,编码的效率才会提高,而不应该闷头就敲代码。这次的代码用到了正则表达式,也是最近编译原理有着重讲解的内容,能实际运用加深了对它的理解。另外代码的规范也非常重要,这也是我需要改进的地方,面向对象的思想理解的不够深刻,刚开始只写出一个类,并没有更加细致地划分,后面重新进行了优化。

原文地址:https://www.cnblogs.com/wyz0918/p/9615789.html