合作者: 201631062626 201631062522
代码地址: https://gitee.com/iy2524/WordCount2.0
本次作业链接地址: https://edu.cnblogs.com/campus/xnsy/2018Systemanalysisanddesign/homework/2188
一、PSP表格
PSP2.1 | PSP阶段 | 预估耗时 (分钟) | 实际耗时 (分钟) |
Planning | 计划 | 20 | 10 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 10 |
Development | 开发 | 1200 | 1600 |
· Analysis | · 需求分析 (包括学习新技术) | 30 | 10 |
· Design Spec | · 生成设计文档 | 10 | 0 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 0 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 90 |
· Design | · 具体设计 | 300 | 240 |
· Coding | · 具体编码 | 600 | 900 |
· Code Review | · 代码复审 | 30 | 100 |
· Test | · 测试(自我测试,修改代码,提交修改) | 140 | 260 |
Reporting | 报告 | 100 | 50 |
· Test Report | · 测试报告 | 30 | 0 |
· Size Measurement | · 计算工作量 | 35 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 35 | 40 |
| 合计 | 1320 | 1660 |
二、代码互审情况
审查模块: WordCount类 和 客户端类
发现的问题: 方法名不符合规范,应符合lowerCamelCase来进行命名;注释该单独占一行;对文件进行统计的每个方法都单独各自对文件进行了操作,可以将文件操作分离开来,提升程序的效率;
三、设计过程
类的总体设计为三个类,一个为FileCount类,考主要起到javabean的作用,封装了如单词数,字母数,文件path等属性。一个为WordCountUtil类,里面包含操作FileCount的属性的方法以及一些常量。最后一个Main类即为客户端类,为程序的启动入口,通过生成FileCount对象并调用WordCountUtil的静态方法来实现功能。
类图:
关键函数算法设计:
总体流程:
获取单词数:
获取字符数: 使用String.length()方法即可获取
获取行数:只要在循环中,行数就加一
获取特殊行数:根据相应条件判断当前行的类型
获取结果:
递归处理文件夹所有文件:
三、代码说明
本次项目严格按照阿里巴巴java编程规范执行,变量和方法命名采用lowerCamelCase,类名采用UpperCamelCase
类前面需要注释作者信息,方法需要注释作者、参数及返回值信息,代码注释需要单独一行
以及编码过程中的魔法值需要设置成常量方便维护
在获取结果方法中,传入参数列表、文件统计对象,并通过单行读取执行相应的方法,将结果保存到文件统计对象里
/**
* 读取文件,并将结果保存到fileCount对象里
* @param orderList 参数列表
* @param fileCount 文件对象
* @throws Exception
*/
public static void getResults(List<Integer> orderList, FileCount fileCount) throws Exception {
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileCount.getPath()));
BufferedReader br = new BufferedReader(isr);
String tempLine = null;
while ((tempLine = br.readLine()) != null) {
if (orderList.contains(HAS_CHAR)) {
if (fileCount.getCharsNum() == -1) {
fileCount.setCharsNum(0);
}
// 每次换行时会有两个字符/r/n,计算长度需要加上这两个字符
fileCount.setCharsNum(fileCount.getCharsNum() + 2 + getCharsNum(tempLine));
}
if (orderList.contains(HAS_WORD)) {
if (fileCount.getWordsNum() == -1) {
fileCount.setWordsNum(0);
}
fileCount.setWordsNum(fileCount.getWordsNum() + getWordsNum(tempLine, stopList));
}
if (orderList.contains(HAS_LINE)) {
if (fileCount.getLinesNum() == -1) {
fileCount.setLinesNum(0);
}
fileCount.setLinesNum(fileCount.getLinesNum() + 1);
}
if (orderList.contains(HAS_LINE_BY_TYPE)) {
if (fileCount.getBlankLinesNum() == -1) {
fileCount.setBlankLinesNum(0);
}
if (fileCount.getCodeLinesNum() == -1) {
fileCount.setCodeLinesNum(0);
}
if (fileCount.getNoteLinesNum() == -1) {
fileCount.setNoteLinesNum(0);
}
switch (tellLineType(tempLine)) {
case NOTE_LINE:
fileCount.setNoteLinesNum(fileCount.getNoteLinesNum() + 1);
break;
case CODE_LINE:
fileCount.setCodeLinesNum(fileCount.getCodeLinesNum() + 1);
break;
case BLANK_LINE:
fileCount.setBlankLinesNum(fileCount.getBlankLinesNum() + 1);
break;
}
}
}
if (fileCount.getCharsNum() != -1) {
// 最后一行没有换行,减2
fileCount.setCharsNum(fileCount.getCharsNum() - 2);
}
isr.close();
}
统计单词数的方法为一个通用方法,传入当前行和停用词列表,当不需要停用词时则传入空列表即可。
/**
* 获取单词数
*
* @author MS
*
* @param str 每行的字符串
* @return count 数量
*/
public static int getWordsNum(String str, List<String> stopList) {
int count = 0;
Matcher matcher = null;
boolean isChar = false;
String tempStr = "";
for (int i = 0; i < str.length(); i++) {
matcher = PATTERN.matcher(str.charAt(i) + "");
if (matcher.matches()) {
tempStr += str.charAt(i);
if (i == str.length() - 1 && !stopList.contains(tempStr)) {
count++;
} else {
isChar = true;
}
} else if (isChar == true) {
isChar = false;
if (!stopList.contains(tempStr)) {
count++;
}
tempStr = "";
}
}
return count;
}
/**
* 从文件中取出以空格隔开的单词
*
* @param stopFile
* 存放单词文件名字字符串 绝对路径
* @return 取出的单词字符串集合
* @throws FileNotFoundException
*/
public static List<String> getStopStringList(String stopFile) throws Exception {
List<String> stopList = new ArrayList<>();
BufferedInputStream stop = new BufferedInputStream(new FileInputStream(stopFile));
int x = -1;
String stopStr = "";
char ch;
// 取出停用词装到stopList
while (true) {
x = stop.read();
if (x == -1) {
stopList.add(stopStr);
stopStr = "";
stop.close();
break;
}
ch = (char) x;
if (ch == ' ') {
stopList.add(stopStr);
stopStr = "";
} else {
stopStr += ch;
}
}
return stopList;
}
在FileCount类中封装了文件统计的属性,如字符数、单词数等。
/**
*
*/
package com.test.javabean;
/**
* @author Dylan
*
*/
public class FileCount {
private int charsNum = -1;
private int wordsNum = -1;
private int linesNum = -1;
private int codeLinesNum = -1;
private int noteLinesNum = -1;
private int blankLinesNum = -1;
private String path;
public FileCount(String path) {
this.path = path;
}
public String getPath() {
return path;
}
public int getCharsNum() {
return charsNum;
}
public void setCharsNum(int charsNum) {
this.charsNum = charsNum;
}
public int getWordsNum() {
return wordsNum;
}
public void setWordsNum(int wordsNum) {
this.wordsNum = wordsNum;
}
public int getLinesNum() {
return linesNum;
}
public void setLinesNum(int linesNum) {
this.linesNum = linesNum;
}
public int getCodeLinesNum() {
return codeLinesNum;
}
public void setCodeLinesNum(int codeLinesNum) {
this.codeLinesNum = codeLinesNum;
}
public int getNoteLinesNum() {
return noteLinesNum;
}
public void setNoteLinesNum(int noteLinesNum) {
this.noteLinesNum = noteLinesNum;
}
public int getBlankLinesNum() {
return blankLinesNum;
}
public void setBlankLinesNum(int blankLinesNum) {
this.blankLinesNum = blankLinesNum;
}
}
递归文件
/**
* 递归处理目录里所有符合条件的文件
*
* @param path
* 目录的位置
* @param orderList
* 命令集合
* @param condition
* 限制的条件
* @throws Exception
*/
public static void recursionAllFiles(File catalog, List<Integer> orderList, String condition,
List<FileCount> fileCounts) {
if (catalog != null) {
if (catalog.isDirectory()) {
File[] fileArray = catalog.listFiles();
if (fileArray != null) {
for (int i = 0; i < fileArray.length; i++) {
recursionAllFiles(fileArray[i], orderList, condition, fileCounts);
}
}
} else if (getFileName(catalog.getName(), DOT).equals(condition)) {
FileCount fileCount = new FileCount(catalog.getAbsolutePath());
try {
getResults(orderList, fileCount);
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
fileCounts.add(fileCount);
}
}
}
五、总结
在这此项目中,我收获了很多。在此之间我写代码不是很有规范,看着杂乱无章,其实我早就想解决这个问题,通过这次项目,了解到了编程规范,花了很多时间去学习规范和使用规范,其中最难改的就是java中大括号的使用,以前是两个括号上下对齐,阿里规范要求不一样,虽然花了很长时间,但体会到了“规矩”带来的方便和愉悦。还有就是认识到了自己在面向对象设计上的不足,在这次项目中简单的根据自己理解的部分面向对象思想简单的设计了类和方法,但在完成所有代码后发现有很多不足,一个类的作用不是很明显,这还需要我更深入的学习面向对象和设计模式,相较于计算机其他方面,我从这次项目中发现,我更喜欢软件系统的设计,今后我会偏向了解学习软件设计方面的知识,而且一个好的结构设计是一个好的软件的开始,没有一个好的架构,软件的开发和使用都会出现问题。在这个项目我认为结对的效果总体是1+1>2的,虽然在合并项目时,两人因为结构设计的不一样,争论了大半天,但最终两个人达成了一致的意见,每个人分别完成自己的事情,有问题能互相帮助解决,节省了很多开发时间。