合作者:201631062129,201631062501
队友博文
代码地址
本次作业链接地址
一、PSP
PSP2.1 | PSP阶段 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Panning | 计划 | 100 | 150 |
.Estimate | .估计这个任务需要多少时间 | 120 | 200 |
Development | 开发 | 3000 | 3600 |
.Aanlysis | .需求分析(包括学习新技能) | 60 | 40 |
.Design Spec | .生成设计文档 | 100 | 180 |
.Design Review | . 设计复审 (和同事审核设计文档) | 30 | 40 |
.Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 40 |
.Design | .具体设计 | 200 | 300 |
.coding | .具体编码 | 2500 | 3000 |
.Test | . 测试(自我测试,修改代码,提交修改) | 240 | 300 |
Reporting | 报告 | 60 | 100 |
.Test Report | . 测试报告 | 60 | 70 |
.Size Measurement | .计算工作量 | 60 | 60 |
.Postmortem & Process Improvement Plan | .事后总结, 并提出过程改进计划 | 30 | 40 |
合计 | 3100 | 3750 |
二、代码互审
- 队友:
- 代码中硬编码地方较多,程序的扩展性不强;
- 代码缺少注释,功能实现完成,但是缺少编程规范。
- 程序流程和结构清晰
- 自己:
- 程序结构清晰但流程复杂,代码注释少且不够清晰;
- 多出使用接口,提高了程序的扩展性,但是增加了系统的复杂度;
- 想的太多,程序边界分不清楚。
二、设计过程
- 主要类:
- WorldCount类。该类主要用于接受用户的输入和处理结果输出。
- Analyzer类。该类主要用于分析参数和处理参数的缺省,错误等问题。
- Processor类。该类接受Analyzer类消息,并根据消息执行相应的功能。
- Format类。该类接受Processor类的处理结果,并对结果进行格式化处理。
- Command,抽象类。所有选项(如:-s、-w等)对用的功能执行类都要继承该类。
- Filter,接口。对用户输入的选项和参数进行过滤,判断是否是合格的数据。
- UML简图:
- **代码实现:** - CommandA:统计源文件空行数、注释行数和代码数: - 点击查看 - CommandE:停词功能: - 点击查看 - CommandS:递归分析: - 点击查看
- **运行效果:** - 选项:-s - 点击查看 - 选项:-a - 点击查看
- <span id="et">选项:-e</span>
- <a style="text-decoration: none" href="#es">点击查看</a>
三、总结:
准确的说在这次对结编程项目中,我们并没有完全遵循结对编程的要求。原因是第一次使用结对编程的方法没有经验,不知怎么进行;并且在先前的项目中,我们两人的编程语言不同,程序设计不同;还有两个人的空闲时间不一样。最终结对编程的方式不是很成功。
但是在这次项目中,我也体会到了交流的好处。在项目的最后阶段,我与同伴各自交流自己的程序设计。俗话说,一个苹果交换一个苹果最后还是得到了一个苹果;但是思想交换得到了思想确是大于二的。在不断地交流中,我学到了很多,同时也迸发出来新的思想。遗憾的是这次结对编程不是那么的成功。(;′⌒`)(╯︵╰)
四、图片:
- UML简图:
- 选项:-s - 点击返回 - 文件: ![](https://img2018.cnblogs.com/blog/1489868/201810/1489868-20181017194610708-1010250324.png) - 结果: ![](https://img2018.cnblogs.com/blog/1489868/201810/1489868-20181017194628562-1035003202.png)
- 选项:-a - 点击返回 - 文件: ![](https://img2018.cnblogs.com/blog/1489868/201810/1489868-20181017194645482-685781478.png) - 结果: ![](https://img2018.cnblogs.com/blog/1489868/201810/1489868-20181017194657710-461173570.png)
- 选项:-e - 点击返回 - 文件: ![](https://img2018.cnblogs.com/blog/1489868/201810/1489868-20181017194949345-277780938.png) - 结果: ![](https://img2018.cnblogs.com/blog/1489868/201810/1489868-20181017194959156-1104058122.png)
五、部分代码
- CommandA类:
点击返回
//统计空行数
public boolean blankLine(String line) {
if(line.length() > 1) { //字符串是否大于0
return false;
}
//判断“{”或“}”独占一行
if(line.length() == 1) {
char ch = line.charAt(0);
if(ch != '}' && ch != '{') {
return false;
}
}
return true;
}
//统计注释行数
public int annotationLine(String line, int annotationStatus) {
char[] chars = line.toCharArray();
boolean leftQuotation = false;
if(annotationStatus == annotation_start) {
for(int i = chars.length - 1; i > 0; --i) {
if(chars[i] == '/') { //判断字符串是否存在“/”
if(chars[i - 1] == '*') { //判断在“/”前面是否存在“*”
return annotation_end; //返回当前注释部分统计结束
}
}
}
return annotation_start; //返回注释统计开始
}
//注释统计中间过程
for(int i = 1; i < chars.length; ++i) {
if(chars[i] == '"') { //防止双引号里的转义字符
leftQuotation = !leftQuotation;
}else if(chars[i] == '/'){
if(!leftQuotation) {
if(chars[i - 1] == '/'){
return annotation_end;
}
}
}else if(chars[i] == '*'){ // 判断是否是多行注释
if(!leftQuotation) {
if(chars[i - 1] == '/') {
i ++;
for(int j = chars.length - 1; j > i; --j) {
if(chars[j] == '/') {
if(chars[j - 1] == '*') {
return annotation_end;
}
}
}
return annotation_start;
}
}
}
}
return annotation_none;
}
//统计代码行数
public boolean codeLine(String line) {
if(line.length() > 2) {
if((line.charAt(line.length() - 1) != '/') && (line.charAt(line.length() - 2) != '*')) {
return true;
}else {
if(line.charAt(0) != '/' && line.charAt(1) != '*') {
return true;
}
}
}
return false;
}
@Override
public Status execute() {
int annotationState = annotation_end;
Data dataf = (Data)(this.getProcessor().getPool().getData(Option.option_f));
String[] strings = (String[])dataf.getValue(true);
for(int i = 0; i < strings.length; ++i) {
String temp = strings[i].trim();
if(blankLine(temp)) {
blankLines ++;
continue;
}
if((annotationState = annotationLine(temp, annotationState)) != annotation_none) {
annotatinLines ++;
if(annotationState == annotation_start) {
if(annotationStartCount > 0) {
continue;
}
annotationStartCount ++;
}else if(annotationStartCount == annotation_end){
annotationStartCount = 0;
}
}
if(codeLine(temp)) {
codeLines++;
continue;
}
}
return Status.OVER;
}
- CommandE类:
点击返回
@Override
public Status execute() {
if(regex == null) {
return Status.OVER;
}
messages.clear(); //清空CommandE对象的数据存储器
//获取CommandF的对象的数据存储器
Data dataf = (Data)(this.getProcessor().getPool().getData(Option.option_f));
String[] messBuiders = (String[])dataf.getValue(true); //获取数据的值
StringBuilder builder = new StringBuilder("");
//使用正则表达式除去停用词,再添加到本对象的数据存储器中
for(int i = 0; i < messBuiders.length; ++i) {
String temp = messBuiders[i];
String[] mess = temp.toString().split(regex);
for(int j = 0; j < mess.length; ++j) {
builder.append(mess[j]);
}
messages.add(builder.toString());
builder.delete(0, builder.length());
}
return Status.OVER;
}
- CommandS类:
点击返回
//递归读取文件,类似树的先序遍历
private Status fileRecurseRead() {
int count = 0;
File[] files = innerFiles.files;
while(innerFiles != null){ //递归读取文件结束标志
while(innerFiles.index < files.length) { //判断当前文件的子文件是否读完
if(files[innerFiles.index].isDirectory()) {
File[] filesTemp = files[innerFiles.index ++].listFiles();
if(filesTemp.length > 0) { //判断是否存在子文件
InnerFile inFile = new InnerFile();
//交换节点
inFile.files = filesTemp;
inFile.parent = innerFiles;
innerFiles = inFile;
files = filesTemp;
}
}else {
if(count < capacity) {
count++;
//把递归到的文件当作参数传给CommandF对象
commandF.addArgument(files[innerFiles.index ++].getPath());
}else {
return Status.PAUSE; //文件暂停
}
}
}
//放回到上一个文件
innerFiles = innerFiles.parent;
if(innerFiles != null) {
files = innerFiles.files;
}
}
return Status.OVER; //文件递归完毕
}