《小学四则运算练习软件》结对项目报告

github项目地址 : FundamentalOperations

演示地址小学生四则运算在线测试  

  测试账号:admin 、admin1、admin2、admin3、admin4

  密码:111

  注:由于服务器环境原因,请忽略验证码,直接登陆

 结对同伴的园子:fateiceb

学号:201571030141      同伴学号:201571030138

实验要求

软件基本功能要求如下:
  •  在《实验二 软件工程个人项目》中,同学们实现了一个命令行四则运算出题小程序,本次实验采用结对编程方式,设计开发一个小学生四则运算练习软件,使之具有以下功能:

  • (1)由计算机从题库文件中随机选择20道加减乘除混合算式,用户输入算式答案,程序检查答案是否正确,每道题正确计5分,错误不计分,20道题测试结束后给出测试总分;

  • (2)题库文件可采用实验二的方式自动生成,也可以手工编辑生成,文本格式如下:

  • (3)程序为用户提供三种进阶四则运算练习功能选择:百以内整数算式(必做)、带括号算式、真分数算式练习;

  • (4)程序允许用户进行多轮测试,提供用户多轮测试分数柱状图,示例如下:

           

  • (5)程序记录用户答题结果,当程序退出再启动的时候,可为用户显示最后一次测试的结果,并询问用户可否进行新一轮的测试;

  • (6)测试有计时功能,测试时动态显示用户开始答题后的消耗时间。

  • (7)程序人机交互界面是GUI界面(WEB页面、APP页面都可),界面支持中文简体(必做)/中文繁体/英语,用户可以进行语种选择。

1、需求分析

  该项目要求程序有人机交互界面,且可评判成绩。可为用户提供百以内整数算式、带括号算式、真分数算式三种练习,可为进行多轮测试并记录成绩生成柱状图并且记录用户消耗的时间。

2、功能分析

  • 可实现基本要求,即可生成数字在 0 和 100 之间,运算符在3个到5个之间的计算表达式,并计算出表达式的结果
  • 随机可生成带括号的运算式,括号不会超过运算符的个数
  • 生成真分数的四则运算
  • 有用户界面,可进行多轮测试
  • 记录测试结果,并将以往成绩以柱状图展示
  • 测试时,记录用户的测试用时

3、设计实现

4、运行测试

  • 运行生成第一类算式 ,即百以内整数算式,以后导入数据库方便,后面标记1  

  

  • 运行生成第二类算式 ,即带括号算式 

  

  • 运行生成第三类算式 ,即真分数算式

  

  • 设计交互界面,首页登陆

 

 

 

  

登陆成功后,跳转主页面,进行选择不同类型的题目,同时下方显示该用户以往测试结果的柱状图

  • 选择试题类型后,进行答题,并计时

  

  • 答题结束后,提交并获取分数,也可查看错误答案

  

  

 

5、核心代码

  • 对于页面跳转请求,进行逻辑处理
     1     @RequestMapping(value = "/Login")
     2     public String login(){
     3         return "./views/login";
     4     }
     5     @RequestMapping(value = "/index")
     6     public String home(){
     7         return "./views/index";
     8     }
     9     @RequestMapping(value = "/admin/index")
    10     public String examIndex(){
    11         return "./views/_exam/index";
    12     }
  • 对于页面请求用户信息,进行获取并处理
     1  @RequestMapping(value = "/user")
     2     public List<UserEntity> getUsers(){
     3         return userRepository.findAll();
     4     }
     5     @RequestMapping(value = "/LoginCheck",method = RequestMethod.POST)
     6     public String loginCheck(@Param("username") String username, @Param("password") String password,
     7                              HttpServletRequest request){
     8         UserEntity userEntity = userRepository.findByUsername(username);
     9         logger.info(username+" "+password);
    10         if (userEntity != null&&userEntity.getPassword().equals(password)){ ;
    11         logger.info("sucess");
    12             request.getSession().setAttribute("user",userEntity);
    13             return "2";
    14         }
    15         logger.info("fail");
    16         return "1";
    17     }
    18     @RequestMapping("/submit")
    19     @Modifying
    20     public String submit(@Param(value = "score") String score,HttpServletRequest request){
    21         HttpSession session  = request.getSession();
    22         UserEntity userEntity = (UserEntity) session.getAttribute("user");
    23         VisualizationEntity visualizationEntity = new VisualizationEntity();
    24         visualizationEntity.setUsername(userEntity.getUsername());
    25         visualizationEntity.setScore(score);
    26         visualiRepository.save(visualizationEntity);
    27         return "success";
    28     }
    29     @RequestMapping("/chart")
    30     public List<VisualizationEntity> getChartData(HttpServletRequest httpServletRequest){
    31         HttpSession session = httpServletRequest.getSession();
    32         UserEntity userEntity = (UserEntity) session.getAttribute("user");
    33         List<VisualizationEntity> visualizationEntities = visualiRepository.findByUsername(userEntity.getUsername());
    34         return visualizationEntities;
    35     }
  • 通过逆波兰式生成运算式,并计算结果,借住栈的使用,判断添加括号
     1 //计算逆波兰式的结果
     2     private static boolean evoe(ArrayList<String> strArr){
     3         String str = ele.getOperations();
     4         boolean flag = true;//判断过程中是否有负数或小数点
     5         int temp = 0;//存放临时计算结果
     6         Stack<String> stack = new Stack<String>();
     7         for(String s : strArr){
     8             if(!str.contains(s)){//如果是数字,放入栈中
     9                 stack.push(s);
    10             }else{
    11                 int a = Integer.valueOf(stack.pop());
    12                 int b = Integer.valueOf(stack.pop());
    13                 try {
    14                     switch(s){
    15                         case "+" :
    16                             stack.push(String.valueOf(a+b));
    17                             break;
    18                         case "-" :
    19                             temp = b-a;
    20                             if(temp < 0) flag=false;
    21                             stack.push(String.valueOf(temp));
    22                             break ;
    23                         case "*" :
    24                             stack.push(String.valueOf(a*b));
    25                             break;
    26                         case "/" :
    27                             if(a == 0) {a = 1;flag = false;}
    28                             temp = b/a;
    29                             if(a*temp != b) flag = false;
    30                             stack.push(String.valueOf(temp));
    31                             break ;
    32                     }
    33                 } catch (Exception e) { }
    34             }
    35         }
    36         ele.setResult(Integer.parseInt(stack.pop()));
    37         return flag;
    38     }
    39     //将逆波兰式转化为运算式
    40     private static void pro_exp(ArrayList<String> strArr){
    41         String str = ele.getOperations();
    42         String ea,eb;
    43         String fh = " ";//临时的符号
    44         boolean lastisnum = false;//记录临时符号后的一位是否为数字
    45         Stack<String> expstack = new Stack<String>();
    46         for(String s : strArr){
    47             if(!str.contains(s)){//如果是数字,放入栈中
    48                 expstack.push(s);
    49                 lastisnum = true;
    50             }else{
    51                 ea = expstack.pop();
    52                 eb = expstack.pop();
    53                 switch(s){
    54                     case "+" :
    55                         expstack.push(eb+"+"+ea);
    56                         break;
    57                     case "-" :
    58                         expstack.push(eb+"-"+ea);
    59                         break ;
    60                     case "*" :
    61                         if("+-".contains(fh)){
    62                             if(!lastisnum) ea = "("+ea+")";
    63                             else eb = "("+eb+")";
    64                         }
    65                         expstack.push( eb+"*"+ea);
    66                         break;
    67                     case "/" :
    68                         if("+-".contains(fh)){
    69                             if(!lastisnum) ea = "("+ea+")";
    70                             else eb = "("+eb+")";
    71                         }
    72                         expstack.push( eb+"/"+ea);
    73                         break ;
    74                 }
    75                 fh = s;
    76                 lastisnum = false;
    77             }
    78         }
    79         ele.setExp(expstack.pop());
    80     }
    81     //随机生成逆波兰式
    82     public static void test(){
    83         ArrayList<String> strArr=new ArrayList<String>();
    84         List list = Collections.synchronizedList(strArr);
    85         N = new Random().nextInt(4)+2;
    86         synchronized(list) {
    87             strArr.clear();
    88             for (int i = 0; i < N; i++) {
    89                 strArr.add(ele.random_N());
    90             }
    91             for (int i = 0; i < N - 2; i++) {
    92                 strArr.add(new Random().nextInt(strArr.size() - N + 1) + N - 1, ele.random_E());
    93             }
    94             strArr.add(strArr.size(), ele.random_E());
    95         }
    96         ele.setStrArr(strArr);
    97     }
  • 生成分数的核心代码,通过对分子分母判断比较,进行加减乘除
     1  public static ProperFraction calProperFraction(ProperFraction a,
     2                                                    ProperFraction b, Symbol symbol){
     3         ProperFraction properFraction = null;
     4         //计算分数加减乘除
     5         switch (symbol.getValue()){
     6             case "+":properFraction = fracAdd(a.numa,a.numb,b.numa,b.numb);break;
     7             case "-":properFraction = fracSub(a.numa,a.numb,b.numa,b.numb);break;
     8             case "*":properFraction = fracMul(a.numa,a.numb,b.numa,b.numb);break;
     9             case "/":properFraction = fractDiv(a.numa,a.numb,b.numa,b.numb);break;
    10         }
    11         return properFraction;
    12     }
    13     static ProperFraction fracAdd(int first_numerator,int first_denominator,int second_numrator,int second_denominator){
    14         int denominator;
    15         int numerator;
    16         if(first_denominator==second_denominator)  //分母相同时加分子
    17         {
    18             denominator=first_denominator;
    19             numerator=first_numerator+second_numrator;
    20         }
    21         else  //否则同分比较分子
    22         {
    23             denominator=first_denominator*second_denominator;
    24 numerator=first_numerator*second_denominator+first_denominator*second_numrator;
    25         }
    26         int gcd = gcd(numerator,denominator);
    27         denominator = denominator / gcd;
    28         numerator = numerator / gcd;
    29         ProperFraction properFraction = new ProperFraction();
    30         properFraction.numa = numerator;
    31         properFraction.numb = denominator;
    32         return properFraction;
    33     }
    34     static ProperFraction fracSub(int first_numerator,int first_denominator,int second_numrator,int second_denominator){
    35         int denominator;
    36         int numerator;
    37         if(first_denominator==second_denominator)  //分母相同时加分子
    38         {
    39             denominator=first_denominator;
    40             numerator=first_numerator-second_numrator;
    41         }
    42         else  //否则同分比较分子
    43         {
    44             denominator=first_denominator*second_denominator;
    45             numerator=first_numerator*second_denominator-first_denominator*second_numrator;
    46         }
    47         int gcd = gcd(numerator,denominator);
    48         denominator = denominator / gcd;
    49         numerator = numerator / gcd;
    50         ProperFraction properFraction = new ProperFraction();
    51         properFraction.numa = numerator;
    52         properFraction.numb = denominator;
    53         return properFraction;
    54     }
    55     static ProperFraction fracMul(int first_numerator,int first_denominator,int second_numerator,int second_denominator){
    56         int denominator;
    57         int numerator;
    58         denominator=first_denominator*second_denominator;
    59         numerator=first_numerator*second_numerator;
    60         int gcd = gcd(numerator,denominator);
    61         denominator = denominator / gcd;
    62         numerator = numerator / gcd;
    63         ProperFraction properFraction = new ProperFraction();
    64         properFraction.numa = numerator;
    65         properFraction.numb = denominator;
    66         return properFraction;
    67     }
    68     static ProperFraction fractDiv(int first_numerator,int first_denominator,int second_numerator,int second_denominator){
    69         int denominator;
    70         int numerator;
    71         numerator = first_numerator*second_denominator;
    72         denominator = first_denominator*second_numerator;
    73         int gcd = gcd(numerator,denominator);
    74         denominator = denominator / gcd;
    75         numerator = numerator / gcd;
    76         ProperFraction properFraction = new ProperFraction();
    77         properFraction.numa = numerator;
    78         properFraction.numb = denominator;
    79         return properFraction;
    80     }

6、总结

  使程序模块化,依照web项目分为config层、controller层、domain层、以及repository层。config层负责配置web环境,controller层负责控制页面跳转以及用户数据获取,domain层负责依照数据库建立实体类,repository层负责建立与数据库中表对应的接口,并进行操作查询数据库。

7、结对编程(非摆拍照片)

8、展示PSP

PSP2.1

任务内容

计划完成需要的时间(min)

实际完成需要的时间(min)

Planning

计划

30

30

 Estimate

估计这个任务需要多少时间,并规划大致工作步骤

30

30

Development

开发

360

300

Analysis

需求分析 (包括学习新技术)

100

60

 Design Spec

生成设计文档

10

10

Design Review

设计复审 (和同事审核设计文档)

20

10

Coding Standard

代码规范 (为目前的开发制定合适的规范)

10

5

Design

具体设计

60

35

Coding

具体编码

120

130

Code Review

代码复审

10

10

est

测试(自我测试,修改代码,提交修改)

30

40

Reporting

报告

30

43

Test Report

测试报告

25

30

Size Measurement

计算工作量

2

3

Postmortem & Process

Improvement Plan

事后总结 ,并提出过程改进计划

3

10

9、对小伙伴的评价

  讲真,本来是不打算写这部分内容的(因为我根本就没看到这部分的实验要求),看到舍友,我的partner对我一波吹捧后,我不得不“下手”。无容置疑,我的partner是实力派选手,优秀的已经无法用“优秀”二字概括,编程能力强,参与过大项目,精通各种web框架。此次项目对于他来说是小case,但对我来说确实是不可多得的机会,让我学习到很多技术。跟他结对编程,真可谓是享受代码的美妙。他带着我搭建了web,给我教会了很多新的技术,再此,鞠躬致谢!总之,优秀的他,用自己的行动诠释这一个程序员该有的各种能力及素质,他是我大学追求的目标,我将努力向我的partner看齐!(哈哈哈,partner,对我的实力“吹捧”还满意吗?)

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

  这个问题的答案是肯定的,如今发展的现状与趋势已经证明了这一点。我认为1+1>2并不是说工作量或者是代码行而应该是效率、行动力以及项目的完成度。正如这次的项目,一个我可能会完成,但需要走很多弯路,并不会选择出这样方便快捷的方式,可能最后的结果也是不尽人意,就只能实现一个简单的功能,而有了partner的合作,不但项目完成的快,而且自认为完成的质量也不错,不仅有不错的UI,项目的功能也很有特色。总之,21世纪的特点就是合作,共赢,尤其是计算机的发展,更需要具备这样的特点,我们更需要具备这样的素质。

项目总结

  起初,看到项目需求觉得难度很大,但在同伴的努力下,运用现有的web技术将难度降低,至少在web这块的搭建节省了好多时间。尽管此次需求加了用户界面,但通过整个项目的完成,感受到,最核心的还是运算式的生成,在数据处理的这块花了不少的时间。此次结对编程,使我收获颇多,我通过同伴学习到了新的技术,当然,这种结对编程也使得完成项目的效率高了许多。希望以后的实践中可以有更多的伙伴一起努力、一起收获、一起进步。

posted @ 2018-04-02 19:00  城尘  阅读(304)  评论(4编辑  收藏  举报