本次项目过程中,是我对javaweb有了更加深入的了解,以前总是自己看视频并没有自己组队尝试的机会,也没有很好的实践能力。这次结对项目使我对模块化的重要性以及效能分析有了深刻的认识,对我以后的学习有很大的帮助。在此十分感谢我的队友马玲,帮助我们上传到服务器上的马福孝同学,各位学长学姐以及我们的兄弟小组陈芳、柯招坤小组。
一、项目
源代码地址:https://git.coding.net/mal123/arithmetic.git
测试url:localhost:8080/arithmetic?state=toHome
网页版测试地址:http://140.143.62.245:8080/mal_war/arithmetic?state=toHome
结对成员: 马玲 2016012054 博客地址:http://www.cnblogs.com/malings/p/8751840.html
王雪 2016012013
二、PSP展示
PSP | 任务内容 | 计划时间(min) |
Planning | 计划 | 60 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 30 |
Development | 开发 | 1200 |
Test | 测试 | 90 |
Algorithm Optimization | 算法优化 | 180 |
Interface Design | 接口设计 | 150 |
Coding Standard | 代码规范 | 240 |
Design | 具体设计 | 120 |
Coding | 具体编码 | 450 |
Code Review | 代码复审 | 90 |
Test | 测试 | 60 |
Reporting | 报告 | 180 |
Test Report | 测试报告 | 120 |
Size Measurement | 计算工作量 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 120 |
三、 看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的。
1)关于Information Hiding:信息隐藏
对于面向对象的程序设计而言,信息隐藏是一种重要的软件开发手段,它与对象的封装与模块化相关。信息隐藏是开发整体程序结构时使用的法则,将每个程序的隐蔽或封装在一个单一的设计模块中,定义每一个模块时尽可能少地显露其内部的处理。把信息隐蔽用作模块化系统的一个设计标准,在后面项目调试过程中,编程人员的任务量能够得以减轻,提高代码可读性。
本次结对项目,我们将生成题目和计算两个模块设计成接口,用户只需在页面输入自己所想要的参数的值,点击提交,后台就会调用生成题目这个接口,将生成的题目输出出来。计算接口也是同样的调用,就会将结果与输入框中的结果进行核对正确性。在后期项目优化过程中,我们只需要修改接口中的方法来实现功能,不必修改其他地方,极大程度上减轻了我们的任务量。
2)Interface Design:接口设计
设计接口的时候,我们也有讨论接口该如何设计会更加合理。接口在设计的时候应当尽量的详细划分,使得每一个接口中方法应该尽量少,将功能实现在接口中,所以我们决定讲生成题目与计算功能分开,分成两个接口。在层与层之间调用接口,类之间通过接口访问。
3)Loose Coupling:降低耦合
模块与模块之间总会存在一些“联系”,这就会提升我们维护过程中的复杂度和任务量。松耦合通过接口的方式实现各个模块之间的调用,可以使模块在发生改变时,其他的部分可以保持不变,减少模块间的相互“联系”,降低耦合。
四、计算模块接口的设计与实现过程
我们将生成题目和计算设计成了两个接口。
生成题目接口:
方法间的关系:
计算接口:
五、计算模块接口部分的性能改进
我们用JProFiler对内存、ALL OBJECTS、CPU等方面进行了分析。以下是我们效能分析:
项目总体分析图:
CPU分析图:
性能分析:
六、
import org.junit.Test; import static org.junit.Assert.*; public class CommandTest { @Test public void main() throws Exception { Command command = new Command(); String[] args1 = {"-m","3","800","-n","7"};//正确的参数类型(默认模式) String[] args2 = {"-m","50","400","-n","100000"};//参数范围不合法 String[] args3 = {"-m","3","800","-n","7","-o","3","-b","-c"};//正确的参数范围(不默认) String[] args4 = {"-m","3","800","-n","7","-o","你","-b","-c"};//参数类型错误 String[] args5 = {"-m","L","800","-n","7"};//参数类型错误 String[] args6 = {"-m","3","800","-n","、"};//参数类型错误 String[] args7 = {"-m","3","800","-n","7","M"};//输入参数不合法 String[] args8 = {"-m","3","800"};//缺少参数 Command.main(args1); Command.main(args2); Command.main(args3); Command.main(args4); Command.main(args5); Command.main(args6); Command.main(args7); Command.main(args8); } }
代码覆盖率如下:
接口类测试覆盖率:
Command类的测试覆盖率:
在单元测试中,每个类对应一个测试类,测试类中每个测试方法对应一个方法。构造测试数据时,根据方法中每个分支对应的情况,设置相应的参数,确保测试会进入到每一个分支,达到测试的目的。
七、计算模块部分异常处理说明
@Test
public void createCharacter() throws Exception {
System.out.println(createArithmetic.CreateCharacter(56,true));
}
当上下界不在合理范围时:
@Test
public void createNumber() throws Exception {
System.out.println(createArithmetic.CreateNumber(4,-9,800));
}
当上下界范围太小时:
@Test
public void createFile() throws Exception {
System.out.println(createArithmetic.createFile(-9,true,true,10,1000,4));
}
当题目数量不在合理范围时:
@Test
public void createFile() throws Exception {
System.out.println(createArithmetic.createFile(-9,true,true,10,1000,4));
}
八、界面模块的详细设计过程
界面模块前端设计采用jsp和js结合完成,通过servlet完成前后端的交互。
根据程序所要实现的功能,我们将设计四个页面用来展示并实现这些功能:
1)home.jsp:四则运算的主页面,有生成题目和上传题目两个提交按钮
2)create_arithmetic.jsp:生成题目的页面(1)通过form表单来获取用户输入的参数并生成相应题目,
(2)可让用户下载生成的题目
(3)利用JavaScript对输入框进行判空
<script language="javascript" type="text/javascript"> function checkform(){ if(document.getElementById('ipt1').value.length==0){ alert('输入为空!'); document.getElementById('ipt1').focus(); return false; } if(document.getElementById('ipt2').value.length==0){ alert('输入为空!'); document.getElementById('ipt2').focus(); return false; } if(document.getElementById('ipt3').value.length==0){ alert('输入为空!'); document.getElementById('ipt3').focus(); return false; } } </script>
3)upload _arithmetic.jsp:上传文件的页面(1)用户可选择文件路径进行文件上传
(2)用JS进行输入框的判空以及文件格式的正确性判断
<html> <head> <title>上传题目</title> </head> <body style="margin-left:30px;margin-top:120px;background:url(../../img/bg.jpg)no-repeat ;background-position-x:55%;background-position-y:-10%;padding-top: 20px;font-size: 20px;"> <form action="${website}/arithmetic?state=upload" onsubmit="return checkform()" method="post" enctype="multipart/form-data"> <table style="padding-top: 160px" align="center"> <tr> <td colspan="2" align="center"> <font style="color:#1e90ff" size="3">${msg}</font> <a href="${website}/arithmetic?state=toDoArithmetic" style="text-decoration: none "><font style="color:dodgerblue">${msg1}</font></a> </td> </tr> <tr style="height: 15px"></tr> <tr> <td><font color="#1e90ff" face="STHupo" size="4">选择一个文件:</font></td> <td><font color="#1e90ff" face="STHupo" size="4"> <input type="file" name="uploadFile" id="nu"/> </font> </td> </tr> <tr style="height: 25px"> <td></td> <td></td> </tr> <tr> <td> <input type="submit" value="上传" style="width: 80px;height: 40px;border: none;background-color: skyblue;box-shadow: 2px 2px 2px #FFD700;border-radius: 5px;color: white;font-weight: 600;" /> </td> <td align="right"> <a href="${website}/arithmetic?state=toHome" style="text-decoration: none "> <input type="button" value="返回" style="width: 80px;height: 40px;border: none;background-color: skyblue;box-shadow: 2px 2px 2px #FFD700;border-radius: 5px;color: white;font-weight: 600;"> </a> </td> </tr> </table> </form> <script language="javascript" type="text/javascript"> function checkform(){ if(document.getElementById('nu').value.length==0){ alert('您还没有选择文件!'); document.getElementById('nu').focus(); return false; } } </script> </body> </html>
4)do _arithmetic.jsp:用户答题页面(1)用户页面会显示题目总数和当前题目序号
(2)页面有下一题,结束,返回主页三个按钮可供用户选择
(3)答题结束页面会显示用户答题数目,答对的题目数量以及花费时间。
<html> <head> <title>做题</title> </head> <center> <body style="margin-top:15%;margin-left:1%;background:url(../../img/bg.jpg)no-repeat ;background-position-x:55%;background-position-y:-10%;padding-top: 20px;font-size: 20px;"> <form id="doform" onsubmit="return checkform()" action="${website}/arithmetic?state=do&i=${i}&count=${count}" method="post"> <table style="padding: 20px" align="center"> <c:if test="${flag != 0}"> <tr style="border: hidden;height: 50px"> <td style="border: none"> <font style="color:#1e90ff" size="4">共${list.size()}题/第${i+1}题 </font> </td> <td style="border-radius:5px;border: double; border-color: dodgerblue" > <font size="4">${list.get(i)}</font> </td> <td style="border: none"> = </td> <td style="border-radius:5px;border: double;border-color: dodgerblue"> <input type="text" name="answer" id="nu" value="0" onfocus="javascript:if(this.value=='0')this.value='';" style="border-style:none;width: 60px;height: 50px"/> </td> </tr> <tr style="height: 30px"></tr> <tr style="border: hidden;"> <c:if test="${i < list.size()-1}"> <td colspan="2" align="center" style="border: none"> <input type="submit" value="下一题" style="width: 80px;height: 40px;border: none;background-color: skyblue;box-shadow: 2px 2px 2px #FFD700;border-radius: 5px;color: white;font-weight: 600;" /> </td> </c:if> <td colspan="2" align="center" style="border: none"> <input type="button" value="结束" onclick="return over();" style="width: 80px;height: 40px;border: none;background-color: skyblue;box-shadow: 2px 2px 2px #FFD700;border-radius: 5px;color: white;font-weight: 600;"/> </td> </tr> </c:if> <tr style="height: 60px"></tr> <font style="color:#1e90ff;margin-top: 15%" size="5" >${msg1}</font> <tr> <td colspan="4" align="left"> <a href="${website}/arithmetic?state=toHome" style="text-decoration: none "> <input type="button" value="返回" style="width: 80px;height: 40px;border: none;background-color: skyblue;box-shadow: 2px 2px 2px #FFD700;border-radius: 5px;color: white;font-weight: 600;"> </a> </td> </tr> </table> </form> <script type="text/javascript"> function over(){ if(document.getElementById('nu').value.length==0){ alert('请输入答案'); document.getElementById('nu').focus(); return false; }else { document.forms.doform.action="${website}/arithmetic?state=end&i=${i}&count=${count}"; document.forms.doform.submit(); } } function checkform(){ if(document.getElementById('nu').value.length==0){ alert('请输入答案'); document.getElementById('nu').focus(); return false; } } </script> </body> </center> </html>
5)login.jsp:用户登录页面
6)register.jsp:用户注册页面
7)record.jsp:记录用户答题数目,正确率,时间等
九、界面模块与计算模块的对接
项目的前后端交互我们使用jsp和servlet来实现,用户发送请求,后台从jsp页面获取到用户所输入的参数,在servlet页面对参数进行处理,调用生成题目接口生成题目到前端页面,答题时调用计算接口中的方法对客户端做出响应。
上传文件:
public void upload(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ boolean isMultipart = ServletFileUpload.isMultipartContent(request); RequestDispatcher rd = null; if(isMultipart){ FileItemFactory factory=new DiskFileItemFactory(); ServletFileUpload upload=new ServletFileUpload(factory); Iterator items; try{ items=upload.parseRequest(request).iterator(); while(items.hasNext()){ FileItem item=(FileItem) items.next(); if(!item.isFormField()){ String name=item.getName(); String fileName=name.substring(name.lastIndexOf('\\')+1,name.length()); String path=request.getRealPath("file")+File.separatorChar+fileName; String suffix = fileName.substring(fileName.lastIndexOf(".") + 1); if (suffix.equals("txt") || suffix.equals("doc")) { File uploadedFile=new File(path); item.write(uploadedFile); response.setContentType("text/html"); response.setCharacterEncoding("gb2312"); rd = request.getRequestDispatcher("/arithmetic?state=read&path="+path); rd.forward(request,response); }else{ request.setAttribute("msg","请上传txt类型的文件"); rd = request.getRequestDispatcher("/arithmetic?state=toUpload"); rd.forward(request,response); } } } }catch(Exception e){ e.printStackTrace(); } } }
读取文件内容:
public void read(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ RequestDispatcher rd = null; response.setContentType("text/html"); response.setCharacterEncoding("GBK"); PrintWriter out=response.getWriter(); String realPath= request.getParameter("path"); List<String> list = new ArrayList<>(); File file=new File(realPath); if(file.exists()){ FileReader reader=new FileReader(file); BufferedReader bufferedReader=new BufferedReader(reader); String line =null; while((line=bufferedReader.readLine())!=null) { list.add(line); } reader.close(); bufferedReader.close(); }else{ out.print("file is not exist!"); out.close(); } request.getSession().setAttribute("list",list); System.out.println(); request.setAttribute("msg1","题目上传成功,点击这里开始答题!"); rd = request.getRequestDispatcher(WebContents.UPLOAD); rd.forward(request,response); }
结束运算:
public void end(HttpServletRequest request,HttpServletResponse response) throws ServletException,Exception,IOException { RequestDispatcher rd = null; Calculation calculation = new CalculationImpl(); Date day=new Date(); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String nowDate = df.format(day); int count = Integer.parseInt(request.getParameter("count")); int i = Integer.parseInt(request.getParameter("i")); List<String> list = (List<String>) request.getSession().getAttribute("list"); int answer = Integer.parseInt(request.getParameter("answer")); String temp = list.get(i); if(i < list.size()){ if(temp.contains("÷")){ temp = temp.replace('÷','/'); } int result = calculation.Calculation(temp); if(result == answer){ count++; } i++; } String strTime = (String) request.getSession().getAttribute("time"); long time = (df.parse(nowDate).getTime() - df.parse(strTime).getTime())/1000; long seconds = (time%3600)%60; long min = (time%3600)/60; long hour = time/3600; request.setAttribute("flag",0); request.setAttribute("msg1","本次共答"+i+"题,答对"+count+"题!用时"+hour+"时"+min+"分"+seconds+"秒!" ); rd = request.getRequestDispatcher(WebContents.DO); rd.forward(request,response); request.getSession().invalidate(); } }
实现的功能:
附加功能:
上传过程中的文件类型应该为.txt
十、描述结对的过程
本次作业,我们两个人在工作室完成结对编程的。本次编程过程中,我们两个人都有明确的分工,一人负责一个模块,过程虽然有点困难,但是我们也充分的感受到了合作的重要性。尤其是在编程过程中,明白了自己设计的东西要简单易懂,让人能一眼看懂。
十一、说明结对编程的优点和缺点
结对编程优点: 1.结对编程就是两个程序员互相审查的过程,在编程过程中能够尽早发现问题并解决问题,提高了编程效率。
2.编程过程中遇到瓶颈两个人相互鼓励,极大的提高了编程的积极性。
3.两个人在编程过程中不断磨合,相互学习,使双方的代码能力得到了一定程度上的增强,同时也有了合作意识。
结对编程缺点: 1.一个人独立钻研的时候,两个人思路会不同,编程方法也不同,需要时间来抉择用哪一个方法,需要磨合时间
马玲优点: 1.编程能力强,认真
2.对于模块独立化有独特的见解
3.逻辑思维能力强,编程严谨
马玲缺点: 1.不够细心,经常会忽略一些细节,考虑不够全面
王雪优点: 1.遇到bug有耐心解决
2.注重细节,努力
3.态度积极,乐于学习
王雪缺点: 1.编程经验不足,对程序把握不够
十二、总结
作业延长的时间里,我们对项目进行了完善,添加了用户的注册和登录功能,并对用户所答问题有所记录。同时我们也对效能测试这一部分重新进行了尝试,进一步完善。
但我们的项目在前端页面设计方面我们有所欠缺,相较于其他掌握的较好同学来说,差距仍很大,在以后的学习中会不断完善。
十三、PSP表格
PSP | 任务内容 | 实际时间(min) |
Planning | 计划 | 50 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 30 |
Development | 开发 | 1560 |
Test | 测试 | 110 |
Algorithm Optimization | 算法优化 | 200 |
Interface Design | 接口设计 | 160 |
Coding Standard | 代码规范 | 60 |
Design | 具体设计 | 160 |
Coding | 具体编码 | 600 |
Code Review | 代码复审 | 60 |
Test | 测试 | 150 |
Reporting | 报告 | 120 |
Test Report | 测试报告 | 100 |
Size Measurement | 计算工作量 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 150 |