首页 > 编程语言 >HNU个人项目评测—中小学数学试卷自动生成程序

HNU个人项目评测—中小学数学试卷自动生成程序

时间:2023-09-19 17:25:54浏览次数:46  
标签:count String question HNU 数学试卷 new type 中小学 size

目录

一.简介

本博客是针对结对编程队友苟怀炜同学的个人项目代码所写的分析与总结,代码使用语言为Java,与本人项目所用编程语言一致。为了更好的实现结对项目功能,在评价苟怀炜同学的代码时,我会学习他的优点,同时针对不足的地方提出自己的拙见。

二.测试与分析

1.黑盒测试

  1. 登录功能

    分析:界面设计良好,登录前后均有充分的文字提示信息,提高用户体验感,同时针对前置空格和后置空格分别做了处理,值得学习!



    分析:能够正确退出,且退出后能够重新登录。


  2. 出题功能

    分析:提示清晰,能够连续生成题目。





分析:小学初中高中三种类型的难度的题目均出题正确,同一个老师出的题存放在同一个文件夹中,且每个题目间有空行,看起来简洁舒适。同时文件命名符合要求规范。



3. 切换难度功能

分析:能够正确切换难度,在切换难度时,有对错误输入进行处理,每一步之间均有充足的提示信息。

2.白盒测试

项目结构
该项目一共有6个Java文件,分别Main,User,Problem,PrimaryProblem,MidProblem,HighProblem。其中Problem为抽象类,PrimaryProblem,MidProblem,HighProblem分别是小学,初中,高中难度下Problem的子类。充分体验了面向对象,继承的思想。

下面对每个Java文件代码进行分析:

  • Main.Java
init();
    while (true) {
      System.out.println("***********欢迎使用中小学数学卷子自动生成程序***********");
      User user = login();
      menu(user);
    }

分析:对代码进行封装,init()方法进行用户的初始化,login()方法用来实现用户的登录功能,menu()方法用来实现用户登录成功后的功能选择和处理。

public static User login() {
    Scanner scan = new Scanner(System.in);
    System.out.println("请输入用户名和密码,两者之间用空格隔开:");
    String temp = scan.nextLine();
    String[] info = temp.split("\\s+");
    if (info.length == 2) {
      for (User user : users) {
        if (user.check(info[0], info[1])) {
          System.out.println("登陆成功\n当前选择为" + user.getType() + "出题");
          return new User(info[0], info[1], user.getType());
        }
      }
    }

    System.out.println("用户名或密码错误,请重新输入!\n");
    return login();
  }

分析:①使用\\s+来对后置空格的数量进行处理,值得学习②递归调用login()方法,看似没有问题,但是当恶意登录错误的时候会存在爆栈导致程序崩溃,这个是需要注意的地方!
个人建议:将递归调用修改为使用一个boolean变量进行登录成功与否的判断。

public static void menu(User user) {
    while (true) {
      System.out.println("准备生成" + user.getType()
          + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录)(切换类型格式:切换为xx):");
      Scanner scan = new Scanner(System.in);
      String s = scan.nextLine().trim();
      if (s.contains("切换")) {
        user.changeType(s);

      } else {
        int i;
        try {
          i = Integer.parseInt(s); // 转化字符类型数字
          if (i == -1) {
            break;
          } else if (i >= 10 && i <= 30) {
            System.out.println("正在生成" + user.getType() + "数学题目");
            user.generateQuestions(i);

          } else {
            System.out.println("题目数量的有效输入范围是10-30,请重新输入");
          }
        } catch (NumberFormatException e) { //不是数字
          System.out.println("请按要求输入\n");
        }
      }
    }
  }

分析:用try catch方法来巧妙解决根据输入类型(数字或字符串)跳转到不同处理方法的难题,值得学习。

  • User.Java
private String name;
private String password;
private String count_type; //小学 中学 高中
private Problem paper;
public User(String name, String password, String count_type);
public boolean check(String name, String password);
public void generateQuestions(int numQuestions);
public Set<String> loadPreviousQuestions(String f);
void changeType(String s);

分析:将name,password,count_type,paper均设为私有属性,提高数据的安全性。但需要注意的是,count_type并不符合Google Java规范,同时paper属性是Problem类型,有点意义不明。
check()方法用来检查账号密码是否正确,generateQuestions()方法用来生成题目,loadPreviousQuestions()方法用来获取已有题目,changeType()方法用来更改试卷类型。
使用Set来保存已有题目,使得后续查重时时间复杂度大大降低,值得学习。

public User(String name, String password, String count_type) {
    this.name = name;
    this.password = password;
    this.count_type = count_type;
    if (count_type.equals("小学")) {
      paper = new PrimaryProblem();
    } else if (count_type.equals("初中")) {
      paper = new MidProblem();
    } else {
      paper = new HighProblem();
    }
  }

分析:根据不同的类型来生成不同的对象,是很好的工厂方法模式的运用,值得学习!

void changeType(String s) {
    if (s.equals("切换为小学")) {
      paper = new PrimaryProblem();
      count_type = "小学";
      System.out.println("当前选择为" + getType() + "出题");
    } else if (s.equals("切换为初中")) {
      paper = new MidProblem();
      count_type = "初中";
      System.out.println("当前选择为" + getType() + "出题");
    } else if (s.equals("切换为高中")) {
      paper = new HighProblem();
      count_type = "高中";
      System.out.println("当前选择为" + getType() + "出题");
    } else {
      System.out.println("请输入:切换为xx(xx为小学,初中,高中其中一个)");
    }
  }

分析:在切换类型的时候生成一个新的对象,但是后续又对原先的paper属性count_type进行修改,属于是画蛇添足。

  • Problem.Java
public abstract class Problem {
  abstract String create(); // 生成题目
  abstract void addOperator(String[] question); // 添加运算符
  public void addBrackets(String[] s); // 添加括号
}

分析:在抽象类中声明了两个抽象方法,子类继承时根据自身特性进行重载,有利于代码的复用和拓展,保证代码规范性。

public void addBrackets(String[] s) { // 添加括号
    int size = s.length;
    if (size > 2) {
      Random random = new Random();
      int begin = random.nextInt(size - 1);
      int end = random.nextInt(size - 1 - begin) + begin + 1;
      if (!s[begin].contains(")") && !s[end].contains("(") && end - begin != size - 1 && !(
          s[begin].contains("(") && s[end].contains(")"))) {
        s[begin] = "(" + s[begin];
        s[end] = s[end] + ")";
      }
    }
  }

分析:使用5个条件同时成立与否来进行添加括号与否的判断,逻辑上过于复杂,同时只能添加一对括号,可以针对上述情况进行优化调整。

  • PrimaryProblem.Java
public class PrimaryProblem extends Problem {

  @Override
  String create() {

    Random random = new Random();
    int size = random.nextInt(4) + 2; // 2到5个操作数
    String[] question = new String[size];
    for (int i = 0; i < size; i++) {
      question[i] = String.valueOf(random.nextInt(100) + 1);
    }
    addOperator(question);

    StringBuilder result = new StringBuilder();
    for (String s : question) {
      result.append(s);
    }
    result.append("=");
    return result.toString();
  }

  @Override
  void addOperator(String[] question) {
    Random random = new Random();
    int size = question.length;
    if (size > 2 && random.nextBoolean()) { //操作数大于二则考虑加括号
      addBrackets(question);
    }
    String[] operator = {"+", "-", "*", "/"};
    for (int i = 0; i < size - 1; i++) { // 添加符号 + - * /
      question[i] = question[i] + operator[random.nextInt(4)];
    }
  }

分析:使用String数组和随机数来进行出题,考虑到了小学题目必须要有两个操作数的情况,是一种出题的好方法,值得学习。

  • MidProblem.Java
    整体和PrimaryProblem并无太大差别,只是加入了对"√"和"²"的处理

  • HighProblem.Java

switch (op) {
        case 0:
          question[i] = "sin" + question[i];
          flag = true;
          break;
        case 1:
          question[i] = "cos" + question[i];
          flag = true;
          break;
        case 2:
          question[i] = "tan" + question[i];
          flag = true;
          break;
        case 3:
          if (!question[i].contains("√")) {
            question[i] = "√" + question[i];
          }
          break;
        case 4:
          if (!question[i].contains("²")) {
            question[i] = question[i] + "²";
          }
          break;
        default:
          addBrackets(question);
      }
    }
    if (!flag) {
      int op = random.nextInt(3);
      if (op == 0) {
        question[size - 1] = "sin" + question[size - 1];
      } else if (op == 1) {
        question[size - 1] = "cos" + question[size - 1];
      } else {
        question[size - 1] = "tan" + question[size - 1];
      }
    }

分析:在addOperator()方法中,重复地进行添加三角函数的操作,导致方法行数为41行违反了要求中方法行数不超过40行的要求T T
在后续询问中发现是由于ghw同学粗心大意修改方法后忘记删除,导致白白被扣掉10分,可怜他三秒QAQ
ghw同学的惨痛经历告诉我们,在最后的时候一定不要随意修改代码,要保证有充足的时间来检查!!!

三.总结

合理性

1.使用继承和抽象类:代码使用了继承和抽象类,通过 Problem 抽象类定义了数学问题生成的基本模板,然后通过 PrimaryProblem、MidProblem 和 HighProblem 具体子类实现了不同难度级别的问题生成逻辑。这种设计使得代码更具可扩展性,可以轻松添加新的数学问题类型。
2.随机性:使用 java.util.Random 来引入随机性,增加了生成问题的多样性。这对于生成多个随机问题是合理的,因为不同的问题可以具有不同的随机因素。
3.封装性:类的成员变量都被适当地封装,方法和变量的访问修饰符使用得当,符合面向对象编程的封装原则。
4.代码重用:通过将问题生成的通用逻辑放在 Problem 类中,不同难度级别的问题生成子类可以重用通用的代码,这是一个良好的设计实践。

不合理性

1.递归登录:在 Main 类中的 login() 方法中,当用户名或密码错误时,使用递归来重新尝试登录。这可能导致栈溢出错误,如果多次登录失败,递归的深度将会增加。更好的做法是使用循环来处理错误的登录尝试,以避免栈溢出。
2.代码注释:整个项目缺少缺少注释来解释其目的和关键部分的逻辑:如对属性的注释、对方法作用的注释,方法中复杂逻辑的解释,导致开始阅读时存在一定的困难。注释对于理解代码的重要性不可忽视。

ghw同学的项目充分使用了面向对象的思想,将很多功能进行了封装。功能模块划分清晰。程序中文字提示清晰,对用户体验良好。只是细节方面需要多加注意,比如属性名,类名,方法名等是否满足Google Java的规范,是否需要添加必要的注释来增加代码的可读性等

四.一点小小展望

在和ghw同学讨论的过程中,我们都发现彼此存在的一些问题,通过对问题的思考和解决,也让我们收获良多!
希望在后续的结对编程中,我们两个能吸取个人项目中的各种经验教训,对代码进行进一步的优化,做出更加合理优秀的程序xx

标签:count,String,question,HNU,数学试卷,new,type,中小学,size
From: https://www.cnblogs.com/zzzjw/p/17715181.html

相关文章

  • 【230901-7】有8位学生,其中小学生2名,初中生3名,高中生3名,现将他们排成一列,要求2名小学
    【230901-7】有8位学生,其中小学生2名,初中生3名,高中生3名,现将他们排成一列,要求2名小学生相邻,3名初中生相邻,3名高中生任意两名都不相邻,则不同排法有几种?......
  • 中小学教育综合管理平台源码,vue2+Java+springboot框架开发
    智慧校园电子班牌软件是出于校园考勤管理以及班级校园信息展示为目的的显示系统软件,电子班牌系统主要用于中小学教育的综合管理平台,融合了多媒体技术、语音技术、人脸识别、信息发布、后台管理等多种技术。智慧班牌通过以云平台为基础,结合互联网、物联网系统进行校园管理,实现学校数......
  • IfcDayInMonthNumber
    IfcDayInMonthNumber类型定义IfcDayInMonthNumber是一个整数,用于定义指定日期在一个月中的位置。 类型:整数 IFC1.5.1中的新型。IFC4添加规则ValidRange的位置 FormalPropositionsRuleDescriptionValidRangeThevalidrangeforpositioningadayinamonth......
  • 瑞熙贝通中小学实验室信息化管理系统--整体解决方案
    随着教育水平不断前行发展,各类校园也紧跟现代教育建设要求,不断提升教育实训及实验教学水平。与此同时,校园已走上教育现代化与智能化管理的道路上,校园实验室综合管理平台搭建助力智慧校园进一步提升教学水平。随着教务管理实行“学分制”改革的不断深入,传统的按照班级集体上实验课的......
  • 学习笔记409—中小学NB资源汇聚!
    中小学学习工具及资料:一、网页:      1.https://basic.smartedu.cn/syncClassroom/prepare(国家中小学智慧教育平台)      2.https://www.eduyun.cn/(国家教育资源公共服务平台)      3.https://syzx-edu.com/new/#/(全国中小学实验在线平台)     ......
  • KthNumber
    [ABC295E]KthNumber考虑这个贡献可以表示成这样\[\sum_{i=1}^mi\timesp_i\]其中\(p_i\)是答案为\(i\)的概率。我们可以考虑枚举空位的可能,设空位有\(w\)个,满足选择的数\(\lei-1(<i)\)的空位有\(x\),\(=i\)的有\(y\)个。很容易发现只需要满足以下两个条件那么......
  • P-smoothnumber
    [ABC300G]P-smoothnumber上来看到题就可以爆搜了。状态是\(f[p][n]\)表示现在再在处理第\(p\)小的素数,剩余\(n\)。然后转移是\(f[n][p]=f[n/prime[p]][p]+f[n][p-1]\),分别表示除掉一个\(prime\),转到下一个\(prime\),这样做显然是对的,因为如果一个数在范围内,那么这样除只......
  • 适合中小学学生初次学习编程语言Scratch
    2023年4月接触了Scratch,不用写代码,全图形化,拖动图标即可完成编程,简单几步能做出‘切西瓜'等小游戏或效果。入门简单Scratch适合中小学学生初次学习编程语言时使用,尤其是没有编程基础或编程基础较少的孩子,用来进行编程启蒙最合适不过了。 内容丰富Scratch编程内容丰富,形式多......
  • HNU2019 Summer Training 3 E. Blurred Pictures
    E.BlurredPicturestimelimitpertest2secondsmemorylimitpertest256megabytesinputstandardinputoutputstandardoutputDamonlovestotakephotosoftheplaceshevisitsduringhistravels,toputthemintoframes.Allofhisphotosarei......
  • 如果我要开发一个中小学生学习的数学软件,我应该找谁去做用户调研
    如果我去开发一个中小学数学学习数学的软件,那么首先我会知道这个软件的绝大部分受众用户是中小学生,对于学习当然是自己对自己的学习情况和方法最为了解,因此我会用调查问卷并实地去询问一些中小学生的意见和学习方法。其次由于中小学生可能并不会具有较好的学习方法以及习惯,因此我......