本篇博客为对结对编程搭档邹同学的个人项目(中小学数学卷子自动生成程序)的评价。该代码使用语言为Java,与我的个人项目语言一致,本次评价旨在总结反省,共同进步。
此次评价主要基于三个方面:
一、代码结构
mathpaper包:
Create类主要用来出题
User类用来对用户类型定义
Begin类包括对输出页面的一系列操作,对用户的输入类型做判断
Database类主要用来对保存并判断用户信息。
调用Main类来运行整个项目
结构较为清晰。在组织类的结构时充分考虑面向对象的特点进行书写。
二、功能完成
登录功能
退出功能
生成试卷功能
切换功能
操作均在操作台实现,我们去看一下生成的文件:
基本功能验证完毕!均已实现。
三、代码分析
1. User类:
未使用到定义的setName、setPassword、setType函数。
2. Database类
逻辑简单,便于理解。经检验,功能正常;
但可复用性不高,当且仅当账户信息不变时使用正常,如需修改账户信息,则代码结构也必须随之发生变化;
3. Create类
(1)createProblem函数
public static String createProblem(String type) throws IOException{ String[] signs={"+","-","*","/","²","√","sin","cos","tan"}; Random random=new Random(); String prob=""; //题目,目前还是空的 int opNumber=random.nextInt(5)+1; //操作数数量1~5 if(type.equals("小学")) { opNumber= random.nextInt(4)+2; //小学2~5 } int[] operand1=new int[opNumber]; String[] oprand=new String[opNumber]; int[] flag=new int[opNumber]; //标记是否成为了特殊数 for(int i=0;i<opNumber;i++){ //给数赋值 operand1[i]=random.nextInt(100)+1; //取值范围1~100 oprand[i]=String.valueOf(operand1[i]); //将数变为字符串类型 flag[i]=0; //如果这个数字有改动,会被标记 } if(type.equals("小学")){ String[] opsign=new String[opNumber-1]; //小学题目的符号比数字少一个 int[] index=new int[opNumber-1]; for(int i=0;i<opNumber-1;i++) { index[i]=random.nextInt(4); opsign[i]=signs[index[i]]; } prob=Add(opNumber,opsign,oprand); } else if(type.equals("初中")){ int a= random.nextInt(opNumber)+1; //有a个被平方或开根号了 //处理a个特殊的数 for(int i=0;i<a;i++) { int signIndex= random.nextInt(2)+4; //选择操作符下表4或5,平方或开根号 int changeIndex=0; //改变的数字下标 while(true){ changeIndex=random.nextInt(opNumber); //选择要改变的数字的下标 if(flag[changeIndex]==0) { flag[changeIndex]++; break; } } if(signIndex==4){ //平方 oprand[changeIndex]=oprand[changeIndex]+"²"; } else if(signIndex==5){ //开根号 oprand[changeIndex]="√"+oprand[changeIndex]; } } //加上+-*/运算符 String[] opsign=new String[opNumber-1]; //初中题目的符号比数字少一个 int[] index=new int[opNumber-1]; for(int i=0;i<opNumber-1;i++) { index[i]=random.nextInt(4); opsign[i]=signs[index[i]]; } prob=Add(opNumber,opsign,oprand); } else if(type.equals("高中")){ //处理b个sin,cos,tan int b=random.nextInt(opNumber)+1; //有b个数是有sin,cos,tan,至少有一个 for(int i=0;i<b;i++) { int signIndex = random.nextInt(3) + 6; //选择操作符下表6或7或8 int changeIndex = 0; //改变的数字下标 while (true) { changeIndex = random.nextInt(opNumber); //选择要改变的数字的下标 if (flag[changeIndex] == 0) { flag[changeIndex]++; break; } } oprand[changeIndex] = signs[signIndex] + oprand[changeIndex]; } //如果还有的数未被sin,cos,tan标记,可以加上平方或开根号 if(b<opNumber) { int a = random.nextInt(opNumber - b); for (int i = 0; i < a; i++) { int signIndex2 = random.nextInt(2) + 4; //选择操作符下表4或5,平方或开根号 int changeIndex2 = 0; //改变的数字下标 while (true) { changeIndex2 = random.nextInt(opNumber); //选择要改变的数字的下标 if (flag[changeIndex2] == 0) { flag[changeIndex2]++; break; } } if (signIndex2 == 4) { //平方 oprand[changeIndex2] = oprand[changeIndex2] + "²"; } else if (signIndex2 == 5) { //开根号 oprand[changeIndex2] = "√" + oprand[changeIndex2]; } } } //加上+-*/运算符 String[] opsign=new String[opNumber-1]; //初中题目的符号比数字少一个 int[] index=new int[opNumber-1]; for(int i=0;i<opNumber-1;i++) { index[i]=random.nextInt(4); opsign[i]=signs[index[i]]; } prob=Add(opNumber,opsign,oprand); } return prob; }
程序逻辑是小学、初中、高中各自用一个函数去写,里面不但复用的代码很多,而且如果有别的运算符比如大学难度,也要重新写一个大学的......扩展与修改难度大,其实仔细阅读后小学、初中、 高中,三者区别不大,一个判断语句足以......函数的作用更多应该是去实现一个独立的功能,而不是将程序部分代码变成一个函数。
(2)Add函数
public static String Add(int opNumber,String[] opsign,String[] oprand){ String prob=""; Random random=new Random(); if(random.nextBoolean()&&opNumber>2){ //加括号 //Left int leftBracket= random.nextInt(opNumber-1); //Right int rightBracket=random.nextInt(opNumber-leftBracket-1)+leftBracket+1; if(leftBracket==0&&rightBracket==opNumber-1){ rightBracket--; } for(int i=0;i<opNumber-1;i++){ if(i==leftBracket){ prob=prob+"("+oprand[i]+opsign[i]; } else if(i==rightBracket) { prob=prob+oprand[i]+")"+opsign[i]; } else { prob=prob+oprand[i]+opsign[i]; } } if(rightBracket==opNumber-1){ prob=prob+oprand[opNumber-1]+")"; } else{ prob=prob+oprand[opNumber-1]; } } else{ //不加括号 for(int i=0;i<opNumber-1;i++){ prob=prob+oprand[i]+opsign[i]; } prob=prob+oprand[opNumber-1]; } return prob; }
将括号设置这一函数单独设置,增加代码的复用性,让结构更加明朗
在处理表达式时可以增加一些功能:除号右边不能为0,括号可不可以设置不止一对。
(3)checkRepeat函数
public static boolean checkRepeat(String name,String problem) throws IOException{ File filePaper=new File("math_problems\\"+name); File[] paperList=filePaper.listFiles(); //获取指定目录下的所有文件或者文件目录的File对象数组 int len=paperList.length; InputStream fileRead; //字节输入流,将文件中的数据读取到java中 for(int i=0;i<len;i++) { fileRead=new FileInputStream(paperList[i]); byte[] problems=new byte[2048]; fileRead.read(problems); //获取当前试卷文件的所有题目,输入流会把读取到的内容放入到这个字节数组中 String content=new String(problems,"utf-8"); if(content.indexOf(problem)!=-1) { //看看字符串content中有无problem这个字符串,没有返回-1 return true; } fileRead.close(); } return false; }
查重算法简单易懂。能有效实现查重
查重算法采用直接循环遍历,在题目大量出现的情况下,查重效率低
(4)获取文件路径
题目要求将对应的试题存在账号的文件夹内,并且对于相应的地址位置,大部分同学都是单纯的提前设置好在自己电脑的文件夹,若是在别人的电脑就得重新修改代码,而队友则是采用了getAbsolutePath()获取绝对路径的方式,动态的去创建文件,灵活高效。
四、代码规范
代码规范比较好,缩进,空格,包括在文件前面的包头声明都是非常明确且清晰的,可读性高,并且类、类函数等都做了充分的注解,让我看着并不会很吃力,命名比较规范,能够清楚得知道每个变量和函数具体在起什么作用。
总结:
邹同学的代码有很多值得我学习的地方,以上列出的优点只是可以看出来的结构,其中蕴含的思想需要我之后多多学习。在此次个人项目中再一次熟悉了Java语言,对之后的项目开始可能大有裨益。