结对编程项目总结
一、简介
这篇博客是我对于我与结对伙伴梁原韶的共同完成的结对项目的代码分析。
我们的程序用swing类的组件完成各个界面,由我和梁原韶同学合作完成整个程序,我负责的部分是根据随机生成的题目字符串生成一个答案,并且在正确答案附近范围内生成三个错误答案,我的搭档梁原韶同学负责的部分是各个界面和监听器的添加以及一些逻辑部分,界面部分复用了一部分他在个人项目阶段写的部分。
实现语言:java
二、代码运行
首先是登录部分,这个部分复用了梁原韶同学在个人项目完成的一部分,并且进行了一些修改,在登录界面可以选择输入账号和密码进行登录,也可以选择注册账号。

接下来是注册界面

这部分是项目的一个难点,涉及到了第三方的API的调用,界面用流式布局设计,输入了要注册的账号和密码之后需要输入电话并确认短信验证码,之后才能完成注册。
登录之后的界面:

这部分也复用了过去的代码,和之前的流程差不多,选择自己要生成的题目类型,并且输入题目的数量进行题目的生成。

选择过后就进入了答题界面,我负责的部分就是答题部分根据表达式得出准确答案和错误答案的生成,这里给出了4个按钮,包含了一个正确答案和三个错误答案,选择后进入下一题。

这里快速跳到最后,答完所有题目之后,会根据答对题目的比例得出用户答题所获得的分数。
三、项目代码结构

图中所示就是我们结对项目的整个项目代码结构,可以看到,每个界面都有一个UI类和监听器Listenener类,监听器类会继承ActionListener类的功能函数,两个类分别负责整个界面的界面显示分布和点击动作获取之后的逻辑部分。
除此之外,PaperCreator类是一个我们用于生成随机题目并且得出题目的答案与错误选项的工具类,我们通过调用其中的函数来在答题界面中的功能,而所有题目生成和答案相关的逻辑都在这个工具类当中完成。
PaperCreator类中根据题目的字符串得到答案的部分由我来负责,我对于这部分功能实现的逻辑是将原本的字符串进行处理,将一些字符如除号替换为“/”,然后在根号和平方的部分都加上一些前缀和后缀使其符合JS中的运算规则,比如将3的平方处理成 Math.pow(3,2) 这样的形式,将根号转化成 Math.sqrt() 这样的形式,之后调用到eval函数将处理好的字符串生成答案,作为函数的返回部分。而错误答案的部分则是在正确答案附近根据随机数生成一些与正确答案不重复的错误答案。
这里附上这部分的代码方便读者更好的理解:
public class PaperCreator {
public static String createPaper(String type) {
//贯穿始末的随机数
Random rand = new Random();
//基本的加减乘除符号
String[] signs = {"+", "-", "*", "÷"};
//定义操作数数组
String[] operands = null;
int operand_num = 0;
//表达式
String formula = "";
if (type.equals("小学")) {
//小学操作数为2到5
operand_num = rand.nextInt(4) + 2;
operands = new String[operand_num];
for (int i = 0; i < operand_num; i++) {
operands[i] = String.valueOf(rand.nextInt(100) + 1);
}
} else if (type.equals("初中")) {
operand_num = rand.nextInt(5) + 1;
operands = new String[operand_num];
for (int i = 0; i < operand_num; i++) {
operands[i] = String.valueOf(rand.nextInt(100) + 1);
}
int special_num = rand.nextInt(operand_num) + 1;
boolean[] flag = new boolean[operand_num];
for (int i = 0; i < special_num; i++) {
//如果操作数位置没有加特殊符号的话
int index = rand.nextInt(operand_num);
if (!flag[index]) {
//百分之50概率会加²,另外百分之50加√
if (rand.nextBoolean()) {
operands[index] = operands[index] + "²";
} else {
operands[index] = "√" + operands[index];
}
flag[index] = true;
}
}
} else if (type.equals("高中")) {
operand_num = rand.nextInt(5) + 1;
operands = new String[operand_num];
for (int i = 0; i < operand_num; i++) {
operands[i] = String.valueOf(rand.nextInt(100) + 1);
}
int special_num = rand.nextInt(operand_num) + 1;
boolean[] flag = new boolean[operand_num];
for (int i = 0; i < special_num; i++) {
//如果操作数位置没有加特殊符号的话
int index = rand.nextInt(operand_num);
//保证第一次有sin,cos,或者tan
if (i == 0) {
int kind = rand.nextInt(3);
if (kind == 0) {
operands[index] = "sin" + operands[index];
} else if (kind == 1) {
operands[index] = "cos" + operands[index];
} else if (kind == 2) {
operands[index] = "tan" + operands[index];
}
flag[index] = true;
} else {
if (!flag[index]) {
//百分之20概率会加²,另外百分之20加√,百分之20概率加sin,百分之20概率加cos,百分之20概率加tan
int kind = rand.nextInt(5);
if (kind == 0) {
operands[index] = operands[index] + "²";
} else if (kind == 1) {
operands[index] = "√" + operands[index];
} else if (kind == 2) {
operands[index] = "sin" + operands[index];
} else if (kind == 3) {
operands[index] = "cos" + operands[index];
} else if (kind == 4) {
operands[index] = "tan" + operands[index];
}
flag[index] = true;
}
}
}
}
//如果操作数大于等于3,百分之50概率随机生成括号
if (operand_num >= 3 && rand.nextBoolean()) {
//括号的长度
int length = rand.nextInt(operand_num - 2) + 1;
int a = rand.nextInt(operand_num - length);
int b = a + length;
operands[a] = "(" + operands[a];
operands[b] = operands[b] + ")";
}
//最后生成表达式
formula += operands[0];
for (int i = 0; i < operand_num - 1; i++) {
formula += signs[rand.nextInt(4)];
formula += operands[i + 1];
}
return formula;
}
//查重,同一个老师不允许出现相同的题目
public static boolean Check_repeat(String name, String math_problem) {
File file_paper = new File(name + " 参考答案");
//获取当前老师文件目录下的所有试卷文件列表
File[] paper_list = file_paper.listFiles();
int len = paper_list.length;
InputStream file_read;
try {
for (int i = 0; i < len; i++) {
file_read = new FileInputStream(paper_list[i]);
byte[] problems = new byte[2048];
//获取当前试卷文件的所有题目
file_read.read(problems);
String content = new String(problems, "utf-8");
//查找是否有相同的字符串
if (content.indexOf(math_problem) != -1) {
return true;
}
file_read.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
//得到答案
public static String getAnswer(String str) {
String extra_str = "";
//将÷变成/
str = str.replace('÷', '/');
//三角函数前缀
str = str.replaceAll("sin", "Math.sin(");
str = str.replaceAll("cos", "Math.cos(");
str = str.replaceAll("tan", "Math.tan(");
//三角函数后缀
//sin
int temp_index = 0;
while (str.indexOf("sin", temp_index) != -1) {
temp_index = str.indexOf("sin", temp_index) + 4;
while (true) {
if (temp_index == str.length() - 1) { //读到最后一个字符,加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index + 1, "*Math.PI/180)");
str = sb.toString();
break;
} else if (str.charAt(temp_index + 1) >= '0' && str.charAt(temp_index + 1) <= '9') { //仍是数字
temp_index++;
continue;
} else { //否则加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index + 1, "*Math.PI/180)");
str = sb.toString();
break;
}
}
}
//cos
temp_index = 0;
while (str.indexOf("cos", temp_index) != -1) {
temp_index = str.indexOf("cos", temp_index) + 4;
while (true) {
if (temp_index == str.length() - 1) { //读到最后一个字符,加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index + 1, "*Math.PI/180)");
str = sb.toString();
break;
} else if (str.charAt(temp_index + 1) >= '0' && str.charAt(temp_index + 1) <= '9') { //仍是数字
temp_index++;
continue;
} else { //否则加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index + 1, "*Math.PI/180)");
str = sb.toString();
break;
}
}
}
//tan
temp_index = 0;
while (str.indexOf("tan", temp_index) != -1) {
temp_index = str.indexOf("tan", temp_index) + 4;
while (true) {
if (temp_index == str.length() - 1) { //读到最后一个字符,加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index + 1, "*Math.PI/180)");
str = sb.toString();
break;
} else if (str.charAt(temp_index + 1) >= '0' && str.charAt(temp_index + 1) <= '9') { //仍是数字
temp_index++;
continue;
} else { //否则加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index + 1, "*Math.PI/180)");
str = sb.toString();
break;
}
}
}
//平方前缀
temp_index = 0;
while (str.indexOf('²') != -1) {
temp_index = str.indexOf('²', temp_index) - 1;
// System.out.println(temp_index+" "+str);
while (true) {
if (temp_index == 0) { //读到第一个字符,加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index, "Math.pow(");
str = sb.toString();
str = str.replaceFirst("²", ",2)");
break;
} else if (str.charAt(temp_index - 1) >= '0' && str.charAt(temp_index - 1) <= '9') { //仍是数字
temp_index--;
continue;
} else { //否则加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index, "Math.pow(");
str = sb.toString();
str = str.replaceFirst("²", ",2)");
break;
}
}
}
//根号后缀
temp_index = 0;
while (str.indexOf('√', temp_index) != -1) {
temp_index = str.indexOf('√', temp_index) + 1;
while (true) {
if (temp_index == str.length() - 1) { //读到最后一个字符,加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index + 1, ")");
str = sb.toString();
break;
} else if (str.charAt(temp_index + 1) >= '0' && str.charAt(temp_index + 1) <= '9') { //仍是数字
temp_index++;
continue;
} else { //否则加后缀
StringBuffer sb = new StringBuffer(str);
sb.insert(temp_index + 1, ")");
str = sb.toString();
break;
}
}
}
//根号前缀
str = str.replaceAll("√", "Math.sqrt(");
//前后括号
StringBuffer sb = new StringBuffer(str);
sb.insert(0, "(");
sb.insert(sb.length(), ").toFixed(2)");
str = sb.toString();
// System.out.println(str);
//进行公式运算
ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
Object answer = null;
try {
answer = jse.eval(str);
} catch (Exception t) {
System.out.println("运算出现错误!");
}
answer += extra_str;
// System.out.println(answer);
return answer.toString();
}
//获得4个选项(正确答案在最前)
public static String[] getSelections(String answer) {
String[] selections = new String[]{answer, null, null, null};
float f_answer = Float.parseFloat(answer);
Random rd = new Random();
NumberFormat formatter = new DecimalFormat("0.00");
for (int i = 1; i < 4; i++) {
float tempf = rd.nextFloat();
boolean tempb = rd.nextBoolean();
float tempwrong = 0;
if (tempb) {
tempwrong = f_answer + tempf;
} else {
tempwrong = f_answer - tempf;
}
selections[i] = formatter.format(tempwrong);
}
return selections;
}
}
四、总结收获
首先总结一下结对编程过程中遇到的问题,梁原韶同学是在调用第三方API部分遇到了困难,而我自己主要遇到困难的部分是一开始对于答案的处理是想完全靠对于字符串的处理来得到答案,但由于自己代码能力一般,在这个方法上花了很长一段时间后最终由于代码过于臃肿而放弃了这个方法,换了方法之后进行得相对算比较的顺利,和结对搭档在项目进度方面也很顺利。
通过这一次的结对编程项目,我也在这个过程中收获了很多,结对的搭档负责用swing库相关组件写界面,我在看搭档写的代码的过程中也对于代码规范和提升代码可读性这方面有了更深的理解,经过这次的结对让我知道了一个项目的顺利进行离不开团队内有效的沟通和互相学习。

浙公网安备 33010602011771号