结对项目
| 这个作业属于哪个课程 | <23级软件工程> |
|---|---|
| 这个作业要求在哪里 | <作业要求的链接> |
| 这个作业的目标 | 学习PSP2.1思想下的程序设计实现方法,学习程序设计中的团队合作 |
| 开发者 | 庄楷彬(3123004250),覃锴锋(3123004240) |
作业文件链接
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 15 | 10 |
| · Estimate | · 估计这个任务需要多少时间 | 15 | 10 |
| Development | 开发 | 650 | 565 |
| · Analysis | · 需求分析 (包括学习新技术) | 180 | 150 |
| · Design Spec | · 生成设计文档 | 30 | 15 |
| · Design Review | · 设计复审 | 10 | 5 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 5 |
| · Design | · 具体设计 | 30 | 20 |
| · Coding | · 具体编码 | 150 | 140 |
| · Code Review | · 代码复审 | 60 | 35 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 180 | 195 |
| Reporting | 报告 | 100 | 90 |
| · Test Repor | · 测试报告 | 60 | 65 |
| · Size Measurement | · 计算工作量 | 10 | 5 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 20 |
| 合计 | 765 | 665 |
程序总体设计
需求分析
1. 概述
本次作业要求编写一个生成小学四则运算题目的命令行工具,同时具备答案生成与检查功能。
2.程序总体功能划分
程序具体设计
程序各接口设计与实现
模块总体设计
1.主模块
- 读取输入的命令行参数(
-n,-r,-e,-a)。 - 功能
- 验证
-n及-e是否存在,否则报错。 - 区分生成模式(
-n)或检查模式(-e和-a) - 在生成模式下验证
-r是否存在,否则报错。
- 验证
2.带分数模块
功能:
- 实现带分数存储与计算
- 进行单个带分数的随机生成
3.方程模块
- 记录方程的各个元素
- 计算方程的解
4.问题生成模块
功能:
- 生成运算符不超过3个的表达式。
- 确保计算过程不产生负数,除法结果为真分数。
- 避免重复题目。
5.文件写入模块
功能:
将题目数据写入到文件中。
模块具体设计
各模块具体接口如下:
-
主模块
public static void main(String[] args) -
带分数模块
带分数的类定义如下:
class MixedNumber { int interger;//带分数的整数部分 int numerator;//带分数的分子部分 int denominator;//带分数的分母部分 }带分数模块有以下函数:
-
MixedNumber()//构造函数,生成值为0,分母为1的标准带分数 -
void setValue(MixedNumber M)//将M的值赋值给此带分数 -
int isEqual(MixedNumber num)//判断两待分数是否相等 -
int isNegative()//判断此带分数是否为正数 -
private int gcd(int a,int b)//寻找最大公约数 -
private void simplify()//化简带分数 -
void randomNumber(int r)//生成随机带分数 -
MixedNumber negation()//将数转换为负数,用于减法 -
MixedNumber mixNumberMultiply(MixedNumber numA,MixedNumber numB)//带分数乘法 -
MixedNumber mixNumberAdd(MixedNumber numA,MixedNumber numB)//带分数加法 -
MixedNumber mixNumberDivide(MixedNumber numA,MixedNumber numB)//带分数除法 -
String getNumber()//将带分数转换为字符串形式输出
-
-
方程模块
方程的类定义如下:
public class Question {
MixedNumber[] elements;
int[] operator;
MixedNumber result;
}
方程模块有以下函数:
-
Question()//构造函数,生成空方程 -
void copyQuestion(Question Q)//将方程Q赋值给次方程 -
MixedNumber getAnswerCalc()//解析方程,给出计算顺序 -
MixedNumber getAnswer(int t)//进行单次计算 -
String getdataString()//将方程作为字符串输出 -
String getAnswerString()//将答案以字符串的形式输出
4.方程生成模块
方程生成模块的类定义如下:
public class QuestionGenerator {
int n;//生成题目数量
int r;//生成范围
List<Question> questions = new ArrayList<>();//方程链表
}
有以下函数:
-
public void questionGenerate()//方程生成函数 -
QuestionGenerator(int n,int r)//构造函数
5.文件写入模块
文件写入模块的类定义如下:
public class writeFile {
static String[] fileName = {"Exercises.txt","Answers.txt"};
}
有以下函数:
public static void writeExerciseFile(QuestionGenerator generator)//写入方程
性能改进
程序性能消耗最大的函数为方程生成函数
public void questionGenerate(){
int t1,t2,t3=0;
Random random=new Random();
Question tmpQuestion = new Question();
for(t1=0;t1<this.n;t1++){
Question question=new Question();
question.elements[0].randomNumber(r);
question.operator[0] = random.nextInt(4)+1;//第一个运算符号不能为等于号
question.elements[1].randomNumber(r);
question.operator[1] = random.nextInt(5);
if(question.operator[1] != 0){//如果第二个运算符号为等于号,则不必继续生成
question.elements[2].randomNumber(r);
question.operator[2] = 0;
if(random.nextInt(10)-5>0){
question.operator[1] += 10;
}
//十分之一的概率生成括号
}
tmpQuestion.copyQuestion(question);
try{
question.result.setValue(tmpQuestion.getAnswerCalc());
} catch (NullPointerException e) {
t1--;
continue;
}
for(t2=0;t2<questions.size();t2++){//查重逻辑如下:首先判断结果是否相等,其次检查各元素是否相等
if(question.result.isEqual(questions.get(t2).result)==0){
continue;
}
for(t3=0;t3 < question.elements.length-1; t3++){
if(questions.get(t2).elements[t3].isEqual(question.elements[t3]) == 0){
break;
}
}
if(questions.get(t2).elements[t3].isEqual(question.elements[t3]) == 0){
t3 = question.elements.length+1;
break;
}
}
if(t3 == question.elements.length+1){//如果发现重复,则丢弃此次生成式子重新生成
t1--;
continue;
}
questions.add(question);
}
}
}
这里通过减少不必要的步骤来提高效率。
程序测试
单元测试展示

1.测试正常输入
@Test
void Test01(){
String[] args={"-n","10","-r","10"};
Main.main(args);
}
2.测试单输入变量下的情况
@Test
//测试单输入变量下的情况
void Test02(){
String[] args={"-n","10"};
Main.main(args);
}
@Test
void Test03(){
String[] args={"-r","10"};
Main.main(args);
}
@Test
void Test04(){
String[] args={"-e","10"};
Main.main(args);
}
@Test
void Test05(){
String[] args={"-a","10"};
Main.main(args);
}
3.测试检测功能
@Test
void Test06(){
String[] args={"-e","Exercises.txt","-a","Answers.txt"};
Main.main(args);
}
4.测试检测功能输入异常报错
@Test
void Test07(){
String[] args={"-e","Areyousure?","-a","I'm_not_a.txt"};
Main.main(args);
}
5.测试10000题输出
@Test
void Test08(){
String[] args={"-n","10000","-r","10"};
assertTimeout(Duration.ofSeconds(100),() -> {
Main.main(args);
});
}
6.测试大数输出
void Test09(){
String[] args={"-n","10","-r","10000"};
assertTimeout(Duration.ofSeconds(100),() -> {
Main.main(args);
});
}
7.测试两项功能同时激活
void Test10(){
String[] args={"-e","Areyousure?","-a","I'm_not_a.txt","-n","10","-r","10"};
Main.main(args);
}
异常处理
1.在读取文件时没有发现相应文件:
处理方法如下:
try {
new GradeExercises().gradeExercises(args[i + 1], args[i + 3]);
} catch (IOException e) {
System.out.println("Error grading exercises: " + e.getMessage());
}
测试单元见test07
2.在输入时输入了-n但没有输入-r
处理方法如下:
if(r == 0){
System.out.println("程序检测到输入-n但没有指定-r的值,请确认您的输入是否正确");
}
测试单元见test02
3.写入文件时写入失败
处理方法如下:
try {
BufferedWriter out = new BufferedWriter(new FileWriter(fileName[1]));
int t;
for(t = 0;t < generator.questions.size(); t++ ){
out.write(t+1+". "+generator.questions.get(t).getAnswerString()+"\n");
}
out.close();
} catch (IOException e) {
System.out.println("写入出现错误,请检查硬盘空间是否充足及程序是否有足够的权限");
}
小结
在本次作业中,我收获良多。
😂由于JAVA好久没写了,这次几乎是从头开始从新学了一遍。
非常感谢这次和我合作的同伴覃锴锋,在这次程序设计中给了我很大的帮助。在我因为太久没写java而产生了许多bug时,他耐心而又热情地帮我指出了这些错误。同时他也很好地完成了他负责的方程检验部分。
浙公网安备 33010602011771号