作业源代码地址:https://git.coding.net/mal123/arithmetic.git
网页版测试地址:http://47.93.197.5:8080/mal_war_exploded/arithmetic?state=toHome
结对成员:马玲 2016012054 ;王雪 2016012013
1.开始实现程序之前,我们对程序的各个模块开发上耗费时间的估计如下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 |
2.关于Information Hiding, Interface Design, Loose Coupling在项目中的应用:
1.)Information Hiding:信息隐藏。
信息隐藏是程序设计过程中的一种隔离原则,可以防止用户接触到一个类的某些部分。一个程序模块可以将它的信息隐藏起来,对外仅仅展现出一种接口。当这个模块的具体实现发生改变时,只要保证它的接口不发生变化,则就算不修改模块外的其他代码,程序依旧可以正确执行。对于面向对象的程序设计而言,信息隐藏是一种重要的软件开发手段,它与对象的封装(encapsulation)和模块化(modularity)密切相关,每个模块完成一个特定的子功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能。主要是封装完成之后,后面做项目用起来会很轻松,并且节约工作量和时间,减少错误以及避免代码的臃肿影响代码的可读性。
在这次的结对项目中,对于用户而言,只需要设置自己的参数,得到相应的题目,用户并不关心程序是怎么生成题目的,他只是调用这个方法,所以可以将生成题目这一模块设计成接口,计算这一模块也是同样的道理。在模块的优化过程中,保证接口不变,我们只需要修改接口的实现,并不需要对其他地方进行修改,程序也可以正常运行。
2.)Interface Design:接口设计。
关于接口的设计,我们也阅读了一些资料,其中有一篇我觉得很有用:https://www.jianshu.com/p/580b943d68e2在设计接口的过程中,不仅要解决当前问题还有保证程序的可扩展性,命名要做到见名知意。对于本次项目来说,我们采用的是mvc模式开发,所以在不同的层都会用到设计的接口,所以命名一定要规范,能够让队友清楚地理解每一个方法所实现的功能,若涉及到多人的项目中,则更应该写一个详细的接口文档来说明。
3.)Interface Design:松耦合。
接口的设计要满足的一个原则就是松耦合,松耦合的接口设计使得程序设计的可拓展性大大增强。新设计的类通过实现某个接口,可以轻轻松松地替换当前的某个类,而无需修改调用这些接口的类。所以我们在设计过程中要尽可能减少模块之间的依赖,达到松耦合。
3.计算模块接口的设计与实现过程:
对于本次的项目,我们会设计两个接口分别实现生成题目和计算题目的功能。具体设计见下图:
生成题目模块的接口:
各个方法之间的关系:
计算题目模块的接口:
本次项目开发,我们采用的是我之前个人项目的代码,我们将之前生成题目和计算的功能分开,仍旧采用之前的算法生成题目,所以具体流程图就不再多做展示了。在之前算法的基础上加入上下界的限制,以及选择乘除法和括号的功能,具体代码写在接口的实现里。在业务逻辑层,获取用户的请求,调用对应的接口,做出响应。由于此次项目要求只需要生成符合要求的题目并不要求算出答案,计算也是对已有的题目直接进行计算,所以在计算题目这一功能的实现我们采用的是eval()函数,直接用于计算用户上传的题目。
4. 计算模块接口部分的性能改进
我们用JProFiler对内存、ALL OBJECT、Record Object等方面进行了分析:
项目总体分析图:
CPU分析图:
性能分析:
在项目中消耗最大的函数isParenthesis方法,由于限制条件太多,所以当题目数量过大时,函数消耗会很大。在分析过程中发现在运行过程中,没有对循环时产生的list清空,所以浪费很大空间,针对这些问题,我们也对代码进行了修改。
5.计算模块部分单元测试展示
在单元测试中,我们首先对两个接口的基本功能进行了测试,在确保基本功能都可以正确实现之后,我们对Command类进行了单元测试。
Command类测试代码如下:
import org.junit.Test; import java.util.ArrayList;
import java.util.List; 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"};//缺少参数 List<String[]> list = new ArrayList();
list.add(args1);
list.add(args2);
list.add(args3);
list.add(args4);
list.add(args5);
list.add(args6);
list.add(args7);
list.add(args8); for (int i = list.size() - 1; i >= 0; i--)
Command.main(list.get(i));
} }
代码覆盖率如下:
接口类测试覆盖率:
Command类的测试覆盖率:
在单元测试中,每个类对应一个测试类,测试类中每个测试方法对应一个方法。构造测试数据时,根据方法中每个分支对应的情况,设置相应的参数,确保测试会进入到每一个分支,达到测试的目的。
6.计算模块部分异常处理说明
在计算模块中,用户如果传入不合法参数,程序会捕获异常,显示异常类型并提示错误参数。由于我们的项目是用web实现的,对于不合法的参数(如汉字,符号)以及参数为空这些问题都会在前端进行判断,并提醒用户,所以在异常处理的得上设计中,只做了参数范围不合法一种异常类型,由于题目会有上下界的要求,但当上下界范围较小时,无法产生题目,因此我们设定当上下界只差小于30时,抛出异常,提示用户扩大范围。
当符号数量不在合理范围内时:
@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 createNumber() throws Exception {
System.out.println(createArithmetic.CreateNumber(4,50,60));
}
当题目数量不在合理范围时:
@Test
public void createFile() throws Exception {
System.out.println(createArithmetic.createFile(-9,true,true,10,1000,4)); }
7. 界面模块的详细设计过程
界面模块我们采用jsp结合js完成前端页面的设计,通过servlet完成前后端的交互。
根据程序所要实现的功能,我们将设计四个页面用来展示并实现这些功能:
1)home.jsp:主页。有生成题目和上传题目两个按钮,点击不同的按钮进入不同页面,进行相应操作
2)create_arithmetic.jsp:生成题目页面。页面通过form表单,获取用户输入的参数,生成相应题目,并可供用户下载生成的题目。页面显示每个参数的输入框,提示参数范围,并对必填输入框进行判空;有生成题目和返回(返回主页)两个按钮。
3)upload _arithmetic.jsp:上传文件页面。用户选择文件上传,上传成功之后点击答题,进入答题页面开始答题。页面显示输入框,用户可选择文件,实现输入框的判空以及文件格式的判断;有上传和返回(返回主页)两个按钮。
4)do _arithmetic.jsp:答题页面。用户在这个页面可以进行答题。页面会显示题目总数和当前题目序号;显示题目,用户做答;页面有下一题,结束,返回(返回主页)三个按钮,用户可以选择继续答题或结束答题;当做到最后一题时,隐藏下一题的按钮。结束答题之后,页面会显示用户答题数目,答对的题目数量以及花费时间。
答题页面代码的展示:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
Created by IntelliJ IDEA.
User: 芋头
Date: 2018/4/2
Time: 20:49
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<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>
8. 界面模块与计算模块的对接
项目中前后端交互采用servlet实现。当用户发送一个请求,会进入到相应的servlet方法中,在servlet方法中获取对应的参数,调用接口中的方法,并对客户端做出响应。为了提高代码的安全性,jsp页面放在了web-INF目录下面,因此只有通过servlet才能访问到页面。servlet中包含了上传文件,下载文件,读取文件,生成题目,答题以及各个页面之间跳转的方法。其中,在生成题目的方法中会调用CreateArithmetic的接口,在答题的方法中会调用Calculation的接口。
这里展示部分代码:
/**
*读取文件内容
* @param request
* @param response
* @throws ServletException
*/
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);
}
实现的功能:
9.描述结对的过程
本次作业,我们两个人在工作室完成结对编程的。本次编程过程中,我们两个人都有明确的分工,一人负责一个模块,过程虽然有点困难,但是我们也充分的感受到了合作的重要性。尤其是在编程过程中,明白了自己设计的东西要简单易懂,让人能一眼看懂。
讨论照片展示:
10.结对编程的优缺点
结对编程优点:
1.结对编程就是两个程序员互相审查的过程,在编程过程中能够尽早发现问题并解决问题,提高了编程效率。
2.编程过程中遇到瓶颈两个人相互鼓励,极大的提高了编程的积极性。
3.两个人在编程过程中不断磨合,相互学习,使双方的代码能力得到了一定程度上的增强,同时也有了合作意识。
结对编程缺点:
1.一个人独立钻研的时候,两个人思路会不同,编程方法也不同,需要时间来抉择用哪一个方法,需要磨合时间
马玲优点:
1.编程能力强,认真
2.对于模块独立化有独特的见解
3.逻辑思维能力强,编程严谨
马玲缺点:
1.不够细心,经常会忽略一些细节,考虑不够全面
王雪优点:
1.遇到bug有耐心解决
2.注重细节,努力
3.态度积极,乐于学习
王雪缺点:
1.编程经验不足,对程序把握不够
11.实际花费的时间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 |
12.总结与修改
在后面老师给的时间段里,我们两个人在之前的代码基础上修改了一些功能。在效能分析这一模块中,我们做了更近一步的完善,在博客中做了相应的补充。在项目功能这一模块,我们加入了用户登录和注册,实现了用户可以查看自己答题记录的功能。但我们的项目中也存在很多不足,对于前端展示页面,由于我们这方面知识的欠缺,所以完成的并不是很好,以后的作业中我们会不断完善,改进自己的不足。
增加的功能展示: