系统分析与设计课程项目 WordCount 结对编程

系统分析与设计课程项目 WordCount 结对编程

作业说明

合作者:

201631084230(只有我一个人,“合作者”这个标题有些不合适了)

代码地址:

https://gitee.com/mxhkkk/Wc/tree/complete/

本次作业的链接地址:

https://edu.cnblogs.com/campus/xnsy/2018Systemanalysisanddesign/homework/2188

为什么只有我一个人

个人项目完成后,我看了很多人的作业博客,也评论了很多,但始终没有发出这样一条评论:“我们的程序设计风格很像,结对编程我们一起吧”,确实是没有发这样的评论。后来才发现,团队项目组队因此简单了,一个人再找一个两人组就可以了。

PSP表格

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

自审

  • 命令行参数解析,不准确,没有错误提示信息。我现在意识到这是一个复杂的过程,决定使用CLI来重构。
  • 根据文件名来生成文件的操作类,这也挺复杂的。有三种不同形式的文件,文件名必须是合法的,文件名中可能带有路径,还有文件通配符的处理,是否支持文件夹操作以及文件夹的递归。
  • 统计过程是低效的,在设计和性能之间无法做到很好地权衡。
  • 结果类似乎没有必要应用组合模式。
  • 不能很好地利用流的思想来做设计。

修复问题及关键代码

删除Command层

发现现在的Command类完全变成了一个委托类,删除这一层次。

Parser返回handler的集合,Invoker直接调用handler执行方法。

添加并行策略

删除GUI的调用策略,改为并行和串行两种调用策略。当处理文件有多个且计算机支持并行时采用并行策略,否则采用串行策略。

下面验证一下并行的效果如何:

并行的效果还是很明显的,我测试用的计算机是双核的,如果是四核、八核的计算机那就更快了,因为我在设计中考虑到了伸缩性。

测试文件的地址:https://gitee.com/mxhkkk/Wc/blob/complete/src/test/java/test.zip

有趣的是,测试文件夹大小有60多MB,而压缩后只有不到1MB,这是因为文件夹中有很多重复的文件,为了方便我直接复制进去的。这个现象引发了我对Wc程序的一个思考,我们也可以像压缩软件那样识别相同的文件,来避免重复计算。

并行任务类代码:

private static class Task implements Callable<ResultItems> {
	private final List<AbstractHandler> handlers;
	private final File file;

	public Task(List<AbstractHandler> handlers, File file) {
		this.handlers = handlers;
		this.file = file;
	}

	@Override
	public ResultItems call() throws Exception {
		ResultItems items = new ResultItems();
		for (AbstractHandler handler : handlers) {
			items.add(handler.execute(file));
		}
		return items;
	}
}

将GUI类界面显示与操作职责分离

为WcFrame添加操作类,分离类的职责

为GUI添加取消操作,并提高GUI界面的响应性

为WcFrame添加一个取消按钮,并将计算操作移到其他线程中,提高GUI的页面响应性

WcFrameOper中计算并设置结果的代码:

public void calculateAndSetResult() {
	SwingUtilities.invokeLater(() -> frame.setBusyState());

	future = exec.submit(new CalculateTask());

	String result = null;
	try {
		result = future.get();
	} catch (InterruptedException e) {
		result = "任务失败";
	} catch (ExecutionException e) {
		result = "任务失败";
	} catch (CancellationException e) {
		result = "任务取消";
	}

	final String finalResult = result;

	SwingUtilities.invokeLater(() -> {
		frame.setResult(finalResult);
		frame.setLeisureState();
	});
}

借鉴流的思想

有些函数式编程思想会使代码更简单,比如集合排序及toString():

@Override
public String toString() {
	return items.stream()
			.sorted(Comparator.comparingLong(ResultItem::separatorCount).thenComparing(ResultItem::getFileName)
					.thenComparingInt(ResultItem::getId))
			.parallel().map(ResultItem::toString).collect(Collectors.joining("\r\n"));
}

在单个文件的处理过程中也可以基于流的思想来实现并行,这要求统一handler类的处理过程,并将字符流在多个handler之间传递,但受现在程序结构的限制,实现这一想法是非常困难的。好在有高层次多个文件处理的并行,程序对CPU的利用率还算可以,但在处理单个超大文件时就不适用了。

部分测试代码(使用JUnit4)

CliParser类的部分测试代码

@Before
public void setup() {
	FileName.restore();
}

@Test()
public void testGuiFailHasOtherOption() {
	String[] args = new String[] { "-x", "-w" };
	try {
		new CliParser().parse(args);
		fail();
	} catch (ParseException e) {
		assertEquals(ParseExceptionMess.CLI_SYSTEM_MESS, e.getMessage());
	}
}

@Test()
public void testGuiFailHasArg() {
	String[] args = new String[] { "-x", "code.txt" };
	try {
		new CliParser().parse(args);
		fail();
	} catch (ParseException e) {
		assertEquals(ParseExceptionMess.GUI_USE_ALONE, e.getMessage());
	}
}

handler包的部分测试代码

@Test
public void testExecute() throws IOException {
	File stopFile = new File("D:\\eclipse_n_java\\Wc\\src\\test\\java\\com\\mxh\\wc\\handler\\stopWord.txt");
	Files.getInstance().setStopWordFile(stopFile);

	File file = new File("D:\\eclipse_n_java\\Wc\\src\\test\\java\\com\\mxh\\wc\\handler\\testStopWords.txt");

	SelectWordCountHandler handler = new SelectWordCountHandler();
	assertEquals(new ResultItem(file.getPath(), Args.STOP, 20), handler.execute(file));
}

总结

个人编程过程中,会有很多意外的错误,这些错误大多是由于某个细节的疏忽而造成的,结对编程就可以很好地避免这一问题,两人都犯同一个低级错误的概率很小。不得不说,我浪费了一次结对编程的机会。

posted @ 2018-10-17 19:09  MXHKKK  阅读(230)  评论(0编辑  收藏  举报