Github项目地址:

https://github.com/dushik/AthmeticPlus

一、题目要求

使用JAVA编程语言,采用结对编程方式实现四则运算出题小程序,软件基本功能要求如下:

  • 由计算机从题库文件中随机选择20道加减乘除混合算式,用户输入算式答案,程序检查答案是否正确,每道题正确计5分,错误不计分,20道题测试结束后给出测试总分
  • 题库文件可采用实验二的方式自动生成,也可以手工编辑生成
  • 程序为用户提供三种进阶四则运算练习功能选择:百以内整数算式(必做)、带括号算式、真分数算式练习
  • 程序允许用户进行多轮测试,提供用户多轮测试分数柱状图
  • 程序记录用户答题结果,当程序退出再启动的时候,可为用户显示最后一次测试的结果,并询问用户可否进行新一轮的测试
  • 测试有计时功能,测试时动态显示用户开始答题后的消耗时间
  • 程序人机交互界面是GUI界面(WEB页面、APP页面都可),界面支持中文简体(必做)/中文繁体/英语,用户可以进行语种选择

    二、需求分析:

    • 程序人机交互界面是GUI界面,界面支持中文简体
    • 程序能够随机从题库选择试题,由用户输入答案,自动检查对错,统计成绩
    • 题库文件可基于实验二生成,也可自定义题库文件
    • 用户可以选择三种四则运算模式:百以内整数算式(结果非负,必须整数),带括号运算,真分数算式练习
    • 系统允许用户进行多轮测试,并实现用户测试测试分数的数据可视化(柱状图)
    • 测试时提供计时功能

    三、功能设计:

    • 采用java Swing编程实现人机交互界面的设计
    • 编写运算式生成类
    • 编写运算式计算类
    • 编写真分数运算类
    • 编写文件操作读写类
    • 编写数据可视化实现类

      四、设计实现:

      类图:

      • 运算式的生成基于实验二,改进完成带括号的运算,真分数运算。使得主程序通过参数的不同来调用不同的方法生成不同的题库
      • 成绩的存储采用文件的写操作,将每次测试结果以追加的方式写入txt文件,保证不覆盖以前的成绩
      • 柱状图的绘制采用jFreeChart图表绘制类库实现,查看成绩时通过读取保存成绩txt文件的最后五行实现最近五次成绩的柱状显示
      • 文件操作类的功能应包括,按行写入文件,按行通过追加的方式写入文件,按行读取文件,按行读取文件的最后四行内容
      • 普通运算式计算类的功能包括,中缀表达式转后缀,计算后缀表达式
      • 分数运算式计算类的功能包括,分数的通分,分数的加操作,分数的减操作,分数的乘操作,分数的除操作

        四、运行结果

         

      • 六、核心代码:

        随机产生num个真分数运算式,计算结果,使得结果非负,并存入题库文件

        
        
         1 /**
         2          * 生成具有真分数运算式数的非负运算式
         3          * @param num
         4          */
         5         public void generateExpressionC(int num){
         6             char[] operator=new char[]{'+','-'};
         7             ArrayList<String> expression=new ArrayList<String>();
         8             for(int i=0;i<num;i++){
         9                 int n=random.nextInt(3)+2; //3-5个运算符
        10                 ProperFraction[] proList=new ProperFraction[n+1];
        11                 for(int j=0;j<=n;j++){
        12                     proList[j]=new ProperFraction();
        13                 }
        14                 char[] symbol=new char[n];
        15                 String ex=new String();
        16                 for(int j=0;j<n;j++){
        17                     char sm=operator[random.nextInt(2)];
        18                     ex+=proList[j].toString()+sm;
        19                     proList[0]=proper.calProperFraction(proList[j], proList[j+1],sm);
        20                 }
        21                 ex+=proList[n]+"="+proList[0].toString();
        22                 if(proList[0].getNuma()/proList[0].getNumb()<0){
        23                     i--;
        24                 }
        25                 else{
        26                     expression.add(ex);
        27                 }
        28             }
        29             doFile.WriteToFile("ArithmeticExpression.txt",expression);          
        30         }

         

        读取文件最后N行

         1 public ArrayList<String> readLastNLine(String path, long numRead)  
         2     {  
         3         File file=new File(path);
         4         ArrayList<String> result = new ArrayList<String>();  
         5         long count = 0;  
         6         if (!file.exists() || file.isDirectory() || !file.canRead()){  
         7             return null;  
         8         }  
         9         RandomAccessFile fileRead = null;  
        10         try{  
        11             fileRead = new RandomAccessFile(file, "r");  
        12             long length = fileRead.length();  
        13             if (length == 0L){  
        14                 return result;  
        15             }  
        16             else{  
        17                 long pos = length - 1;  
        18                 while (pos > 0){  
        19                     pos--;  
        20                     fileRead.seek(pos);  
        21                     if (fileRead.readByte() == '\n'){    
        22                         String line = fileRead.readLine();
        23                         result.add(line);   
        24                         count++;  
        25                         if (count == numRead){  
        26                             break;  
        27                         }  
        28                     }  
        29                 }  
        30                 if (pos == 0){  
        31                     fileRead.seek(0);  
        32                     result.add(fileRead.readLine());  
        33                 }  
        34             }  
        35         }  
        36         catch (IOException e){  
        37             e.printStackTrace();  
        38         }  
        39         finally{  
        40             if (fileRead != null){  
        41                 try{  
        42                     fileRead.close();  
        43                 }  
        44                 catch (Exception e){  
        45                 }  
        46             }  
        47         }  
        48         return result;  
        49     }  

        通过标准答案和测试结果,计算正确的题数

         1  /**
         2      * 通过标准答案和测试结果,计算正确的题数
         3      * @param resHm
         4      * @param ansHm
         5      * @return
         6      */
         7     private static int critical(HashMap<Integer, String> resHm,HashMap<Integer, String> ansHm){
         8         int count=0;
         9         for(Map.Entry<Integer, String> resentry:resHm.entrySet()){
        10             String resvalue = resentry.getValue() == null?"":resentry.getValue();
        11             String ansvalue = ansHm.get(resentry.getKey())==null?"":ansHm.get(resentry.getKey());
        12             if(resvalue.equals(ansvalue)){
        13                 count++;
        14             }
        15         }
        16         return count;
        17     }

        七、总结:

               本次实验说起来是实验二的拓展项目,但是难度较实验二又大了很多,举例来说:图形用户界面设计,过程很复杂,需要耗费很多心力、程序人机交互界面是GUI界面是难点、实现用户测试测试分数的数据可视化(柱状图)之前没有接触过,需要进行行的了解和学习等等。最重要的问题依然还是JAVA编程能力太差,在短期内查漏补缺但是离要求还相差甚远。还是要继续努力。

        八、PSP:

        PSP任务内容计划完成需要的时间(min)实际完成需要的时间(min)
          计划 50 50
        Estimate 需求分析,函数实现 50 50
          开发 100 100
        Analysis 需求分析 5 10
        Design Spec 设计 30 30
        Design Review 设计复审 30 30
        Design 具体设计 30 30
        Coding 具体编码 30 30
        Code Review 代码复审,查找语法错误 2 2
        Test 测试 5 5
          报告 30 30
        Test Report 经测试,程序符合实验要求,但严重脱离实际应用 20 20
        Size Measurement 主要工作量在GUI编程 20 20
           

        九、对小伙伴的评价

      • 这里手动@杜总,非常感谢杜世康同学在本次合作中非常耐心、不遗余力的帮助我这个编程水平和他相差甚远的学渣。他是个很爽快也很幽默的人,合作很愉快,对我来说合作的过程就是一个学习的过程,正所谓“三人行必有我师”。结对编程可以促进参与项目双方自身的提高,结对工作的时候,水平较低的一方会潜移默化地受水平略高的程序员影响,比如我在本次结对编程中就学到了很多新的并且实用的东西,这种学习比起从网站上找教学视频和自己抱着各种参考书啃是不一样的,更容易理解并且接受,而且更容易上手。对于小伙伴来说,他是水平高的一方,虽然带上我这个“菜鸟”玩家很吃力,但是也同样因为不断地把自己的想法说出来而整理了自己的思路,不至于在很大的工作量中把思路搞混。
      • 把实验结果做成GIF动图这个想法就是杜世康同学提出的,而且我们做的用户界面和大部分同学做的也不太一样。他是个很有想法的人,而且这些想法大部分都是精华,能够很高效地完成任务,并且能够把我们想展示的东西很直观的展示出来。这个也是我很佩服他的一点。
      • 合作过程中我也深刻的体会到了——有效的交流的重要性。因为我们不是随时都有时间能凑到一起的,所以除了当面互相交流想法之外,QQ/微信这些社交软件也起到了很大的作用,让我们彼此的交流更便捷更高效。

                          十、结对编程真的能够带来1+1>2的效果吗?通过这次结对编程,请谈谈你的感受和体会

                                 我觉得结对编程确实能够带来1+1>2的效果,但是对于合作双方来说,这个效果肯定会不尽相同。首先合作双方肯定得是能互相合得来并且性格和工作能力可以互补的人,这样两个志趣相投的人在一起敲代码做项目,要比一个人闷头写程序改bug要有趣的多,工作效率也相对来说会高一点;其次对于编程能力相对较弱的我来说,1+1>2的效果最大的体现就是我能从对方身上学到很多我之前运用不熟练或者根本不知道的新知识,这对我之后的学习起到了很积极的作用,也在很大程度上减轻了我的精神负担——如果给我一周半时间让我一个人做这个项目的话我肯定会不知所措。最后我想说这种编程方式也并不是百利而无一害的,比如结对编程对于目前的我们来说,每天还有很多的课程,并没有充分的条件能够完全抽出一两天整个的时间来结对编程,而且合作双方也需要一定的磨合时间,还有编程过程中可能会出现意见上的小冲突,这些都会造成完成作业的时间线拉长。不过大家也乐在其中,我始终相信合作才能共赢。