结对编程——队友个人项目分析
结对编程——队友个人项目分析
一、简介
本博客是对结对编程队友ljy的个人项目代码的分析和总结
- 项目内容:中小学数学卷子自动生成程序
- 实现语言:java
- 代码结构:按功能、实体封装代码,分用户、题目、登录界面三个类实现项目。
二、代码运行结果
1.登录
正确登录的情况

登录失败、输出提示信息

2.生成试卷
对输入题目数量合法性判断,输入-1时正常退出,重新登录

正确输入时,默认生成用户本身难度试卷在项目同级目录下



3.切换难度
输入不合法的情况

输入合法的情况

4.各级难度试卷验证
小学

初中

高中

题目生成基本符合要求
三、代码分析
共设计了用户User类,数学问题Problem类,登录主页面indexView类3个类
1.User类
注意了对成员变量的隐藏
代码:
package main;
/**
- 用户类
*/
public class User {
private String userType;
private String userName;
private String userPwd;
public User() {
}
public User(String userType, String userName, String userPwd) {
this.userType = userType;
this.userName = userName;
this.userPwd = userPwd;
}
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
}
2.Problem类解决卷子的生成功能
getExistedProblems(String name)返回用户生成过的试卷中的题目集合,通过hashset来实现去重功能
getProblemsByType(String type, int num, String name)根据用户选择的类型来分别调用getPrimaryProblem,getJuniorProblem,getHighProblem函数生成指定数量的试卷在同名目录下
代码:
package main;
import java.io.*;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Random;
import java.util.Scanner;
/**
- 数学问题类
*/
public class Problem {
public static String[] operators = {"+", "-", "*", "/", "²", "√", "sin", "cos", "tan"};
public static int[] sanFigure = {0, 30, 45, 60, 90}; // 设计合理的三角函数值
private static Random random = new Random(System.currentTimeMillis());
/**
- 根据用户名获取已经为其生成的题目集合
- @param name
- @return
*/
public static HashSet <String> getExistedProblems(String name) {
HashSet <String> probList = new HashSet<>();
File file = new File("./" + name);
if (!file.exists()) {
file.mkdir();
}
File[] fileList = file.listFiles();
for (File f : fileList) { // 遍历所有文件
try {
BufferedReader br = new BufferedReader(new FileReader(f));
String str;
while ((str = br.readLine())!= null) {
int index = str.indexOf('.'); // 获取的题目去除前面的编号
if (index == -1) {
continue;
}
probList.add(str.substring(index + 1));
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return probList;
}
/**
- 根据学校类型,输入数量和用户名生成试卷文件
- @param type
- @param num
- @param name
- @return
*/
public static Boolean getProblemsByType(String type, int num, String name) {
Scanner in = new Scanner(System.in);
while (num < 10 || num > 30) {
if (num == -1) {
return false;
}
System.out.println("请控制题目数量在10-30");
num = in.nextInt();
}
HashSet <String> existedProblems = getExistedProblems(name);
Calendar cal = Calendar.getInstance();
String path = "./" + name + "/" + cal.get(Calendar.YEAR) + "-" + (cal.get(Calendar.MONTH)+1) + "-" + cal.get(Calendar.DATE)
+ "-" + cal.get(Calendar.HOUR_OF_DAY) + "-" + cal.get(Calendar.MINUTE) + "-" + cal.get(Calendar.SECOND) + ".txt";
String problem = "";
try {
OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(path), "UTF-8");
for (int i = 0; i < num; i++) {
if ("小学".equals(type)) {
do {
problem = getPrimaryProblem();
} while (existedProblems.contains(problem)); // 若重复则重新生成到不重复为止,下同
fw.write(i + 1 + "." + problem + "\n\n");
fw.flush();
}
if ("初中".equals(type)) {
do {
problem = getJuniorProblem();
} while (existedProblems.contains(problem));
fw.write(i + 1 + "." + problem + "\n\n");
fw.flush();
}
if ("高中".equals(type)) {
do {
problem = getHighProblem();
} while (existedProblems.contains(problem));
fw.write(i + 1 + "." + problem + "\n\n");
fw.flush();
}
}
System.out.println("生成完毕,请在项目同级目录查收");
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
- 生成小学题目
- @return
*/
private static String getPrimaryProblem() {
String problem = "";
int figureNum = random.nextInt(4) + 2; // 小学的操作数至少为2
int khMax = random.nextInt(5);
int left = 0;
int right = 0;
boolean khFlag = false; // 避免出现”(数字)“的情况
for (int i = figureNum; i > 1; i--) {
if ((random.nextBoolean()) && (left < khMax) && (i != figureNum)) {
problem += "(";
left++;
khFlag = false;
}
problem += random.nextInt(100) + 1;
if ((random.nextBoolean()) && (right < left) && khFlag) {
problem += ")";
right++;
}
khFlag = true;
problem += operators[random.nextInt(4)];
}
// 处理最后一个操作数
problem += random.nextInt(100) + 1;
while (right < left) {
problem += ")";
right++;
}
problem += "=";
return problem;
}
/**
- 生成初中题目
- @return
*/
private static String getJuniorProblem() {
String problem = "";
int figureNum = random.nextInt(5) + 1;
int khMax = random.nextInt(5);
int left = 0;
int right = 0;
boolean khFlag = false; // 避免出现”(数字)“的情况
boolean pfFlag = true; // 避免出现开根又平方的情况
int speacial = 0;
for (int i = figureNum; i > 1; i--) {
if (random.nextBoolean()) {
problem += "√";
speacial++;
pfFlag = false;
}
if ((random.nextBoolean()) && (left < khMax) && (i != figureNum)) {
problem += "(";
left++;
khFlag = false;
}
problem += random.nextInt(100) + 1;
if ((random.nextBoolean()) && (right < left) && khFlag) {
problem += ")";
right++;
}
if (random.nextBoolean() && pfFlag) {
problem += "²";
speacial++;
}
khFlag = true;
pfFlag = true;
problem += operators[random.nextInt(4)];
}
// 处理最后一个操作数
if (random.nextBoolean()) {
problem += "√";
speacial++;
pfFlag = false;
}
problem += random.nextInt(100) + 1;
while (right < left) {
problem += ")";
right++;
}
if ((speacial == 0) || random.nextBoolean()) { // 确保含有开根或平方
if (pfFlag) {
problem += "²";
}
}
problem += "=";
return problem;
}
/**
- 生成高中题目
- @return
*/
private static String getHighProblem() {
String problem = "";
int figureNum = random.nextInt(5) + 1;
int khMax = random.nextInt(5);
int left = 0;
int right = 0;
boolean khFlag = false; // 避免出现”(数字)“的情况
boolean pfFlag = true; // 避免出现开根又平方的情况和三角函数中数字平方的情况
boolean sanFlag = true; // 避免多个三角函数同时出现在一个操作数上的情况和开根中出现三角函数的情况
int sanjiao = -1; // 记录出现三角函数时运算符的下标
int speacial = 0;
for (int i = figureNum; i > 1; i--) {
if (random.nextBoolean()) {
problem += "√";
pfFlag = false;
sanFlag = false;
}
if ((random.nextBoolean()) && (left < khMax) && (i != figureNum)) {
problem += "(";
left++;
khFlag = false;
}
if (random.nextBoolean() && sanFlag) {
sanjiao = random.nextInt(3);
problem += operators[sanjiao + 6];
speacial++;
pfFlag = false;
}
if (sanjiao == -1) { // 设计三角函数解出特殊值利于计算
problem += random.nextInt(100) + 1;
}else if (sanjiao < 2) {
problem += sanFigure[random.nextInt(5)];
}else if (sanjiao == 2) {
problem += sanFigure[random.nextInt(4)];
}
if ((random.nextBoolean()) && (right < left) && khFlag) {
problem += ")";
right++;
}
if (random.nextBoolean() && pfFlag) {
problem += "²";
}
khFlag = true;
pfFlag = true;
sanFlag = true;
sanjiao = -1;
problem += operators[random.nextInt(4)];
}
// 处理最后一个操作数
if (random.nextBoolean() && (speacial > 0)) {
problem += "√";
pfFlag = false;
sanFlag = false;
}
if (random.nextBoolean() || (speacial == 0)) {
if (sanFlag) {
sanjiao = random.nextInt(3);
problem += operators[sanjiao + 6];
pfFlag = false;
}
}
if (sanjiao == -1) {
problem += random.nextInt(100) + 1;
}else if (sanjiao < 2) {
problem += sanFigure[random.nextInt(5)];
}else if (sanjiao == 2) {
problem += sanFigure[random.nextInt(4)];
}
while (right < left) {
problem += ")";
right++;
}
if (random.nextBoolean()) {
if (pfFlag) {
problem += "²";
}
}
problem += "=";
return problem;
}
}
3.indexView负责处理用户的登录与输入
静态代码块录入用户信息在成员变量中
loginConfirmByData(String name, String pwd)通过用户名和密码检验登录用户是否合法,即是否在成员变量用户集合中
login()使用户合法地登录
getTypeAndNum()获得用户指定的类型与卷子数量,在其中做合法性的判断
主函数执行循环,保证当前的登录状态并不断提供出题服务
代码:
package main;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
- 登录主页面类
*/
public class indexView {
private static List<User> userList;
private static User nowUser = null;
static {
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"));
}
/**
- 根据账号密码判断是否存在合法用户
- @param name
- @param pwd
- @return
*/
private static Boolean loginConfirmByData(String name, String pwd) {
Boolean loginSuccess = false;
for (int i = 0; i < userList.size(); i++) {
User tempUser = userList.get(i);
if (tempUser.getUserName().equals(name) && tempUser.getUserPwd().equals(pwd)) {
loginSuccess = true;
nowUser = tempUser;
System.out.println("当前选择为" + nowUser.getUserType() + "出题");
}
}
return loginSuccess;
}
/**
- 登录直至成功
*/
private static void login() {
if (nowUser != null) { // 已在登录状态
return;
}
Scanner in = new Scanner(System.in);
System.out.println("请输入用户名和密码");
Boolean loginFlag = loginConfirmByData(in.next(), in.next());
while (!loginFlag) {
System.out.println("请输入正确的用户名、密码");
loginFlag = loginConfirmByData(in.next(), in.next());
}
}
/**
- 获取用户生成卷子的类型和题目数量
- @return
*/
private static int getTypeAndNum() {
Scanner in = new Scanner(System.in);
String switchType = nowUser.getUserType();
System.out.println("准备生成" + switchType + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):");
String cmd = in.next();
if (cmd.contains("切换")) {
cmd += "suffix"; // 后缀防止用户输入字符串长度过短
switchType = cmd.substring(3, 5);
while (!("小学".equals(switchType) || "初中".equals(switchType) || "高中".equals(switchType))) {
System.out.println("请输入小学、初中和高中三个选项中的一个");
switchType = in.next();
}
System.out.println("准备生成" + switchType + "数学题目,请输入生成题目数量");
cmd = in.next();
}
nowUser.setUserType(switchType);
return Integer.parseInt(cmd);
}
public static void main(String[] args) {
while (true) {
login();
int num = getTypeAndNum();
If(!Problem.getProblemsByType(nowUser.getUserType(), num, nowUser.getUserName())) {
nowUser = null; // 输入为-1的情况,当前用户为空,需要重新登录
}
}
}
}
四、优缺点分析
1.优点:
- 对代码各个模块功能进行了一定的拆分,有合理的注释,基本符合代码规范。
- 考虑了三角函数时数据的合理性,对算式的复杂程度做出了限制,比如开方和三角函数不能同时作用在一个操作数上,使得项目更加贴切实际,有一定的可用性。
2.缺点:
- 代码比较冗余,特别是生成题目的部分,生成各难度题目的函数体太长且有重复,使得可读性较差,可以尝试进一步将代码复用。
- 主页面类忘记大写开头字母。生成试卷时,时间如果不足十位只能显示1位而非2位,不够整齐规范。
- 对于算式的具体设计方面,没有考虑到算式两端可能出现多余的一对括号的情况。
- 没有将用户的个人信息存储在文件中而是直接写在代码中,耦合度太高,使得管理用户信息需要改动程序源码重新编译。
- 用户的体验一般,没有什么提示信息。经测试使用时会因为数据类型不匹配而崩溃,代码的健壮性有待加强。
浙公网安备 33010602011771号