个人项目-小初高算数题目生成(Java互评)

目录

  1. 简介
  2. 测试与分析
    1. 功能测试
    2. 代码分析
  3. 总结

一、简介

    本博客对结对编程队友高义林同学的个人项目进行分析测试,使用语言为Java。需求如下图所示:

二、测试与分析

1.功能测试

   1.登录功能测试

  分析:登录时输入错误账密、错误密码、空的账密均不可成功登录,可以满足普通的登陆需求,且提示到位,登录成功后出现准备生成题目提示,符合要求。

  分析:可以正常退出登录,退出之后可以再次登录,满足日常需要。

  2.生成题目功能与切换难度功能测试

  分析:输入10~30之间的数字可以正常生成试卷,输入不符合要求的数字会出现错误提示并返回生成界面,符合需求。试卷文件名以规定时间格式命名,每道题目之间以空行隔开,以题号开头,整洁美观,符合需求。

  分析:切换时输入错误会触发提示信息,输入正确后成功切换,生成的题目难度也切换完毕。

2.代码分析

  1.项目结构

    本项目共有7个类,包含2个抽象类Question和Check,其余5个类分别是CheckUnit,FileProduct,Main,QuestionUnit,User。其中QuestionUnit类继承Question类,CheckUnit类继承Check类,实现抽象方法。

  CheckUnit类

实现抽象类Check功能,完成查重操作

FileProduct类

    生成试卷的文件操作

  Main类

  初始化账户,实现操作界面

QuestionUnit类

      生成题目

  User类

      账户类型

  2.代码分析

  • Main.java
  • import java.util.ArrayList;
    import java.util.Scanner;
    
    public class Main {
      public static boolean flag;
      public static ArrayList<User> userList;
      public static User curUser;
    
      public static void initArray() {
        userList = new ArrayList<>();
        userList.add(new User("小学", "张三1", "123"));
        userList.add(new User("小学", "张三2", "123"));
        userList.add(new User("小学", "张三3", "123"));
        userList.add(new User("初中", "李四1", "123"));
        userList.add(new User("初中", "李四2", "123"));
        userList.add(new User("初中", "李四3", "123"));
        userList.add(new User("高中", "王五1", "123"));
        userList.add(new User("高中", "王五2", "123"));
        userList.add(new User("高中", "王五3", "123"));
      }
    
      public static void mainMenu() {
        System.out.println("请输入用户名和密码,两者之间用空格隔开,或输入exit退出");
        while (!flag) {
          String temp;
          Scanner scanner = new Scanner(System.in);
          temp = scanner.nextLine();
          if (temp.equals("exit")) {
            flag = true;
            System.out.println("系统退出");
            continue;
          }
          String[] tempUser = temp.split(" ");
          if (tempUser.length != 2) {
            System.out.println("请输入正确的用户名、密码”,重新输入用户名、密码");
          } else {
            for (User user : userList) {
              if (user.getName().equals(tempUser[0]) && user.getPassword().equals(tempUser[1])) {
                curUser.setName(user.getName());
                curUser.setPassword(user.getPassword());
                curUser.setType(user.getType());
                flag = true;
                break;
              }
            }
            if (!flag) System.out.println("请输入正确的用户名、密码”,重新输入用户名、密码");
            else {
              testProductMenu(curUser);
              flag = false;
              curUser = new User();
              System.out.println("请输入用户名和密码,两者之间用空格隔开,或输入exit退出");
            }
          }
        }
      }
    
      public static void testProductMenu(User curUser) {
        boolean flag = true;
        while (flag) {
          System.out.println("准备生成" + curUser.getType() + "数学题目,请输入生成题目数量");
          System.out.println("题目数量在10~30之间,或输入-1退出登录,或输入”切换为xx“更换出卷类型");
          Scanner scanner = new Scanner(System.in);
          String temp = scanner.nextLine();
          if (temp.length() > 3 && temp.startsWith("切换为")) {
            temp = temp.substring(3);
            switch (temp) {
              case "小学" -> curUser.setType("小学");
              case "初中" -> curUser.setType("初中");
              case "高中" -> curUser.setType("高中");
              default -> System.out.println("请输入小学、初中和高中三个选项中的一个");
            }
          } else {
            int num;
            try {
              num = Integer.parseInt(temp);
              if (num == -1) {
                flag = false;
                System.out.println("退出登录");
              } else if (num >= 10 && num <= 30) {
                FileProduct.createTest(curUser, num);
              } else System.out.println("输入数量不在10~30之间");
            } catch (NumberFormatException exc) {
              System.out.println("输入格式错误");
            }
          }
        }
      }
    
      public static void main(String[] args) {
        flag = false;
        curUser = new User();
        initArray();
        mainMenu();
      }
    }

  分析:Main类中实现了整个界面操作,flag变量标记是否切换界面。首先创建当前用户,初始化用户列表,进入主菜单,随后进行菜单操作。

  • FileProduct.java
  • import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class FileProduct {
      public static CheckUnit checkUnit = new CheckUnit();
      public static QuestionUnit questionUnit = new QuestionUnit();
    
      public static void createTest(User curUser, int num) {
        File file = createFile(curUser);
        File checkFile = checkUnit.getDir();
        FileWriter fileWriter;
        FileWriter fileWriter1;
        try {
          fileWriter = new FileWriter(file, true);
          fileWriter1 = new FileWriter(checkFile, true);
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
        String tempQuestion;
        for (int i = 0; i < num; i++) {
          tempQuestion = formatQuestion(curUser, i, fileWriter1);
          try {
            fileWriter.write(tempQuestion + "\n\n");
          } catch (IOException e) {
            throw new RuntimeException(e);
          }
        }
        try {
          fileWriter.close();
          fileWriter1.close();
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
        System.out.println("试卷创建成功");
      }
    
      public static File createFile(User curUser) {
        Date date = new Date();
        String time = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(date);
        String path =
            System.getProperty("user.dir") + "\\user\\" + curUser.getName() + "\\" + curUser.getType();
        String filename = time + ".txt";
        File file = new File(path + "\\" + filename);
        File dir = new File(path);
        if (!dir.exists()) {
          dir.mkdirs();
        }
        dir = new File(path + "\\check.txt");
        checkUnit.setFile(file);
        checkUnit.setDir(dir);
        return file;
      }
    
      public static String formatQuestion(User curUser, int i, FileWriter fileWriter) {
        boolean flag;
        String tempQuestion = "";
        flag = false;
        while (!flag) {
          int typeNum = 0;
          if (curUser.getType().equals("小学")) typeNum = 1;
          else if (curUser.getType().equals("初中")) typeNum = 2;
          else if (curUser.getType().equals("高中")) typeNum = 3;
          tempQuestion = questionUnit.createQuestion(typeNum) + "=";
          flag = checkUnit.checkQuestion(tempQuestion);
          if (flag) {
            try {
              fileWriter.write(checkUnit.hashCode(tempQuestion) + "\n");
            } catch (IOException e) {
              throw new RuntimeException(e);
            }
          }
          tempQuestion = i + 1 + ". " + tempQuestion;
        }
        return tempQuestion;
      }
    }

  分析:此类主要由三个方法组成,完成对文件的操作。首先createFile方法准备好文件路径,formatQuestion方法调用questionUnit.createQuestion方法和checkUnit.checkQuestion方法实现单个题目的生成和查重,最后上述两种方法被createTest方法调用生成整张试卷。

  • User.java
  • public class User {
      String name;
      String type;
      String password;
    
      public User() {}
    
      public User(String type, String name, String password) {
        this.type = type;
        this.name = name;
        this.password = password;
      }
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        this.name = name;
      }
    
      public String getPassword() {
        return password;
      }
    
      public void setPassword(String password) {
        this.password = password;
      }
    
      public String getType() {
        return type;
      }
    
      public void setType(String type) {
        this.type = type;
      }
    }

  分析:实现了User类的三个属性的构造和调用。

  • Check.java
  • import java.io.File;
    
    public abstract class Check {
      static File file;
      static File dir;
    
      public abstract File getFile();
    
      public abstract void setFile(File file);
    
      public abstract File getDir();
    
      public abstract void setDir(File dir);
    
      public abstract Boolean checkQuestion(String question);
    
      public abstract String hashCode(String question);
    }

  分析:实现了抽象方法的定义。

  • Question.java
  • public abstract class Question {
      public static final String[] operator0 = {"+", "-", "*", "/"};
      public static final String[] operator1 = {"sqrt", "^2"};
      public static final String[] operator2 = {"sin", "cos", "tan"};
    
      public abstract String createQuestion(int typeNum);
    }

  分析:实现了抽象方法的定义。

  • CheckUnit.java
  • import java.io.*;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    public class CheckUnit extends Check {
      @Override
      public File getFile() {
        return file;
      }
    
      @Override
      public void setFile(File file) {
        Check.file = file;
      }
    
      @Override
      public File getDir() {
        return dir;
      }
    
      @Override
      public void setDir(File dir) {
        Check.dir = dir;
      }
    
      @Override
      public Boolean checkQuestion(String question) {
        FileReader fileReader;
        try {
          fileReader = new FileReader(dir);
        } catch (FileNotFoundException e) {
          throw new RuntimeException(e);
        }
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        question = hashCode(question);
        try {
          String temp = bufferedReader.readLine();
          while (temp != null) {
            if (temp.equals(question)) {
              System.out.println("A question is already in the database");
              return false;
            }
            temp = bufferedReader.readLine();
          }
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
        return true;
      }
    
      @Override
      public String hashCode(String question) {
        MessageDigest md;
        try {
          md = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
          throw new RuntimeException(e);
        }
        byte[] hashInBytes = md.digest(question.getBytes());
        StringBuilder sb = new StringBuilder();
        for (byte b : hashInBytes) {
          sb.append(String.format("%02x", b));
        }
        return sb.toString();
      }
    }

  分析:继承抽象类,实现抽象方法。主要功能由checkQuestion方法和hashCode方法实现。其中checkQuestion方法逐条检查历史记录文件,寻找重复题目,hashCode方法将题目转换为哈希码实现加密,最后查重比较哈希码是否相同。

  • QuestionUnit.java
  • import java.util.Random;
    
    public class QuestionUnit extends Question {
    
      @Override
      public String createQuestion(int typeNum) {
        Random rand = new Random();
        int num = rand.nextInt(5) + 1;
        if (typeNum == 1) {
          num = rand.nextInt(4) + 2;
        }
        String[] numberList = new String[num];
        for (int i = 0; i < num; i++) {
          numberList[i] = String.valueOf(rand.nextInt(100) + 1);
        }
        String Question = "";
        int pos = rand.nextInt(num) + 1, caseNum;
        for (int i = 1; i <= num; i++) {
          if (typeNum == 2 && i == pos) {
            caseNum = rand.nextInt(2) + 1;
            if (caseNum == 1) Question += operator1[0] + "(" + numberList[i - 1] + ")";
            else if (caseNum == 2) Question += numberList[i - 1] + operator1[1];
          } else if (typeNum == 3 && i == pos) {
            caseNum = rand.nextInt(3) + 1;
            if (caseNum == 1) Question += operator2[0] + "(" + numberList[i - 1] + ")";
            else if (caseNum == 2) Question += operator2[1] + "(" + numberList[i - 1] + ")";
            else if (caseNum == 3) Question += operator2[2] + "(" + numberList[i - 1] + ")";
          } else Question += numberList[i - 1];
          if (i != num) Question += operator0[rand.nextInt(operator0.length)];
        }
        return Question;
      }
    }

  分析:此类继承Question类,实现抽象方法createQuestion。其主要思想在于先确定生成操作数的数量,然后选定一个操作数,根据不同难度对其进行不同操作。例如生成了五个操作数12345,选定4进行高中难度转换,就可能变成123sqrt(4)5,最后用随机符号依次拼接即可。但是这样只会对一个操作数进行难度转换。

三、总结

  此项目基本完成了所给需求,测试下来也没有bug,使用体验良好,稍加修改即可成为一个实用的小程序。此外,还有许多地方可以进行功能升级,例如将账户信息保存到本地或者云端,实现初高中难度的多个操作数难度转换,在生成试卷时等待1秒避免题目数据重复写入等功能。

posted @ 2023-09-20 13:13  仿生代码蟹  阅读(522)  评论(0)    收藏  举报