结对项目
| 这个作业属于哪个课程 | 软件工程 |
| ---- | :----: | :----: |
| 这个作业要求在哪里 | 作业要求 |
| 这个作业的目标 | 四则运算生成器+合作完成项目 |
代码链接
合作者
- 姓名:李俊贤 学号:3118005367
- 姓名:李林飞 学号:3118005368
代码链接
PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 40 |
| · Estimate | · 估计这个任务需要多少时间 | 40 | 40 |
| Development | 开发 | 300 | 320 |
| · Analysis | · 需求分析 (包括学习新技术) | 30 | 40 |
| · Design Spec | · 生成设计文档 | 60 | 80 |
| · Design Review | · 设计复审 | 20 | 30 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 40 | 30 |
| · Design | · 具体设计 | 30 | 30 |
| · Coding | · 具体编码 | 200 | 220 |
| · Code Review | · 代码复审 | 30 | 20 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 30 | 30 |
| ·Reporting | 报告 | 60 | 60 |
| · Test Repor | · 测试报告 | 30 | 30 |
| · 合计 | 850 | 970 |
项目结构

-bean
--Exercise 单个练习题对象
-constant
--CommandConstant 命令行参数常量
--OperatorConstant 运算符参数常量
-exception
--MyExpection 自定义异常 用于处理异常信息
-service
--impl
---ExercisesGenerateServiceImpl 四则运算题目生成器
ExercisesGenerateService ExercisesGenerateServiceImpl的接口
-test
--Test 测试用例
-utils 工具类
--CalculateUtil 用于处理计算的工具类,提供针对整数或分数的运算
--CommobUtil 用于处理随机数的工具类,生成随机整数或随即分数
主要功能模块
ExercisesGenerateServiceImpl
-List<Exercise> getExerciseList(int exercisesNum, int range) 根据题目数量和数字范围生成多个题目
-String getEqualFormatQuestion(String exercise) 生成题目最简式 用于查重
-Exercise generateOneExercise(int range) 生成单条题目 用于1
-void exercisesToFile(List<Exercise> exerciseList) 将题目和答案输出到文件
-boolean checkQuestion(Exercise exercise,Set<String> questionSet) 查重
-String getAnswer(Exercise exercise) 获取题目答案
-String generateExerciseStr(Exercise exercise) 获取题目符合格式的输出
CalculateUtil
-String generateNaturalNumber(int range) 根据范围生成自然数
-String generateFraction(int range) 根据范围生成分数
-String About(String operand) 对假分数和真分数约分
-String BinocularOperation 双目运算
1.1 函数随机生成题目表达式代码
- 主要思路:根据范围随机生成一条练习题,首先随机生成运算数和运算符,设置到Exercise对象的属性容器中,调用generateExerciseStr(Exercise exercise)方法生成符合题目格式的输出
其中获取符合题目输出的算法是将所有运算数加入队列,根据运算符数量,在队列取出两个运算数与运算符生成一个表达式放入队列,直到队列只剩一个载体时,该载体就是本题题目符合
格式的输出。
/**
* 生成范围内的一条题目
* @param range
* @return
*/
public Exercise generateOneExercise(int range) {
Exercise exercise = new Exercise();
exercise.setNumberRange(range);
exercise.setOperatorNum(CommonUtil.getRandomNum(1,3));
List<String> operatorList = new ArrayList<String>();
String operator = null;
for (int i = 0; i < exercise.getOperatorNum() ; i++) {
operator = OperatorConstant.operatorMap.get((Integer) CommonUtil.getRandomNum(1, 4));
operatorList.add(operator);
}
List<String> numList = new ArrayList<String>();
int flag = 0;
for (int i = 0; i < operatorList.size()+1 ; i++) {
flag = CommonUtil.getRandomNum(0,1);
if(flag == 0){
numList.add(CalculateUtil.generateFraction(range));
}else {
numList.add(CalculateUtil.generateNaturalNumber(range));
}
}
exercise.setNumbers((ArrayList<String>) numList);
exercise.setOperators((ArrayList<String>) operatorList);
exercise.setExerciseString(generateExerciseStr(exercise));
exercise.setAnswer(getAnswer(exercise));
return exercise;
}
/**
* 根据Exercise对象获取符合题目格式的输出
* @param exercise
* @return
*/
private static String generateExerciseStr(Exercise exercise) {
Queue<String> queue = new LinkedList<>();
ArrayList<String> operators = exercise.getOperators();
ArrayList<String> numbers = exercise.getNumbers();
for (int i = 0; i < numbers.size() ; i++) {
queue.add(numbers.get(i));
}
String exerciseStr = "";
for (int i = 0; i < operators.size() ; i++) {
String operator = operators.get(i);
String num1 = queue.remove();
String num2 = queue.remove();
if(i != operators.size()-1){
exerciseStr = OperatorConstant.LEFT_BRACKETS+num1+operator+num2+OperatorConstant.RIGHT_BRACKETS;
}else {
exerciseStr = num1+operator+num2;
}
queue.add(exerciseStr);
}
StringBuilder sb = new StringBuilder("").append(queue.remove()).append("=");
return sb.toString();
}
1.2 对题目进行检测是否符合要求和查重
- 要求是否符合思路:当答案小于0零时return false 说明题目不符合要求
- 查重:生成题目最简式,将最简式逆转,两者皆放入set集合,每次生成题目比对Set里面的值,如果存在,则排除该题目
/**
* 检查问题是否重复或答案是否小于0
* @param exercise
* @param questionSet
* @return
*/
public boolean checkQuestion(Exercise exercise,Set<String> questionSet){
if(exercise == null || questionSet == null){
throw new MyException("checkQuestion方法参数出现问题");
}
if(exercise.getAnswer().contains("-")){
return false;
}
if(questionSet.contains(getFormatQuestion(exercise))){
return false;
}
return true;
}
性能分析
在 -r 10000 -n 10下测试性能

可见主要性能消耗在getFormatQuestion(exercise)生成符合格式的练习题、generateOneExercise(int range)根据范围生成练习题、checkQuestion(exercise)检测题目是否符合要求和重复

上图可见主要消耗在字符串的处理上
优化思路:查重构建练习题答案的Set
测试

运行结果

Exercises.txt

Answer.txt

单元测试
利用Junit进行单元测试
| 序号 | 样例 |
|---|---|
| 1 | -n 1 -r 1 |
| 2 | -n 1 -r 10 |
| 3 | -n 1 -r 100 |
| 4 | -n 10 -r 1 |
| 5 | -n 100 -r 1 |
| 6 | -n 100 -r 10 |
| 7 | -n 100 -r 100 |
| 8 | -n 1000 -r 10 |
| 9 | -n 10000 -r 10 |
| 10 | -n 10000 -r 100 |
部分测试代码
//10000,10
@Test
public void testMethod09() throws IOException {
ExercisesGenerateService service = new ExercisesGenerateServiceImpl();
service.exercisesToFile(service.getExerciseList(10000,10));
}
//10000,100
@Test
public void testMethod10() throws IOException {
ExercisesGenerateService service = new ExercisesGenerateServiceImpl();
service.exercisesToFile(service.getExerciseList(10000,100));
}

项目小结
李俊贤:结对编程需要很好的沟通,提前设计好接口,做好需求分析,安排参数非常重要,做好模块设计。
李林飞:需求分析很重要,设计文档要写好。

浙公网安备 33010602011771号