《小学四则运算练习软件》结对项目报告
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、需求分析
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这块的搭建节省了好多时间。尽管此次需求加了用户界面,但通过整个项目的完成,感受到,最核心的还是运算式的生成,在数据处理的这块花了不少的时间。此次结对编程,使我收获颇多,我通过同伴学习到了新的技术,当然,这种结对编程也使得完成项目的效率高了许多。希望以后的实践中可以有更多的伙伴一起努力、一起收获、一起进步。