队友:张笑睿
语言:Java
本博客是对我的队友的个人项目的代码分析及评价。个人项目的内容为中小学生试题自动生成系统。
可以看出在登陆时随便输入的字符串系统会检测出来并提示用户输入正确的用户名以及密码,在生成数学题时如果输入不符合规范会检测到并提示用户输入正确的范围或指令。由此看出程序的安全性和可靠性还是比较高的。
可以看出生成试卷的功能可以实现,并且按照要求在指定路径下生成规定格式的试卷,并且以txt的形式保存下来,符合题目要求。
美中不足的是并没有很清晰的表明生成的试卷是那种类型,可以稍微改进一下增加用户的体验。
各个类的分析如下:
此类为操作类,用来存储用户信息,进行登录验证和操作种类的判断。
属性:
变量 | 类型 | 含义 |
---|---|---|
userMap | HashMap<String, User> | 用户信息Map |
方法:
构造方法
public Operations() { userMap.put("张三1", new User("张三1", "123", "小学")); userMap.put("张三2", new User("张三2", "123", "小学")); userMap.put("张三3", new User("张三3", "123", "小学")); userMap.put("李四1", new User("李四1", "123", "初中")); userMap.put("李四2", new User("李四2", "123", "初中")); userMap.put("李四3", new User("李四3", "123", "初中")); userMap.put("王五1", new User("王五1", "123", "高中")); userMap.put("王五2", new User("王五2", "123", "高中")); userMap.put("王五3", new User("王五3", "123", "高中")); }
将用户信息存储在map中,方便登录时的验证比较,查找效率较高。此处可以考虑将用户信息存到数据库中。
用户登录方法
public User userLogin(String username, String password) { if (userMap.get(username) == null) { System.out.println("请输入正确的用户名、密码"); return null; } User user = userMap.get(username); if (user.getPassword().equals(password)) { System.out.println("当前选择为" + user.getType() + "出题"); return user; } else { System.out.println("请输入正确的用户名、密码"); return null; } }
将输入框中的内容获取到并进行校验,成功即可登录,否则重新输入用户名和密码。
用户选择方法
public boolean selectOperation(String username, String password) { User user = userLogin(username, password); if (user != null) { while (true) { System.out.println("准备生成" + user.getType() + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录;输入-2将退出程序):"); String inStr = new Scanner(System.in).nextLine(); // 当输入的是 切换为XX 时 改变用户类型 if (inStr.contains("切换为")) { String sub = inStr.substring(0, 3); if (sub.equals("切换为")) { this.setUserType(username, inStr.substring(3)); } } else { // 输入的是数字时 识别数字 生成题目 try { int num = Integer.parseInt(inStr); if (num == -1) { // 输入-1退出登录 break; } else if (num == -2) { // 输入-2退出程序 return true; } else if (num >= 10 && num <= 30) { user.makePaper(num); } else { System.out.println("请输入10-30之间的自然数 或 输入-1退出登录 或 输入-2退出程序 或 切换为XX"); } } catch (NumberFormatException numberFormatException) { System.out.println("请输入10-30之间的自然数 或 输入-1退出 或 输入-2退出程序 或 切换为XX"); } } } } return false; }
此方法实现了用户要进行的各种操作。
用户类型切换方法
public void setUserType(String username, String type) { while (true) { if (type.equals("小学")) { userMap.get(username).setType(type); break; } else if (type.equals("初中")) { userMap.get(username).setType(type); break; } else if (type.equals("高中")) { userMap.get(username).setType(type); break; } else { System.out.println("请输入小学、初中和高中三个选项中的一个"); Scanner scanner = new Scanner(System.in); type = scanner.next(); } } }
获取输入内容通过if来判断并切换用户类型。
此类为提供试卷的用户类,可以获取用户信息,修改用户信息,生成试卷,创建用户专属的文件夹和试卷文件。
属性:
变量 | 类型 | 描述 |
---|---|---|
username | string | 用户名 |
password | string | 密码 |
type | string | 学校种类 |
方法:
get和set方法
public String getUsername() { return username; } public String getPassword() { return password; } public String getType() { return type; } public void setType(String type) { this.type = type; }
get和set方法来获取用户名,密码,学校种类并设置学校种类,清晰明了。
创建文件夹并生成txt文件方法
public File makeFile() { String mkPath = "paper\\" + this.getUsername(); File file = new File(mkPath); Date date = new Date(); // 时间处理 用于txt文件的名称 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); String title = simpleDateFormat.format(date) + ".txt"; if (!file.exists()) { // 单例模式 如果没有对应文件夹则生成 file.mkdirs(); } File paper = new File(mkPath, title); try { paper.createNewFile(); } catch (IOException e) { e.printStackTrace(); } return paper; }
此方法可以在对应路径下生成文件夹以及txt文件,其中判断没有文件夹就生成这部分做的比较细致,考虑周全,值得学习。
制作试卷的方法
public void makePaper(int num) { PaperMaker paperMaker = new PaperMaker(); File newFile = makeFile(); if (this.type.equals("小学")) { paperMaker.makePrimaryPaper(num, newFile); } else if (this.type.equals("初中")) { paperMaker.makeJuniorPaper(num, newFile); } else if (this.type.equals("高中")) { paperMaker.makeSeniorPaper(num, newFile); } }
此方法实现试卷生成功能,简洁明了,可读性较高。
此类为试卷制造类,可以将试题按顺序写入试卷文件中去。
属性:
变量 | 类型 | 描述 |
---|---|---|
titleMaker | TitleMaker | 题目制造类对象 |
方法:
小学试卷制造方法(初中,高中类似)
public void makePrimaryPaper(int number, File newFile) { // 生成多少道题目就执行多少次循环 for (int i = 0; i < number; i++) { Random random = new Random(); StringBuffer stringBuffer = new StringBuffer(); // 生成一道小学难度的题目 titleMaker.makePrimaryTitle(random, stringBuffer); // 存储题目 String title = stringBuffer.toString(); // 题目查重 if (this.check(newFile, title)) { i--; continue; } // 写入试卷 Writer writer = new Writer(); writer.writeIn(newFile, i + 1, title); } }
该方法实现了小学试卷的制作,由于初中高中类似,不过多赘述。
试题查重方法
public boolean check(File file, String title) { // 定义变量 是否重复 boolean repetition = false; // 获取txt文件上一级目录的路径 String parentPath = file.getParent(); File parent = new File(parentPath); // 遍历上一级目录,获得其下的所有文件 File[] files = parent.listFiles(); // 一个一个文件进行查重对比 for (int i = 0; i < files.length; i++) { try { FileReader fileReader = new FileReader(files[i]); BufferedReader bufferedReader = new BufferedReader(fileReader); while (true) { // 读取一行数据 String usedTitle = bufferedReader.readLine(); // 如果是空行就跳过 if (usedTitle == null) { break; } // 以" "为分隔符," "后面的就是算式 String[] titleElements = usedTitle.split(" "); if (titleElements.length > 1) { if (titleElements[1].equals(title)) { System.out.println("重复"); // 如果重复改变变量的值 repetition = true; } } } } catch (Exception e) { e.printStackTrace(); } } return repetition; }
此方法实现试题查重的功能,核心算法为遍历所有文件中所有题目,效率较低,可以考虑采用数据库。
此类为文件写入类,用于试卷的写入。
方法:
写入文件方法
public void writeIn(File file, Integer n, String title) { try { // 传入文件 确定类型为续写 FileWriter fileWriter = new FileWriter(file, true); // 序号 String num = n.toString() + "."; fileWriter.append(num + " " + title); // 空一行 fileWriter.append("\r\n"); fileWriter.append("\r\n"); // 刷新文件,关闭writer fileWriter.flush(); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } }
该方法实现将试题内容写入文件中去。
此类为题目制造类,可以生成单个的算式。
属性:
变量 | 类型 | 描述 |
---|---|---|
elementMaker | ElementMaker | 元素制造类的对象 |
方法:
小学题目制造方法
public void makePrimaryTitle(Random random, StringBuffer stringBuffer) { // 判断括号配对 int bracket = 0; // 操作数最多个数 int cnt = 5; // 保证操作数不超过5个 while (cnt-- > 0) { // 判断是否本轮退出 boolean quit = elementMaker.quitOrNot(random, cnt); // 随机加入 ( Object[] objects = elementMaker.addLeftBracket(bracket, random, quit, stringBuffer); Boolean leftBracketOrNot = (Boolean) objects[0]; bracket = (Integer) objects[1]; // 加入数字 elementMaker.addNum(random, stringBuffer); // 判断 加入 ) bracket = elementMaker.addRightBracket(bracket, random, leftBracketOrNot, stringBuffer); // 随机结束算式 if (quit) { break; } // 随机生成符号 elementMaker.addSymbol(random, stringBuffer); } // 补全 ) elementMaker.closeBrackets(bracket, stringBuffer); // 输出 = stringBuffer.append("="); }
该方法实现了小学题目的制造,可以看出整个方法中大部分都在调用方法,xr同学对代码的封装性还是很强的,值得学习。初中和高中的题目制造方法类似,这里不做过多说明。
此类为元素制造类,用于生成算式中每个元素,并对其进行拼接形成算式。
属性:
变量 | 类型 | 描述 |
---|---|---|
primarySymbol | LinkedList | 存放小学难度符号 |
juniorSymbol | LinkedList | 存放初中难度符号 |
seniorSymbol | LinkedList | 存放高中难度符号 |
方法:
方法名 | 返回值 | 描述 |
---|---|---|
ElementMaker | 构造函数 | |
quitOrNot | boolean | 判断本轮添加数字后是否退出 |
addNum | void | 随机生成1-100之间的操作数 |
addLeftBracket | Object[] | 添加随机左括号 |
addRightBracket | int | 随机添加右括号 |
addEvolution | void | 随机添加根号 |
addSquare | int | 随机添加平方 |
addTFunction | int | 随机添加三角函数 |
addSymbol | void | 随机添加一种运算符 |
closeBrackets | void | 补全右括号 使括号闭合 |
方法数量过多,这里只举一个例子:
// 随机添加根号 public void addEvolution(Random random, StringBuffer stringBuffer) { // 随机加根号 int evolutionOrNot = random.nextInt(2); if (evolutionOrNot == 1) { stringBuffer.append(juniorSymbol.get(1)); } }
该方法实现了添加根号的功能。
该类为测试类,用于测试程序。
方法:
main方法
public static void main(String[] args) { Scanner in = new Scanner(System.in); Operations operations = new Operations(); while (true) { // 识别用户名和密码 System.out.println("请输入用户名、密码"); if (operations.selectOperation(in.next().trim(), in.nextLine().trim())) { break; } } }
创建operations对象开始程序。
1. 整体架构清晰明了,封装性很强。 2. 代码的功能实现非常完备,逻辑判断边界考虑非常周到。 3. 代码可读性很高,注释也很多,方便阅读。 4. 严格遵守Google编码规范,代码整体很美观。
总的来说,xr同学的程序整体上是非常不错的,是很值得我去借鉴学习的,希望以后可以多多跟xr大佬请教!