结对作业搭档:童宇欣
本篇博客结构一览:
1).前言(包括仓库地址等项目信息)
2).开始前PSP展示
3).结对编程对接口的设计
4).计算模块接口的设计与实现过程
5).计算模块接口部分的性能改进
6).计算模块部分单元测试展示
7).计算模块部分异常处理说明
8).界面模块的详细设计过程
9).界面模块与计算模块的对接
10).结对过程的描述
11).结对编程的优缺点
12).完成后实际的PSP
13).项目总结与改进
1)前言
本次结对项目,我们制作的是网页版的四则运算系统。在结对的过程中,我(王文雨)负责前端,童宇欣负责后端。一起进行了两部分功能的对接与融合
以下是我们的一些项目信息:
仓库地址(王文雨的仓库):https://coding.net/u/theMostCute/p/sizeyunsuanxitong/git/tree/master/?public=true
仓库地址(童宇欣的仓库):https://git.coding.net/honey_xiaoxinxin/FreshCalculate.git
网页版可测试的URL地址:http://47.93.197.5:8080/FirstCalculate/index.jsp
项目代码说明(以童宇欣的代码仓库内容为准):项目整体放在了Calculate文件夹下,里面包含命令行出题部分代码NewCalculate,以及网页版完整源代码(web文件夹下)。命令行测试入口为src下Command.java;在src下将其编译,即可输入数据进行测试运行。
2).开始前PSP展示
PSP
PSP2.1 |
任务内容 |
计划共完成需要的时间(min) |
Planning |
计划 |
20 |
Estimate |
估计这个任务需要多少时间,并规划大致工作步骤 |
10 |
Development |
开发 |
1000 |
Analysis |
需求分析 (包括学习新技术) |
20 |
Design Spec |
生成设计文档 |
20 |
Design Review |
设计复审 (和同事审核设计文档) |
10 |
Coding Standard |
代码规范 (为目前的开发制定合适的规范) |
1 |
Design |
具体设计 |
20 |
Coding |
具体编码 |
500 |
Code Review |
代码复审 |
20 |
Test |
测试(自我测试,修改代码,提交修改) |
15 |
Reporting |
报告 |
20 |
Test Report |
测试报告 |
10 |
Size Measurement |
计算工作量 |
10 |
3).结对编程对接口的设计
在《构建之法》书中没有找到有关信息,希望老师在设计要求的时候,可以告诉我们页数。我在网上查阅了资料。
Information Hiding信息隐藏:信息隐藏指在设计和确定模块时,使得一个模块内包含的特定信息(过程或数据),对于不需要这些信息的其他模块来说,是不可访问的。
Interface Design接口设计:是传统的后勤保障的一种要素也是一种后勤功能。
Loose Coupling松耦合:系统通常是基于消息的系统,此时客户端和远程服务并不知道对方是如何实现的。客户端和服务之间的通讯由消息的架构支配。只要消息符合协商的架构,则客户端或服务的实现就可以根据需要进行更改,而不必担心会破坏对方。
本次作业采用的是网页制作,整体采用MVC模型MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。本次作业进行了模块化编程,运算模块和界面模块相对较独立,尽量做到了低耦合度。让判断正误和计时以及条件受限放到前端制作,减少服务器的负担。
4).计算模块接口的设计与实现过程
我负责的有
①计算结果正误判断:
用js方法通过回答数组对答案数组的一一对应。
for(var i=0;i<huida.length;i++){
anwser[i].style.display="inline-block";
if(huida[i].value==daan[i].innerHTML){
yesno[i].innerHTML="√";
yesno[i].style.color="#1DF126";
yes++;
}else{
yesno[i].innerHTML="×";
yesno[i].style.color="red";
no++;
}
}
②智能出题的条件判断,通过js方法,限制输入的条件
var oBiaodan=document.getElementById('oBiaodan');
$("#yiding").click(function(){
var nummMax=document.getElementById('nummMax');
var numm=document.getElementById('numm');
var minn=document.getElementById('minn');
var maxx=document.getElementById('maxx');
if(numm.value==""){
alert("请填写题目数量!!!");
oBiaodan.onsubmit=function(){return false;}
}
if(numm.value<1||numm.value>10000){
alert("题目数量超出范围,范围为1-10000");
oBiaodan.onsubmit=function(){return false;}
}
if(minn.value==""||minn.value<1||minn.value>50){
alert("请填写正确范围!!!下线范围1-100,上限范围50到1000 ");
oBiaodan.onsubmit=function(){return false;}
}
if(maxx.value==""||maxx.value<50||maxx.value>1000){
alert("请填写正确范围!!!下线范围1-100,上限范围50到1000 ");
oBiaodan.onsubmit=function(){return false;}
}
});
5).计算模块接口部分的性能改进(这部分展示雨欣的成果)
1.性能分析改进
在运算模块的性能分析过程中,大约花费了2小时,首先经过性能分析知道了我有些资源在使用结束后是没有回收的,经过排查后,我发现了是在写入文件后没有关闭输出流,导致了资源没有完全回收。发现这一问题后,我针对它进行了改进,关闭了输出流。使项目的性能得到了提高。同时经过性能分析,我发现我整个程序消耗最大函数是出题过程中的MakeQuestion3,即出题过程中不仅要出题,还要判断题目是否符合要求,要调用调度场算法运算答案。因为这里要满足出题的时候必须在规定数字范围内,及整除等条件,不符合要求的算式都要重新出题。发现这一问题后,我也对我的程序进行了改进,将是否能整除这些判断在出两个数字的简单式子的时候直接进行了保证,使这里只需要判断每一步的运算结果是否在规定范围内,使整个程序的性能得到了提高。
2.性能分析截图
项目总体分析图,从内存,多线程,CPU等方面分析了计算模块的性能,截图如下:
性能分析过程截图:
首次按F4,出现以下截图。可见有些资源没有回收,经排查,我发现是我没有关闭写入文件的输出流。解决过这个问题后,我再一次进行了性能分析。
经过以上的分析,让我了解了性能分析的过程,也知道了性能分析的重要性。
6)计算模块部分单元测试展示
(1)分别对计算模块的Command.java和MakeQuestion.java进行了测试。写了如下两个单元测试类。
(2)以下是测试类的部分代码截图:
(3)构造测试数据思路
a.对于Command的测试,主要应保证参数的各种输入情况都有涉及。所以我就分别构造了:参数输入正确时的参数args数组;不包含必须输入的参数,并且-o参数后没有输入应有数字的args1数组;-n参数后不包含应有的数字的args2数组;及字母参数后数字范围有错的args3,args4,args5,args6。经过以上设计,最终Command类的测试覆盖率达90%以上。
b.对于MakeQuestion方法的测试,为保证测试覆盖率,应保证各种类型的参数都输入执行。所以这里就构造了有乘除的,有括号的;没有乘除的,没有括号的;及不同运算符个数限制;不同数量;不同范围限制这些情况。经过以上设计,最终MakeQuestion类的测试覆盖率达90%以上。
(4)测试覆盖率截图展示
以下分别为两个类测试覆盖率的截图展示以及部分代码执行情况展示:
由图可知,此次单元测试的覆盖率还是相对较高的,执行通过的绿色占大部分,但仍有一些呈现黄色和红色。也会继续改进,争取再次提高测试覆盖率。
7)计算模块部分异常处理说明
(1)没有输入必须的参数-m,-n
①通过if判断对这种异常进行了处理。设计了两个参数ifm和ifn判断是否有-m,-n的输入。
②单元测试样例
③错误对应场景:例如命令行没有输入-n或-m。发生错误时,会提示用户输入的参数中必须包含题目数量/对题目数值上下界的设定。请重新输入。
(2)输入的参数超出指定范围
①对于这种情况我设计了参数ifrun对能获取到数字但是数字超出范围的情况进行了判断,出现异常时ifrun值为false。
②单元测试样例
③错误对应场景:例如命令行输入-n 10 –m -1 3。发生错误时,会提示用户各个参数应有的界限,请用户重新输入。
(3)输入的字母后没有紧跟着对应数量的或对应形式的参数。
①对于这种情况用了try-catch语句判断处理异常。输入错误就会执行catch语句中内容,给出合理提示。
②单元测试样例
③错误对应场景:例如-n –m 1 100的输入。发生错误时,会提示用户正确的输入形式,请用户重新输入。
(4)出题后,写入文件时出错
对于这种情况用了try-catch语句判断处理异常。输入错误就会执行catch语句中内容,给出文件出错提示。
8)界面模块的详细设计过程
整体思路:
①出题部分
功能如图所示通过<form>表单进行功能实现向后台传输数据
如果输入的东西不符合要求,js会进行判断
②文件上传(题目导入)
功能如图,文件提交由后台代码实现
③功能转换
这三个功能在同一个页面上,通过js改变css样式进行改变。
$("#mia").click(function(){
$("#xinxi").css("display","block");
$("#miao").css("display","none");
});
$("#chu").click(function(){
$("#xinxi").css("display","none");
$("#miao").css("display","block");
});
$("#otou").click(function(){
$("#mengban").css("display","block");
});
$("#cha").click(function(e){
$("#chamian").css("display","block");
e.stopPropagation();
})
$("#chamian").click(function(e){
$("#chamian").css("display","block");
e.stopPropagation();
})
$(document).click(function(e){
$("#chamian").css("display","none");
});
④成绩汇总
有两个汇总,一个是全部成绩,一个是最佳成绩。点击这两个按钮都会跳转到同一个jsp页面。而内容显示的不同由后台代码实现
全部成绩
嗷,前面截图漏了学号
各用户的最佳成绩
⑤题目页
没有提交之前,包括计时器(js实现)和题目显示,答案写在<input>中通过js进行与答案的判断
提交后出现答题情况的弹窗js判断对错,统计总题目数,答对题数,答错题数,和总时间,将数据通过隐藏的<form>传到后台
答题情况的弹窗是可以拉动的,可以让学生查看自己哪里答错了,可以再做但是不可以再次提交。拉动由js实现
//面板拖动
var drag=document.getElementById('drag');
drag.onmousedown=function(){
startMove();
}
var oCha=document.getElementById('QQcha');
oCha.onclick=function(){
var oBody=document.getElementById('mainbody');
var oCha=document.getElementById('QQcha');
oBody.style.display="none";
}
function startMove(event){
event=event || window.event;
var oBody=document.getElementById('mainbody');
var disX=event.clientX-oBody.offsetLeft;
var disY=event.clientY-oBody.offsetTop;
document.onmousemove=function(event){
event=event ||window.event;
okMove(event,disX,disY);
event.preventDefault();
}
document.onmouseup=function(){
document.onmousemove=null;
document.onmouseup=null;
}
}
function okMove(e,posX,posY){
var oBody=document.getElementById('mainbody');
var l=e.clientX-posX;
var t=e.clientY-posY;
var winW=document.documentElement.clientWidth || document.body.clientWidth;
var winH=document.documentElement.clientHeight || document.body.clientHeight;
var maxW=winW-oBody.offsetWidth-10;
var maxH=winH-oBody.offsetHeight;
if(l<0){
l=0;
}else if(l>maxW){
l=maxW;
}
if(t<0){
t=10;
}else if(t>maxH){
t=maxH;
}
oBody.style.left=l+'px';
oBody.style.top=t+'px';
}
9)界面模块与计算模块的对接。
作为前端,我只说我负责的部分:
①计时
// setInterval计时器
var theTime=document.getElementById('theTime');
var n=0;
var m=0;
var f=0;
var ff=0;
var timert=null;
function timeCount(){
clearInterval(timert);
timert=setInterval(
function(){
n=n+1;
f=n/60;
ff=parseInt(f)
m=n%60;
theTime.innerHTML=ff+" 分 "+m+" 秒";
},1000);
}
timeCount();
②判断正误
//判断回答是否正确
var tiJiao=document.getElementById('tiJiao');
var oBody=document.getElementById('mainbody');
var huida=document.getElementsByClassName('huida');
var daan=document.getElementsByClassName('e');
var anwser=document.getElementsByClassName('anwser');
var yesno=document.getElementsByClassName('yesno');
var emm=document.getElementsByClassName('emm');
var allnum=document.getElementById('allnum');
var alltime=document.getElementById('alltime');
var allyes=document.getElementById('allyes');
// var yes=0;
// var no=0;
tiJiao.onclick=function(){
var yes=0;
var no=0;
for(var i=0;i<huida.length;i++){
anwser[i].style.display="inline-block";
if(huida[i].value==daan[i].innerHTML){
yesno[i].innerHTML="√";
yesno[i].style.color="#1DF126";
yes++;
}else{
yesno[i].innerHTML="×";
yesno[i].style.color="red";
no++;
}
}
clearInterval(timert);
③统计总题目数,答对题数,答错题数,和总时间,将数据通过隐藏的<form>传到后台
HTML:
<form onsubmit="return PostData()" id="form1">
<input type="text" id="allnum" value="1" style="display: none;">
<input type="text" id="allyes" value="" style="display: none;">
<input type="text" id="alltime" value="" style="display: none;">
<input type="submit" class="quedinng" value="确定">
</form>
JS:
oBody.style.display="block";
tiJiao.style.display="none";
allnum.value=daan.length;
allyes.value=yes;
emm[0].innerHTML=daan.length;
emm[1].innerHTML=yes;
emm[2].innerHTML=no;
if(theTime.innerHTML=="计时区"){
emm[3].innerHTML="您没有计时";
alltime.value=0;
}else{
emm[3].innerHTML=theTime.innerHTML;
alltime.value=theTime.innerHTML;
}
}
10)描述结对的过程
我们的结对大约分为两个阶段,由于我们编写的是网页版,两个人擅长的领域不相同
第一阶段主要是一人负责前端,一人负责后端,这一过程我们相当于是分开各自编程的,所以这里部分对驾驶员和领航员这种模式的切换和运用不够明显,每人负责自己的那部分代码的编写;
第二部分,进入合页面和两个模块的对接过程,我们两人一起合作,在这一阶段也充分体验了结对编程两个人一起的高效之处。我们两个人不断切换驾驶员领航员角色,比较顺利的完成了不同板块的对接与项目的测试。同时也共同完成了整个项目的性能分析和单元测试的覆盖率分析。
结对的时候,我们都要耐心倾听,一起改bug,听取对方的建议
11)结对编程的优点和缺点
结对编程的优点:锻炼合作能力,沟通交流能力,分工配合能力
结对编程的缺点:两人分开干活,彼此进度不同,代码功能不同。合在一起不容易
童宇欣的优点:学习的上进心,在最开始考虑项目功能时,她一直强调想做出附加功能
强大的代码能力,在工作室她已经接手过几个校级项目,对代码熟悉
耐心,合作中配合,会有不同的bug一起改
童宇欣的缺点:真想不出来,和她配合很愉快
我的优点:积极配合,因为是前端,更多是辅助宇欣,她想要什么功能都可以满足
对前端语言的熟悉,也接手过项目
耐心,合作中配合,会有不同的bug一起改
我的缺点:对Java语言不熟悉
9)在你实现完程序之后,在附录提供的PSP表格记录下你在程序的各个模块上实际花费的时间。(1')
PSP
PSP2.1 |
任务内容 |
实际完成需要的时间(min) |
Planning |
计划 |
30 |
Estimate |
估计这个任务需要多少时间,并规划大致工作步骤 |
10 |
Development |
开发 |
2700 |
Analysis |
需求分析 (包括学习新技术) |
40 |
Design Spec |
生成设计文档 |
20 |
Design Review |
设计复审 (和同事审核设计文档) |
20 |
Coding Standard |
代码规范 (为目前的开发制定合适的规范) |
15 |
Design |
具体设计 |
60 |
Coding |
具体编码 |
1800 |
Code Review |
代码复审 |
45 |
Test |
测试(自我测试,修改代码,提交修改) |
700 |
Reporting |
报告 |
840 |
Test Report |
测试报告 |
780 |
Size Measurement |
计算工作量 |
30 |
13)项目总结与改进
本次作业完成了所有基本功能,能够实现规定要求的出题,和带负数的计算,同时实现了允许多用户做题,并能记录所有用户的全部成绩和最好成绩。
但仍然存在很多不足之处。比如另外一个附加功能(能够支持多语言)没能实现,还有由于对每一步运算结果的范围限制,导致我们的程序出现括号的概率变小。接下来的时间我们一定会继续改进我们的项目,争取能呈现出更好的效果。
和宇欣合作非常棒!!她做事认真,热情开朗,是一个非常棒的合作伙伴!!