结对项目

结对项目

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13479
这个作业的目标 <通过结对编程实践的完整开发流程和团队协作开发一个自动生成与批改系统>
队员一 计科4班 吴键斌 3123004803 https://github.com/sugarcaneprince/sugarcaneprince
队员二 计科4班 余嘉祺 3123004811 yjq-yes (yjq)

PSP表格

PSP2.1 阶段划分 子任务 预估耗时(分钟) 实际耗时(分钟)
计划阶段(Planning) Estimate(估算总时间) 30 30
开发阶段(Development) Analysis(需求分析+学习新技术) 205 230
Design Spec(生成设计文档) 15 15
Design Review(设计复审) 10 10
Coding Standard(制定代码规范) 10 5
Design(具体设计) 20 30
Coding(具体编码) 90 120
Code Review(代码复审) 30 30
Test(测试:自我测试+修改优化) 30 20
报告阶段(Reporting) Test Report(编写测试报告) 40 40
Size Measurement(计算工作量) 20 20
Postmortem & Process Improvement Plan(事后总结与改进计划) 20 20
合计 (所有子任务总计) 235 260

效能分析

改进程序性能的思路:调用更优化的数据结构,抛弃传统的if-else避免繁重的代码,而使用case-when语句从而简化代码

  • 修改时间大概在30min左右

通过调用树发现, 消耗最大的竟然是用户Service的使用,仔细思考,实际上确实是,因为用户的读取涉及到读取用户文件的选择,并且把每一次的题目记录都要存放在用户文件中,涉及到多次的文件读入写入,所以消耗大是很合理的

image

设计实现过程

程序涉及到的几个类如下图所示

image

  • 由于这是一个算数生成的程序,关于用户的登录读取等等先展示略过,仔细研究算数生成的功能
  • 各个类与函数之间的调用关系如下流程图所示,具体的流程为,在用户初始菜单中选择难度,设置题目的数量和解答时间,随后会有专门的生成器类QuestionGenerator来生成对应的题目,startQuiz开始解答,解答完毕后addres()方法保存到对应的用户信息中
    image

代码说明

对于算数自动生成程序,我们依旧关心具体的实现方法

算数生成的核心:generateOne()方法,生成一个具体的算术题

public static Question generateOne(QuestionType type) {
        int a, b, c, answer;
        String expr;
        switch (type) {
            // === 一年级 ===
            case GRADE1_ADD_SUB_10_1STEP:
                a = rand.nextInt(10);
                b = rand.nextInt(10);
                if (rand.nextBoolean()) {
                    answer = a + b;
                    expr = a + " + " + b;
                } else {
                    while (a < b) {
                        a = rand.nextInt(10);
                        b = rand.nextInt(10);
                    }
                    answer = a - b;
                    expr = a + " - " + b;
                }
                break;
            case GRADE1_ADD_SUB_10_2STEP:
                a = rand.nextInt(10);
                b = rand.nextInt(10);
                c = rand.nextInt(10);
                answer = a + b - c;
                expr = a + " + " + b + " - " + c;
                break;
            case GRADE1_ADD_SUB_20_1STEP:
                a = rand.nextInt(11) + 9; // 10~20
                b = rand.nextInt(11);
                if (rand.nextBoolean()) {
                    answer = a + b;
                    expr = a + " + " + b;
                } else {
                    while (a < b) {
                        a = rand.nextInt(11) + 9;
                        b = rand.nextInt(11);
                    }
                    answer = a - b;
                    expr = a + " - " + b;
                }
                break;
            case GRADE1_ADD_SUB_20_2STEP:
                a = rand.nextInt(11) + 9;
                b = rand.nextInt(11);
                c = rand.nextInt(11);
                answer = a + b - c;
                expr = a + " + " + b + " - " + c;
                break;
            case GRADE1_MIXED:
                // 随机用以上4种
                int t = rand.nextInt(4);
                return generateOne(QuestionType.values()[t]);
            // === 二年级 ===
            case GRADE2_ADD_NO_CARRY:
                a = rand.nextInt(90) + 10;
                b = rand.nextInt(10 - a % 10); // 不进位
                answer = a + b;
                expr = a + " + " + b;
                break;
            case GRADE2_SUB_NO_BORROW:
                a = rand.nextInt(90) + 10;
                b = rand.nextInt(a % 10 + 1); // 不退位
                answer = a - b;
                expr = a + " - " + b;
                break;
            case GRADE2_ADD_WITH_CARRY:
                a = rand.nextInt(90) + 10;
                b = rand.nextInt(90) + 10;
                answer = a + b;
                expr = a + " + " + b;
                break;
            case GRADE2_SUB_WITH_BORROW:
                a = rand.nextInt(90) + 10;
                b = rand.nextInt(a + 1);
                answer = a - b;
                expr = a + " - " + b;
                break;
            case GRADE2_CHAIN:
                a = rand.nextInt(90) + 10;
                b = rand.nextInt(90) + 10;
                c = rand.nextInt(90) + 10;
                answer = a + b - c;
                expr = a + " + " + b + " - " + c;
                break;
            case GRADE2_MIXED:
                t = rand.nextInt(5);
                return generateOne(QuestionType.values()[t + 5]);
            // === 三年级 ===
            case GRADE3_MULTIPLICATION:
                a = rand.nextInt(9) + 1;
                b = rand.nextInt(9) + 1;
                answer = a * b;
                expr = a + " × " + b;
                break;
            case GRADE3_DIV_WITH_REMAINDER:
                b = rand.nextInt(9) + 1;
                answer = rand.nextInt(9) + 1;
                a = b * answer;
                expr = a + " ÷ " + b;
                break;
            default:
                expr = "未知类型";
                answer = 0;
        }
        return new Question(expr, answer, type);
    }

generateOne函数是一个用于根据指定题型生成数学题目的工具函数,其核心功能是依据输入的QuestionType类型参数,动态生成包含随机数字、运算表达式、答案及对应题型的Question对象。

参数为QuestionType枚举类型,用于指定生成题目的具体类型(如一年级10以内加减、二年级进位加法、三年级乘法等)。

逻辑流程上,函数通过switch语句匹配不同题型:

  • 基础题型(如GRADE1_ADD_SUB_10_1STEPGRADE2_ADD_NO_CARRYGRADE3_MULTIPLICATION等):根据题型规则生成随机操作数(如10以内数字、两位数等),通过rand.nextInt()控制数值范围(如一年级10以内加减取0-9的随机数,二年级不进位加法确保个位数相加不超过10),计算运算结果(加法、减法、乘法等),并构造表达式字符串(如“3 + 5”“12 - 4”);
  • 特殊处理:减法题型确保被减数大于减数(避免负数答案),除法题型通过“商×除数=被除数”生成整除式,混合题型(如GRADE1_MIXED)则随机选择同年级下的子题型递归调用自身生成题目;
  • 默认情况:若题型未知,生成“未知类型”表达式及0答案。

最终,函数返回一个Question对象,包含生成的题目表达式(expr)、运算答案(answer)及原始题型(type)。

添加题目的方法

private static void addWrong(User user, Question q) {
    WrongQuestionStats stats = user.wrongQuestions.get(q.expression);
    if (stats == null) {
        stats = new WrongQuestionStats(q);
        user.wrongQuestions.put(q.expression, stats);
    } else {
        stats.addWrongAttempt();
    }
}

addWrong方法的功能是记录用户答错的题目并更新错题统计信息(如答错次数)。实现方式为:通过用户对象的wrongQuestions(键为题目表达式的Map)获取该题的统计对象stats,若首次答错(stats为null),则新建WrongQuestionStats对象并存入Map;若重复答错,则调用stats.addWrongAttempt()更新统计数据(如递增答错次数)。

测试运行

接下来演示整个程序的流程

  1. 登录成功状态,扣1开始答题

image

  1. 自定义答题数目和时间

!image

  1. 具体答题的测试界面

image

  1. 扣2进入专题强化训练

image

  1. 所有的做题文件都保存在json文件中,便于下次的读取

image

关于结果验证,自己算一下就知道是不是对的了

项目小结

这次项目让我明白,好的项目不仅是功能的堆砌,更是“需求洞察-技术实现-用户反馈-迭代优化”的闭环。未来开发中,我会更注重前期需求调研的深度,尤其在教育领域,需将“专业性”(如符合教学大纲)与“易用性”(如适配用户认知水平)放在同等重要的位置。

posted @ 2025-10-22 22:23  甘蔗王子  阅读(4)  评论(0)    收藏  举报