系统分析与设计结对项目——WordCount
结对项目完成WordCount
合作者:201631062507 201631062526(学号)
代码地址:https://gitee.com/WordCountMC/WordCountTeam
本次作业链接:https://edu.cnblogs.com/campus/xnsy/2018Systemanalysisanddesign/homework/2188
1.结对项目PSP编写
我们的项目于10月11日正式开始,在开始编写代码之前,我们先对我们如何完成项目以及对老师布置的作业等进行再一次的商定和讨论。并结合我们平时上课的时间以及项目外等因素,约定了我们一起进行结对编程的时间和地点。最后一起完成了PSP表格的填写:
PSP2.1 |
PSP阶段 |
预估耗时(分钟) |
实际耗时(分钟) |
Planning | 计划 | 30 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 620 | 945 |
· Analysis | · 需求分析 (包括学习新技术) | 30 | 40 |
· Design Spec | · 生成设计文档 | 20 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 40 |
· Design | · 具体设计 | 40 | 45 |
· Coding | · 具体编码 | 350 | 500 |
· Code Review | · 代码复审 | 60 | 100 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 130 |
Reporting | 报告 | 80 | 80 |
· Test Report | · 测试报告 | 30 | 30 |
· Size Measurement | · 计算工作量 | 20 | 10 |
· Postmortem & Process Improvement Plan | .事后总结并提出改进计划 | 30 | 40 |
合计 | 730 | 1055 |
总结分析:这次在项目完成后,发现预估时间和实际耗时相差比较大,其主要表现在开发阶段。由于我们在代码自审和互审阶段,都发现了编码所存在的不规范,所以花了更多的时间去修改代码。还有就是在编码的时候,我们要完成的内容比我们想象得要困难一点,所以在编码的时候花了更多的时间。最后就是测试,这次项目开发采用了TDD,我们要先编写测试代码,再实现接口,这与我们之间采用的方式也不同,所以也花了更多的时间。
2.代码互审
2.1总体情况分析
在审查之后发现,队友的代码已经根据了我们一起编写的小组编程规范进行了修改。修改后的代码风格简洁明了,非常容易理解和阅读。但是仍需注意的地方是,方法里的代码注释要写在对应代码的上面而不是后面,这样会使代码阅读更加清晰。除此之外,在互审之后,发现代码存在一个小bug需要进行改正。
2.2 问题
出现问题的模块在CountUtil类中的returnWords方法。
问题是在测试时发现:当一个文件为空的时候,它会返回单词数为1,而且当有两个文件同时测试时,如果第一个文件的单词数比第二个文件的单词数多的话,只显示第一个的单词数。
2.3 解决方法
在问题提出后,我和队友一起解决了问题,发现问题出在每次统计单词数目所要用的StringBuffer没有进行清空的操作。
3.设计
3.1 结构设计
功能类:主函数类(WordCount类) 接口类(CountUtil类)
WordCount类:
CountUtil类:
3.2 类间关系
3.3 WordCount类的流程图
4.代码说明
4.1扩展功能:
(1)当输入-e时,后面跟停用词表stoplist.txt
首先解析停用词表中的单词:
/**
* 用来解析停用词表中单词
* **/
public String[] stoplistWord(String stopList)
{
String lineString = null;
String[] stopListBuffer = null;
File sl = new File(stopList);
try {
BufferedReader br = new BufferedReader(new FileReader(sl));
while((lineString = br.readLine())!=null) {
//当遇到空格的时候,就为一个单词
stopListBuffer = lineString.split("\\s+");
}
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stopListBuffer;
}
然后判断文件中的单词是否是停用词表中的单词:
/***
*
* 判断该词是否存在于停用词表内
* */
public static boolean inStop(String str, String[] fileBuffer)
{
int count = 0;
for(int i = 0;i<fileBuffer.length;i++) {
//将获得的单词,与停用词表中的单词一个一个的与进行比较
if(str.equals(fileBuffer[i])) {
count++;
}
}
if(count > 0) {
return true;
}else
return false;
}
(2)当输入-a时,查询文件中的代码行数、注释行数、空行数
getCodeLines() 函数:
/**
* 查询文件的代码行数
* @param filePaths 所有有效文件的集合
* @return String类型 查询结果
*/
public String getCodeLines(Set<String> filePaths) {
// 结果字符串
String result = "";
// 缓存变量
String tmp = "";
// 统计次数
int count = 0;
// 得到输入流
FileInputStream is = null;
InputStreamReader ist = null;
BufferedReader br = null;
try {
for (String filePath : filePaths) {
is = new FileInputStream(filePath);
ist = new InputStreamReader(is);
br = new BufferedReader(ist);
while((tmp = br.readLine())!=null) {
// 去除读取的空格,方便识别内容类型
tmp = tmp.replace(" ", "");
// 改行不为空,则计数+1
if (!"".equals(tmp)&&!tmp.startsWith("//")&&tmp.indexOf("//")!=1) {
count++;
}
}
// 拼接结果字符串
result+=filePath+CODE_LINES+count+SPACE;
// 重置count计数变量
count = 0;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// 关闭输入流
is.close();
ist.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} }
return result;
}
getEmptyLines() 函数:
/**
* 查询文件的空行数
* @param filePaths 所有有效文件的集合
* @return String类型 查询结果
*/
public String getEmptyLines(Set<String> filePaths) {
// 结果字符串
String result = "";
// 缓存变量
String tmp = "";
// 统计次数
int count = 0;
// 得到输入流
FileInputStream is = null;
InputStreamReader ist = null;
BufferedReader br = null;
try {
for (String filePath : filePaths) {
is = new FileInputStream(filePath);
ist = new InputStreamReader(is);
br = new BufferedReader(ist);
while((tmp = br.readLine())!=null) {
// 改行不为空,则计数+1
if ("".equals(tmp)) {
count++;
}
}
// 拼接结果字符串
result+=filePath+EMPTY_LINES+count+SPACE;
// 重置count计数变量
count = 0;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// 关闭输入流
is.close();
ist.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} }
return result;
}
getNoteLines() 函数:
/**
* 查询文件的注释行数
* @param filePaths 所有有效文件的集合
* @return String类型 查询结果
*/
public String getNoteLines(Set<String> filePaths) {
// 结果字符串
String result = "";
// 缓存变量
String tmp = "";
// 统计次数
int count = 0;
// 得到输入流
FileInputStream is = null;
InputStreamReader ist = null;
BufferedReader br = null;
try {
for (String filePath : filePaths) {
is = new FileInputStream(filePath);
ist = new InputStreamReader(is);
br = new BufferedReader(ist);
while((tmp = br.readLine())!=null) {
// 去除读取的空格,方便识别内容类型
tmp = tmp.replace(" ", "");
// 改行不为空,则计数+1
if (!"".equals(tmp)&&tmp.startsWith("//")||tmp.indexOf("//")==1) {
count++;
}
}
// 拼接结果字符串
result+=filePath+NOTE_LINES+count+SPACE;
// 重置count计数变量
count = 0;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
// 关闭输入流
is.close();
ist.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} }
return result;
}
(3)当输入-s时,递归处理目录下符合条件的文件
/**
* 得到输入文件中指定文件和指定文件夹下符合条件的文件
* @param names 输入文件的集合
* @param condition 文件夹下的筛选条件
* @return Set<String>类型 所有指定文件和指定文件夹下符合条件的文件
*/
public Set<String> getAllFileWithCondition(Set<String> names,String condition) {
// 存储输入文件和输入文件夹下符合指定条件的文件的集合
Set<String> filePaths = new HashSet<String>();
for (String name : names) {
File file = new File(name);
if (file.isFile()) {
filePaths.add(name);
}else if (file.isDirectory()) {
// 调用处理文件夹下查询符合指定条件的文件
filePaths.addAll(getDirectoryFilesWithCondition(file, condition));
}
}
return filePaths;
}
4.2高级功能:
当输入-x的时候,打开图形化界面操作,调用getUI()方法
/**
* 获得图形界面
*/
public void getUI() {
JFileChooser jFileChooser = new JFileChooser();
JFrame jFrame = new JFrame("文件数据查询");
Container container = jFrame.getContentPane();
container.setLayout(null); JLabel hint = new JLabel("选择文件",Label.RIGHT);
hint.setBounds(10,10,100,30); JTextField name = new JTextField();
name.setBounds(80,10,190, 30);
name.setEditable(false);
name.setFont(new Font("dialog", 1, 15));
name.setBackground(Color.WHITE); JLabel hint1 = new JLabel("输入停用词",Label.RIGHT);
hint1.setBounds(10,58,100,30); JTextField wordList = new JTextField();
wordList.setBounds(80,58,190, 30);
wordList.setFont(new Font("dialog", 1, 15));
wordList.setBackground(Color.white); JLabel hint3 = new JLabel("(单词之间用空格隔开)");
hint3.setBounds(85,85,150,30); JButton confirm = new JButton("确定");
confirm.setBounds(280,58,80,30); JButton choose = new JButton("选择...");
choose.setBounds(280,10,80,30); JLabel hint2 = new JLabel("结果如下:");
hint2.setBounds(10,100,190,30); JTextArea contents = new JTextArea();
contents.setBounds(43,140,300,280); choose.addActionListener(new ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
int returnVal = jFileChooser.showOpenDialog(null);
if(JFileChooser.APPROVE_OPTION == returnVal) {
filePaths.add(jFileChooser.getSelectedFile().getAbsolutePath());
name.setText(jFileChooser.getSelectedFile().getName());
contents.setText(performOperation());
}
}
}); confirm.addActionListener(new ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
CountUtil.USE_STOP = true;
CountUtil.putInFile(wordList.getText()+" ", stopListPath);
STOPLIST_FILE = stopListPath;
}
});
container.add(hint);
container.add(name);
container.add(choose);
container.add(contents);
container.add(wordList);
container.add(hint1);
container.add(confirm);
container.add(hint2);
container.add(hint3);
jFrame.setSize(400, 500);
jFrame.setVisible(true); }
getUI()方法代码
5.测试过程
5.1测试模块建立
此次测试使用的是java提供的单元测试工具进行测试,新建了一个CountUtilTest类,对CountUtil中的接口方法进行测试。在此次单元测试的设计中,我们所设计的测试用例包括了没有内容的.c文间,只有一个单词的.c文件等比较典型的测试用例。
private CountUtil countUtil = new CountUtil();
@org.junit.Test
public void testReturnCharacters() {
Set<String> filePaths = new HashSet<String>();
String File1 = "F:\\out.c";
filePaths.add(File1);
filePaths =countUtil.getAllFilePaths(filePaths);
String result = "F:\\out.c,字符数:46 ";
assertTrue("字符数错误",result.equals(countUtil.returnCharacters(filePaths)));
} @org.junit.Test
public void testReturnWords() {
Set<String> filePaths = new HashSet<String>();
String File1 = "F:\\test.java";
String File2 = "F:\\WordCount";
String stopList = "";
filePaths.add(File2);
filePaths.add(File1);
filePaths =new CountUtil().getAllFilePaths(filePaths);
String result = "F:\\WordCount\\result.txt,单词数:35 F:\\WordCount\\wc.exe,单词数:256100 "
+ "F:\\WordCount\\AimResult.txt,单词数:76 F:\\test.java,单词数:3 "
+ "F:\\WordCount\\ddd\\hello.txt,单词数:2 ";
assertTrue("单词数错误", result.equals(countUtil.returnWords(filePaths, stopList)));
} @org.junit.Test
public void testReturnLines() {
Set<String> filePaths = new HashSet<String>();
String File1 = "F:\\test.java";
String File2 = "F:\\WordCount";
filePaths.add(File2);
filePaths.add(File1);
filePaths =countUtil.getAllFilePaths(filePaths);
String result = "F:\\WordCount\\result.txt,行数 :3 F:\\WordCount\\wc.exe,行数 :3042 "
+ "F:\\WordCount\\AimResult.txt,行数 :3 F:\\test.java,行数 :1 "
+ "F:\\WordCount\\ddd\\hello.txt,行数 :1 ";
assertTrue("行数错误", result.equals(countUtil.returnLines(filePaths)));
} @org.junit.Test
public void testOutPutFile() {
String FilePath = "result.txt";
String Content = "OutPutFile test !";
assertTrue(countUtil.outPutFile(FilePath, Content));
} @org.junit.Test
public void testGetAllFilePaths() {
Set<String> filePaths = new HashSet<String>();
Set<String> resultPaths = new HashSet<String>();
String File1 = "F:\\WordCount";
String FIle2 = "F:\\out.c";
String result1 = "F:\\WordCount\\result.txt";
String result2 = "F:\\out.c";
String result3 = "F:\\WordCount\\wc.exe";
String result4 = "F:\\WordCount\\AimResult.txt";
String result5 = "F:\\WordCount\\ddd\\hello.txt";
filePaths.add(File1);
filePaths.add(FIle2);
resultPaths.add(result1);
resultPaths.add(result2);
resultPaths.add(result3);
resultPaths.add(result4);
resultPaths.add(result5);
filePaths = countUtil.getAllFilePaths(filePaths);
assertTrue("得到的文件错误", resultPaths.equals(filePaths));
} @org.junit.Test
public void testGetDirectoryFiles() {
Set<String> filePaths = new HashSet<String>();
Set<String> resultPaths = new HashSet<String>();
File File1 = new File("F:\\WordCount");
String file1 = "F:\\WordCount\\result.txt";
String file2 = "F:\\WordCount\\wc.exe";
String file3 = "F:\\WordCount\\AimResult.txt";
String file4 = "F:\\WordCount\\ddd\\hello.txt";
resultPaths.add(file1);
resultPaths.add(file2);
resultPaths.add(file3);
resultPaths.add(file4);
filePaths = CountUtil.getDirectoryFiles(File1);
assertTrue("得到的文件错误", resultPaths.equals(filePaths));
} @org.junit.Test
public void testGetAllFileWithCondition() {
Set<String> filePaths = new HashSet<String>();
Set<String> resultPaths = new HashSet<String>();
Set<String> Files = new HashSet<String>();
String File1 = "F:\\WordCount";
String FIle2 = "F:\\out.c";
Files.add(FIle2);
Files.add(File1);
String condition = ".c";
String result = "F:\\out.c";
resultPaths.add(result);
filePaths = new CountUtil().getAllFileWithCondition(Files, condition);
assertTrue("得到的文件错误", resultPaths.equals(filePaths));
} @org.junit.Test
public void testGetDirectoryFilesWithCondition() {
Set<String> filePaths = new HashSet<String>();
Set<String> resultPaths = new HashSet<String>();
File File1 = new File("F:\\WordCount");
String condition = ".c";
filePaths = CountUtil.getDirectoryFilesWithCondition(File1, condition);
assertTrue("得到的文件错误", resultPaths.equals(filePaths));
} @org.junit.Test
public void testGetCodeLines() {
Set<String> filePaths = new HashSet<String>();
filePaths.add("F:\\out.c");
String result = "F:\\out.c,代码行数:2 ";
assertTrue("代码行错误", result.equals(countUtil.getCodeLines(filePaths)));
} @org.junit.Test
public void testGetEmptyLines() {
Set<String> filePaths = new HashSet<String>();
filePaths.add("F:\\out.c");
String result = "F:\\out.c,空行数:1 ";
assertTrue("空行数错误", result.equals(countUtil.getEmptyLines(filePaths)));
} @org.junit.Test
public void testGetNoteLines() {
Set<String> filePaths = new HashSet<String>();
filePaths.add("F:\\out.c");
String result = "F:\\out.c,注释行数:2 ";
assertTrue("注释行错误", result.equals(countUtil.getNoteLines(filePaths)));
}
CountUtilTest()单元测试代码
5.2 单元测试结果
5.3 exe文件测试结果
所有的文件:
out.c用例:
(1)代码行数、注释行数、空行数测试:
(2)单词数、行数、字符数测试
(3)字符数、行数、单词数和代码数、注释行数、空行数测试:
(4)基础功能和扩展功能测试
(5)停用词表测试
(6)UI界面
6.总结
在这次结对项目实践完成之后,我发现1+1是大于2的。我们项目完成的效率远比当时我一个人完成的时候要高出很多,而且我们一起编程可以及时解决所遇到的问题和避免一些不必要的问题。避免走了很多的弯路。除此之外,我还学习到了我队员的编程思想和他的学习方式,这些东西都给我带来了很多的启发和新的思路。总得来说,整个项目的完成也比之前的更加顺利和充满动力。提高了我对完成项目的积极性。
系统分析与设计结对项目——WordCount的更多相关文章
- 系统分析与设计个人作业:WordCount
本次作业gitee地址:https://gitee.com/ackary/WordCount 一.项目简介 1.基础功能 基础功能部分主要实现的功能是统计一个程序设计语言源文件的字符数.单词数.行数, ...
- 结对项目-WordCount
结对作业: 成员:201631062115(me),201631062613(partner) 代码地址:https://gitee.com/ackary/WordCount 作业的链接地址:http ...
- 四则运算生成器功能完善&&界面设计——结对项目
结对成员:何小松 && 李入云 一.对结对编程的认识 优点: 1)程序员互相帮助,互相教对方,可以得到能力上的互补. 2)可以让编程环境有效地贯彻Design. 3)增强代码和产品质量 ...
- WordCount 2.0(结对项目)
序言 合作伙伴 201631062220 201631062120 项目码云地址: https://gitee.com/zhege/WordCount 作业详细要求 系统分析与设计结对项目 ...
- WordCount结对项目
合作者:201631062124,201631062423 代码地址:https://gitee.com/yryx/WordCount 作业地址:https://edu.cnblogs.com/cam ...
- 结对项目——Core设计与实现
写在前面:关于结对编程 结对编程我一直认为是一种非常好的合作方式,他的形式主要是由一个人负责代码编写,另一个人则在一旁即时对写下的代码进行审查,这样可以大大减少代码实现方面的错误. 这次我的结对伙伴是 ...
- 结对作业——WordCount进阶版
Deadline: 2018-10-7 22:00PM,以博客提交至班级博客时间为准 要求参考来自:https://www.cnblogs.com/xinz/archive/2011/11/27/22 ...
- 软工结对项目之词频统计update
队友 胡展瑞 031602215 作业页面 GitHub 具体分工 111500206 赵畅:负责WordCount的升级,添加新的命令行参数支持(自定义输入输出文件,权重词频统计,词组统计等所有新功 ...
- 结对作业-WordCount进阶版
1.在文章开头给出博客作业要求地址. 博客园地址:https://www.cnblogs.com/happyzm/p/9559372.html 2.给出结对小伙伴的学号.博客地址,结对项目的码云地址. ...
随机推荐
- luoguP1463:反素数ant(打表心得☆)
题目描述 对于任何正整数x,其约数的个数记作g(x).例如g()=.g()=. 如果某个正整数x满足:g(x)>g(i) <i<x,则称x为反质数.例如,整数1,,,6等都是反质数. ...
- iOS-AES算法总结
AESCipher.h #import <Foundation/Foundation.h> @interface AESCipher : NSObject /** 加密算法 @param ...
- 岩哥带你飞——Spring 学习1&重要组件
------------------------------------------------------------------------------ 准备工作:1.创建一个web 项目 2.创 ...
- CNN-2: AlexNet 卷积神经网络模型
1.AlexNet 模型简介 由于受到计算机性能的影响,虽然LeNet在图像分类中取得了较好的成绩,但是并没有引起很多的关注. 知道2012年,Alex等人提出的AlexNet网络在ImageNet大 ...
- 用户字符串操作,这里面包括字符串的decode、encode、substract等等操作
工具类描述:用户字符串操作,这里面包括字符串的decode.encode.substract等等操作 package cn.hgnulb; import java.io.UnsupportedEnco ...
- Jmeter做HTTP接口测试
接口测试概述:https://www.cnblogs.com/mawenqiangios/p/7886115.html Jmeter接口测试教程:https://www.cnblogs.com/hou ...
- python学习-28 map函数
1. num_1 = [10,2,3,4] def map_test(array): ret = [] for i in num_1: ret.append(i**2) # 列表里每个元素都平方 re ...
- Redis键的基本操作
1.Redis键的键名查询 ·命令名称:KEYS ·语法:KEYS pattern ·Pattern的用法: ? 任意一个字符 * 任意个任意字符 [ae] a或者e [^ae] 除了a和e [a-c ...
- 轻松搭建CAS 5.x系列(9)-登录后显示通知信息
概述说明 用户在账号名密码认证通过后,CAS可以跳转到登陆完成页面前,显示相关的通知页面. 搭建步骤 `1. 首先,您需要有个CAS Server端 如果您没有,可以按照我之前写的文章<轻松搭建 ...
- .NET中AOP实现
静态织入,如:PostSharp(收费)动态织入1.使用Castle DynamicProxy不太方便的是需要在AOP的方法上加上virual关键字,配置较繁琐https://blog.csdn.ne ...