结对项目博客
1.代码仓库地址:https://git.coding.net/mafx8859/ArithWeb.git
远程服务器测试路径(直接点击测试):http://47.93.197.5:8080/ArithmeticWeb1.0/
2.PSP表格:
PSP | 任务内容 | 计划共完成需要的时间 | 实际完成需要的时间 |
Planning | 计划 | 一周 | 一周 |
Estimate | 估计这个任务需要多少时间,并规划大致工作步骤 | 一周 | 一周 |
Development | 开发 | 4天 | 4天 |
Analysis | 需求分析 (包括学习新技术) | 3.5(h) | 3(h) |
Design Spec | 生成设计文档 | 2(h) | 2(h) |
Design Review | 设计复审 (和同事审核设计文档) | 1(h) | 1(h) |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 1(h) | 40min |
Design | 具体设计 | 1h | 35min |
Coding | 具体编码 | 2天 | 2天 |
Code Review | 代码复审 | 1(h) | 1(h) |
Test | 测试(自我测试,修改代码,提交修改) | 2(h) | 2(h) |
Reporting | 报告 | 2h | 1.5h |
Test Report | 测试报告 | 1.5h | 1h |
Size Measurement | 计算工作量 | 30min | 30min |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30min | 20min |
3.
(1)Information Hiding:在出题模块和计算模块的设计过程中尤其体现出了Information Hiding,在出题模块中对于访问者来说,存储题目的容器list、运算数的上下界、存储运算符的数组等都是不可随意访问修改的,运用java的private进行将其私有化,向外提供访问的方法即可。在计算模块,计算运用到的栈、判断运算符优先级的map等均是向外隐藏的,同样运用private关键字将其向外隐藏。如下:
private Stack<String> s1=new Stack<String>();
private Stack<Integer> s2=new Stack<Integer>();
private Map<String,Integer> map=new HashMap<String,Integer>();
private int maxDigitNum=6;
private int minDigitNum=4;
private int[] digitArray;
private String[] operatorArray;
private List<String> list=new ArrayList<String>();
同时在web页面程序开发中,运用到的实体类中的属性均运用private修饰,向外提供geter和seter方法,增强了信息的保护。
(2)Interface Design:运用接口编程,对出题模块和计算模块设计接口增强了程序的可扩展性,可维护性。
(3)Loose Coupling:在程序设计中将出题模块、计算模块、运算符判断模块等均封装到不同的方法中,降低程序的耦合度。
4.计算模块接口的设计与实现过程:
在计算模块,由于计算模块中所设计到的操作有运算符的优先级判断以及堆栈,因此放在同一个类中足以实现,包含的函数有运算符优先级判断函数、运算数和运算符的判断函数、中缀表达式转后缀表达式函数等。在计算模块中算法的关键是中缀表达式转后追表达式和计算结果时的堆栈算法,其中中缀表达式转后缀表达式算思路大致为:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级不高于栈顶符号则栈顶元素一次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止,计算是的堆栈算法思路大致为:从左到右遍历后缀表达式的每个数字和字符,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出栈进行运算,运算结果进栈,一直到最终获得结果。
5.计算模块接口部分的性能改进:
在性能改进中大致时间表如下:
项目 | 用时 |
使用JProfiler工具分析调试 | 2(h) |
查找影响性能的程序模块 | 1(h) |
性能改进 | 2(h) |
改进调试思路:首先进行内存资源监控,当程序刚进入运行时,对当前状态进行Mark,观察不同类的内存变化,一段时间后按下F4进行资源回收,此时发现在自己的向本地文件写入模块中有资源无法回收,经过进一步调试发现是对IO资源没有close,进而调用close()对IO资源进行关闭。同时在资源监测过程中发现出题模块对资源的占有较多,以及耗时较大,这里所改进的思路是将原来通过循环产生合法的运算式的方式改进成一次性输出合法运算式。性能分析图如下:
Mark:
再次回收:
内存分析:
6.计算模块部分单元测试展示:
该项目中的部分单元测试代码如下:
import static org.junit.Assert.*;
import java.io.IOException;
import org.junit.Test;
public class getResultTest {
DealAtith da=new DealAtith();
@Test
public void test1() {
assertEquals(17,da.getResult("6+3-2+10", true));
assertEquals(17,da.getResult("6+3-2+10", false));
}
@Test
public void test2() {
assertEquals(18,da.getResult("(6+4)÷2+10", true));
assertEquals(18,da.getResult("6+4÷2+10", false));
}
@Test
public void test3() {
assertEquals(260,da.getResult("(6+4)÷2*50+10", true));
assertEquals(260,da.getResult("(6+4)÷2*50+10", false));
}
@Test
public void test4() {
da.creatAtith(10, 4, 1, 100, true, true);
da.creatAtith(10, 4, 1, 100, false, false);
da.getBl("9+6*(2+10)");
da.getBl("9+6*(2+10)-(10÷2-2)");
try {
da.printOperForm();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("打印函数抛出异常");
}
}
}
覆盖率截图:
在计算模块中设计到对个条件的判断分支,其中分支主要存在于,对运算符优先级的判断、对括号的判断等,同样在由中缀缀表达式获取后缀表达式的方法中同样存在以上分支的判断,因此为了增加测试的覆盖率,首先从简单的加减法运算数据入手,接着再加入乘除法,最后引入带有括号的运算数据。这样逐渐提高了测试的覆盖率。
7.计算模块部分异常处理说明
在计算模块主要涉及到异常处理问题有参数合法性和运算结果的正确性,其中参数合法性包括对生成运算式个数的参数的合法性判断以及异常处理、运算符个数参数合法性判断以及异常处理、运算数范围参数合法性判断以及异常处理等
(1)对-m、-n参数的整体为空值是的测试,测试其为空时,程序对异常处理的友好性。
@Test
public void testcommand1() {
String[] args={};
Command.main(args);
}
(2)对运算数范围参数-m的合法性异常处理的JUNit测试代码以及参数构造思路为:
①选择合理的参数范围测试其在参数合理的条件下程序的正确性:
@Test
public void testcommand3() {
String[] args={"-m","1","100","-n","10"};
Command.main(args);
}
②构造不合理参数进行测试,测试其在出现异常时友好的异常处理功能:
@Test
public void testcommand3() {
String[] args={"-m","1","40","-n","10"};
Command.main(args);
}
(3)对生成运算式个数参数-n的合法性异常处理的JUNit测试代码以及参数构造思路为:
①构造用户传入的-n参数是合法的,用于测试在参数合法的条件下程序的正确性。
@Test
public void testcommand4() {
String[] args={"-m","1","40","-n","10"};
Command.main(args);
}
②构造不合理的参数用于测试当参数不合法是,程序对异常处理的友好性。
@Test
public void testcommand3() {
String[] args={"-m","1","100","-n","20000"};
Command.main(args);
}
(3)对计算模块运算结果的测试,其参数构造思路是从简单的运算式开始入手,逐渐构造叫复杂的运算式,测试计算程序的运行结果的正确性:
DealAtith da=new DealAtith();
@Test
public void test1() {
assertEquals(17,da.getResult("6+3-2+10", true));
assertEquals(17,da.getResult("6+3-2+10", false));
}
@Test
public void test2() {
assertEquals(18,da.getResult("(6+4)÷2+10", true));
assertEquals(18,da.getResult("6+4÷2+10", false));
}
@Test
public void test3() {
assertEquals(410,da.getResult("(6+4)÷2*50+10", true));
assertEquals(410,da.getResult("(6+4)÷2*50+10", false));
}
@Test
public void test4() {
da.creatAtith(10, 4, 1, 100, true, true);
da.creatAtith(10, 4, 1, 100, false, false);
da.getBl("9+6*(2+10)");
da.getBl("9+6*(2+10)-(10÷2-2)");
try {
da.printOperForm();
} catch (IOException e) {
System.out.println("打印函数抛出异常");
}
}
8.界面模块的详细设计过程
在界面设计过程中花费较多时间,为了增强界面的交互友好性,首先我们以草图的形式设计出了界面的大致布局与初级的样式,同时为了增强两人合作的工作效率初步拟定运用前后台分离的方式进行小系统的开发,前台运用ajax实现数据填充,后台给前台返回json格式的数据。在初步的界面布局设计号以后着手于代码的编写,其中初期重要页面部分代码如下:
<button onclick=timedCount()>开始答题</button> <button onclick=getGradpm()>成绩排名</button>
<form action="AtithControlle?flag=getGrad" method="post">
<input type="hidden" name="filename" id="filename_id"/>
<input type="hidden" name="userid" id="user_id"/>
<table id="atithList">
<tr><th>题目</th><th>填写答案</th></tr>
</table>
<input type="submit" value="提交答案"/>计时:<input type="text" name="time" id="time_id"/>
</form>
<div style="width:500px;background-color:yellow">
本套试题成绩排名(点击成绩排名可获取):
<table id="grad_id">
</table>
</div>
<script type="text/javascript">
$(function(){
$.ajax({
url:"AtithControlle",/*请求的地址*/
data:"flag=makeques&fileName="+"<%=request.getParameter("filename")%>",/*数据携带*/
type:"get",/*请求的方式*/
dataType:"json",
success:function(result){/*请求后得到的结果*/
console.log(result);
buildList(result);
}
});
});
function buildList(result){
$.each(result,function(index,item){
var i=0;
if(i==0){
document.getElementById("filename_id").value=item.filename;
document.getElementById("user_id").value=item.zcm;
}
var td1="<td>"+item.atith+"</td>";
var td2="<td><input type='hidden' name='userSolve'/><input type='hidden' name='rightSolve' value='"+item.result+"'/></td>";
$("<tr></tr>").append(td1).append(td2)
.appendTo("#atithList");
});
}
</script>
</body>
初期的界面实现了大部分交互功能,但是在用户的交互友好性上很欠缺,于是我们有进一步对界面进行的美化,添加了css样式和js动态效果,同时也进一步完善了ajax请求与数据处理程序,同时对功能做了进一步实现,部分代码如下:
<body>
<form method="post" name="chuti" action="AtithControlle?flag=chuti" onsubmit="return checkform()">
<div class="detail">
<label>
<span class="form-group tishi" style="letter-spacing: 4px;">题 目 数 量</span>
<input type="text" name="count" id="count" placeholder="必须为1-10000整数" required />
</label>
<b id="countb"></b>
</div>
<div class="detail">
<label>
<span class="form-group tishi" style="letter-spacing: 2px;">算式数值范围</span>
<input type="text" name="rangeLow" id="rangeLow" required/>
<span>——</span>
<input type="text" name="rangeHigh" id="rangeHigh" required/>
</label>
<b id="rangebL"></b>
<b id="rangebH"></b>
</div>
<div id="js-example-change-value" class="detail">
<span class="tishi">运算符的最大数目</span>
<input type="range" min="1" max="10" value="1" data-rangeslider name="opeMax">
<output></output>
</div>
<div class="detail selediv">
<span class="tishi" style="letter-spacing: 1px;">是否包含乘除法</span>
<select name="if_multiply" >
<option value="no">否</option>
<option value="yes">是</option>
</select>
</div>
<div class="detail selediv">
<span class="tishi" style="letter-spacing:1px;">是否包含括号</span>
<select name="if_bracket">
<option value="no">否</option>
<option value="yes">是</option>
</select>
</div>
<div class="detail ">
<div class="detailsub">
<div class="radius"></div>
<input type="submit" value="开始出题" class="submit" name="submit" />
</div>
</div>
</form>
<script src="js/jquery.min.js"></script>
<script src="js/rangeslider.min.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<script type="text/javascript">
$(function() {
var $document = $(document);
var selector = '[data-rangeslider]';
var $inputRange = $(selector);
function valueOutput(element) {
var value = element.value;
var output = element.parentNode.getElementsByTagName('output')[0];
output.innerHTML = value;
}
for (var i = $inputRange.length - 1; i >= 0; i--) {
valueOutput($inputRange[i]);
};
$document.on('input', selector, function(e) {
valueOutput(e.target);
});
$inputRange.rangeslider({
polyfill: false
});
$document.on('click', '#js-example-change-value button', function(e) {
var $inputRange = $('input[type="range"]', e.target.parentNode);
var value = $('input[type="number"]', e.target.parentNode)[0].value;
$inputRange
.val(value)
.change();
});
});
</script>
</body>
通过较长时间的设计与开发,开发出了交互较友好,功能全面的图形界面。
注:由于界面程序含有大量css和HTML等程序,不易在博客展示,详细代码请参考代码仓库。
9.界面模块与计算模块的对接
在界面与计算模块的链接中,前台向后台主要发送ajax请求,后台由Servlet接受前台请求并且向前台返回json格式的数据,功能展示如下:
初始页面,可点击选择不同模式:
出题界面:
出题成功以后,支持题目下载:
做题界面,用户可以自己上传题目,也可以选择已经上传的题目进行练习:
在用户做题过程中可显示计时:
10.结对的过程
11.结对编程优缺点都是存在着的,首先优点在于:
(1)程序员互相帮助,互相教对方,可能得到能力上的互补。
(2)可以让编程环境有效地贯彻Design。
(3)增强代码和产品质量,并有效的减少BUG。
(4)降低学习成本。一边编程,一边共享知识和经验,有效地在实践中进行学习。
(5)在编程中,相互讨论,可能更快更有效地解决问题。
缺点在于:
(1)对于有不同习惯的编程人员,可以在起工作会产生麻烦,甚至矛盾。
(2)有时候,程序员们会对一个问题各执己见(代码风格可能会是引发技术人员口水战的地方),争吵不休,反而产生重大内耗。
(3)两个人在一起工作可能会出现工作精力不能集中的情况。程序员可能会交谈一些与工作无关的事情,反而分散注意力,导致效率比单人更为低下。
(4)结对编程可能让程序员们相互学习得更快。有些时候,学习对方的长处,可能会和程序员们在起滋生不良气氛一样快。比如,合伙应付工作,敷衍项目。
(5)面对新手,有经验的老手可能会觉得非常的烦躁。不合适的沟通会导到团队的不和谐。
(6)新手在面对有经验的老手时会显得非常的紧张和不安,甚至出现害怕焦虑的的精神状态,从而总是出现低级错误,而老手站在他们后面不停地指责他们导致他们更加紧张,出现恶性循环。最终导致项目进展效率低下,并且团队貌合神离。
(7)有经验的人更喜欢单兵作战,找个人来站在他背后看着他可能会让他感到非常的不爽,最终导致编程时受到情绪影响,反而出现反作用。
团队成员优缺点分析:
(1)马福孝
优点:①擅长后台程序开发 ②擅长系统设计 ③有较强好的的解决程序bug能力
缺点:不太擅长交流,对部分前端技术不够熟悉
(2)吴建瑜
优点:①擅长前端页面的设计以及动态效果的实现②擅长交流与分析③对实际问题有较强的分析解决能力
缺点:对后端部分技术不够熟悉
12.实际花费时间记录: