小学四则运算结对项目
一.项目地址
https://git.coding.net/chenxin1998/Arithmetic.git
结对成员:马乐平,地址:https://git.coding.net/maleping/Amerithic.git
二.预计完成项目时间(PSP)
|
PSP2.1 |
任务内容 |
计划共完成需要的时间(h) |
实际完成需要的时间(h) |
|
Planning |
计划 |
15 |
17 |
|
· Estimate |
· 估计这个任务需要多少时间,并规划大致工作步骤 |
18 |
20 |
|
Development |
开发 |
98 |
100 |
|
·· Analysis |
需求分析 (包括学习新技术) |
52 |
54 |
|
· Design Spec |
· 生成设计文档 |
22 |
24 |
|
· Design Review |
· 设计复审 (和同事审核设计文档) |
18 |
20 |
|
· Coding Standard |
代码规范 (为目前的开发制定合适的规范) |
12 |
16 |
|
· Design |
具体设计 |
26 |
28 |
|
· Coding |
具体编码 |
48 |
56 |
|
· Code Review |
· 代码复审 |
7 |
9 |
|
· Test |
· 测试(自我测试,修改代码,提交修改) |
18 |
24 |
|
Reporting |
报告 |
9 |
6 |
|
·· Test Report |
· 测试报告 |
3 |
2 |
|
· Size Measurement |
计算工作量 |
2 |
1 |
|
· Postmortem & Process Improvement Plan |
· 事后总结 ,并提出过程改进计划 |
3 |
3 |
三.如何进行接口设计*
在进行接口设计之前,我们认真阅读了教材中的相应章节,以及相关的博客,以下是一些介绍接口设计的优秀博客:
https://blog.csdn.net/yu870646595/article/details/51900478
https://blog.csdn.net/blueangle17/article/details/54965597
首先要想好是给什么业务提供的接口,比如是给旅游业务提供的接口,可以叫做TravelService,意思就是说需要根据业务给项目起名字。
信息隐藏:信息隐藏是指在设计和确定模块时,使得一个模块内包含的特定信息(过程或数据),对于不需要这些信息的其他模块来说,是不可访问的。因此,我们在最初设计时决定运用java的private进行将其私有化,向外提供访问的方法。
松耦合:降低耦合度的过程就是模块化编程的过程。我们在设计的过程中,决定采用前后端分离的模式,运算模块,与界面模块相对独立,以此来降低耦合度。
接口设计:面向接口编程是软件工程领域常用的设计手段。
四.计算模块接口的设计与实现过程*
在计算模块,实现小学四则运算的这部分代码中,主要分为三个类,其中RandomArithmetic类负责开启程序运行的入口,CreateProblem主要用于处理运算式,WriteResult用于文件写入,整个程序由这三个类块构成,实现了功能的模块化。
关键函数:
其中在CreateProblem类中又分离出了private Stack<Integer> stackOfNum=new Stack<Integer>();//计算结果所用栈;private Stack<Character> suffix=new Stack<Character>();//后缀表达式private Stack<Character> stackOfOperation=new Stack<Character>();//计算后缀表达式所用栈;这样设计使得各功能再次模块化,使得程序更加的便于维护和扩展。
独到之处:
1.用两个Character栈存储数字和符号,然后用两个栈分别用作转换的缓冲栈和计算结果的缓冲栈
2. 一般Character里面的编码是ascii,但我没有那样做,就是一种新的编码,用ascii0-99表达0-99,而符号和数字冲突,所以符号是100-105





五.计算模块接口部分的性能改进
(记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图,并展示你程序中消耗最大的函数。)
|
|
用时 |
|
使用JProfiler工具分析调试 |
3(h) |
|
查找影响性能的程序模块 |
3(h) |
|
性能改进 |
6(h) |
我们在性能分析的过程中花费了比较多的时间。
性能优化涉及面很广。一般而言,性能优化指降低响应时间和提高系统吞吐量两个方面,但在流量高峰时候,性能问题往往会表现为服务可用性下降,所以性能优化也可以包括提高服务可用性。在某些情况下,降低响应时间、提高系统吞吐量和提高服务可用性三者相互矛盾,不可兼得。
一开始的时候,我们用了比较多的正则表达式,发现:正则表达式给人的印象是快捷简便。但是在 N.O.P.E 分支中使用正则表达式将是最糟糕的决定。如果万不得已非要在计算密集型代码中使用正则表达式的话,至少要将Pattern缓存下来,避免反复编译Pattern。最好还是用普通的 char[] 数组或者是基于索引的操作。还有就是使用了大量的泛型,导致的结果是使用了 byte、 short、 int 和 long 的包装类,当我们处于 N.O.P.E. 分支的深处时,应该极力避免使用包装类。这样做的坏处是给GC带来了很大的压力。GC将会为清除包装类生成的对象而忙得不可开交。所以最后优化方法是使用基本数据类型、定长数组,并用一系列分割变量来标识对象在数组中所处的位置。
以下是一些JAVA性能优化的办法:http://www.importnew.com/16181.html
性能分析图:

六. 计算模块部分单元测试展示
部分单元测试代码如下:
package cn.bravedawn.airthmeticwebappse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class AirthmeticwebappseApplicationTests {
@Test
public void contextLoads() {
}
}
部分函数代码如下:
package cn.bravedawn.airthmeticwebappse.common.util;
public class CalculateUtil {
public static void main(String[] args) {
System.out.println(compute(1, 2, "+"));
}
public static Integer compute(int firstNum, int secNum, String operator) {
switch (operator) {
case Const.Operator.add: {
return firstNum + secNum;
}
case Const.Operator.subtraction: {
return firstNum - secNum;
}
case Const.Operator.multiplication: {
return firstNum * secNum;
}
case Const.Operator.division: {
return firstNum / secNum;
}
default: {
return null;
}
}
}
}
package cn.bravedawn.airthmeticwebappse.common.util;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CookiesUtil {
/**
* 添加cookie
*
* @param response
* @param name
* @param value
*/
public static void addCookie(HttpServletResponse response, String name, String value, int time) {
Cookie cookie = new Cookie(name.trim(), value.trim());
cookie.setMaxAge(time);// 设置为30min
cookie.setPath("/");
System.out.println("已添加===============");
response.addCookie(cookie);
}
/**
* 修改cookie
*
* @param request
* @param response
* @param name
* @param value 注意一、修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。
*/
public void editCookie(HttpServletRequest request, HttpServletResponse response, String name, String value) {
Cookie[] cookies = request.getCookies();
if (null == cookies) {
System.out.println("没有cookie==============");
} else {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
System.out.println("原值为:" + cookie.getValue());
cookie.setValue(value);
cookie.setPath("/");
cookie.setMaxAge(60 * 60 * 7);// 设置为30min
System.out.println("被修改的cookie名字为:" + cookie.getName() + ",新值为:" + cookie.getValue());
response.addCookie(cookie);
break;
}
}
}
}
/**
* 删除cookie
*
* @param request
* @param response
* @param name
*/
public void delCookie(HttpServletRequest request, HttpServletResponse response, String name) {
Cookie[] cookies = request.getCookies();
if (null == cookies) {
System.out.println("没有cookie==============");
} else {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
cookie.setValue(null);
cookie.setMaxAge(0);// 立即销毁cookie
cookie.setPath("/");
System.out.println("被删除的cookie名字为:" + cookie.getName());
response.addCookie(cookie);
break;
}
}
}
}
/**
* 根据名字获取cookie
*
* @param request
* @param name cookie名字
* @return
*/
public static Cookie getCookieByName(HttpServletRequest request, String name) {
Map<String, Cookie> cookieMap = ReadCookieMap(request);
if (cookieMap.containsKey(name)) {
Cookie cookie = (Cookie) cookieMap.get(name);
return cookie;
} else {
return null;
}
}
/**
* 将cookie封装到Map里面
*
* @param request
* @return
*/
private static Map<String, Cookie> ReadCookieMap(HttpServletRequest request) {
Map<String, Cookie> cookieMap = new HashMap<String, Cookie>();
Cookie[] cookies = request.getCookies();
if (null != cookies) {
for (Cookie cookie : cookies) {
cookieMap.put(cookie.getName(), cookie);
}
}
return cookieMap;
}
}
单元测试覆盖率:

七. 计算模块部分异常处理说明
异常处理代码如下:
package cn.bravedawn.airthmeticwebappse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class AirthmeticwebappseApplicationTests {
@Test
public void contextLoads() {
}
}
八.界面模块的详细设计过程*
首先,为了提高完成项目的效率,我们采用了前后端分离的方式进行项目开发。在最初的界面设计中,考虑到我们的目标用户中有小学生、家长、以及教师,因此界面的适用性比较重要。所以我们现在Photoshop上规划出页面的大体格局及初步界面样式。我们分了三个界面,分别是进入界面,答题界面,和结果界面。在编程过程中,页面实现采用了html+css+js+jquery进行编码。
初步重要代码如下:
! function(a, b) {
var c = "function" == typeof define,
d = "undefined" != typeof module && module.exports;
c ? define(a, b) : d ? module.exports = b() : this[a] = b()
}("Calculagraph", function() {
function a() {
this.time = 0, this.isRunning = !1, this._callback = null, this._interval = 0, this.last = 0
}
return a.prototype.increase = function(a, b, c, d) {
var e = this;
return e._curry(0)(a, b, c, d), e
}, a.prototype.decrease = function(a, b, c, d) {
var e = this;
return e._curry(1)(a, b, c, d), e
}, a.prototype._curry = function(a) {
var b = this;
return a && (b._set = b.time),
function(c, d, e, f) {
if(b.isRunning) return !1;
b.isRunning = !0, b._callback = c, b._interval = d, b._tick = e || 1e3, b._finish = f || function() {}, b.last = a;
var g = +new Date;
! function h() {
var f = +new Date,
i = parseInt((f - g) / 1e3);
return c(b.time / 1e3), a ? b.time -= e : b.time += e, b.time < 0 || d && i >= d ? (b.stop(), void b._finish()) : void(b.times = setTimeout(function() {
h()
}, e))
}()
}
}, a.prototype.set = function(a) {
var b = this;
return b.isRunning ? !1 : (b.time = 1e3 * a, void(b._time = b.time))
}, a.prototype.stop = function() {
var a = this;
clearTimeout(a.times), a.time = null, a.isRunning = !1
}, a.prototype.parse = function() {
var a = this;
clearTimeout(a.times), a.isRunning = !1
}, a.prototype.restore = function() {
var a = this;
a.last ? a.decrease(a._callback, a._interval, a._tick, a._finish) : a.increase(a._callback, a._interval, a._tick, a._finish)
}, a.prototype.restart = function() {
var a = this;
a.stop(), a._time && (a.time = a._time), a.restore()
}, a
});
九.界面模块与计算模块的对接*
UI模块设计过程:我们分为三个页面:用户进入页面,做题页面,结果分析页面。
用户进入页面:前台使用form表单把用户输入的信息传到后台进行出题。这部分对用户输入信息的各种情况的判断,由前台的js判断并实现。

做题界面:打开做题页面,页面上可以呈现出所有题目。通过js对答案对错进行判断。后台把所有题目和所有题目答案放在list里传递到前台,答题者完成题目后,可以进行检查。检查无误后,答题情况即可提交到后台。

结果界面:答题者提交答题后,由后台JSP页面返回结果。

十.描述结对的过程

十一.结对编程的优点和缺点
同时指出结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。
通过这次结对项目,我觉得结对编程能够带来1+1>2的效果。
结对编程有以下优点:
1.之前一个人做的时候,遇到不懂得问题时,就会陷入迷茫。当两个人一起编程时,有不懂的可以一起讨论,说出自己的意见,也为整个编程过程节约了时间。
2.除此之外,两个人一起合作时就会产生更多更好的想法,可以更好的优化整个项目的设计,遇到问题时可以相互帮助解决问题,效率也比较高。
3.两个人合作还可以发现多方身上的优点,找出自己身上的不足,并能相互监督让彼此变得更好,还可以彼此分享好的学习经验。综上所述,我们认为两个人合作的效率远远大于一个人。
要说结对编程的缺点:
1.我觉得主要在完成项目的过程中,会产生分歧。每个人有每个人自己的想法,如果不能及时解决分歧,达成一致,很有可能演化成矛盾,进而使合作关系破裂,无法完成项目。
2.对于一个有经验的编程人员来说,更习惯于一个人编程。毕竟,在编程的过程中有别人在看的感觉有些奇怪。
小组成员互评:
马乐平:优点:认真细心,勤奋好问,善于思考;
缺点:理解能力欠佳,不善沟通
陈逸璇:优点:对学习充满热情,不怕困难,善于沟通;
缺点:技术还有待提高
十二.实际完成项目时间(PSP)
|
PSP2.1 |
任务内容 |
计划共完成需要的时间(h) |
实际完成需要的时间(h) |
|
Planning |
计划 |
15 |
17 |
|
· Estimate |
· 估计这个任务需要多少时间,并规划大致工作步骤 |
18 |
20 |
|
Development |
开发 |
98 |
100 |
|
·· Analysis |
需求分析 (包括学习新技术) |
52 |
54 |
|
· Design Spec |
· 生成设计文档 |
22 |
24 |
|
· Design Review |
· 设计复审 (和同事审核设计文档) |
18 |
20 |
|
· Coding Standard |
代码规范 (为目前的开发制定合适的规范) |
12 |
16 |
|
· Design |
具体设计 |
26 |
28 |
|
· Coding |
具体编码 |
48 |
56 |
|
· Code Review |
· 代码复审 |
7 |
9 |
|
· Test |
· 测试(自我测试,修改代码,提交修改) |
18 |
24 |
|
Reporting |
报告 |
9 |
6 |
|
·· Test Report |
· 测试报告 |
3 |
2 |
|
· Size Measurement |
计算工作量 |
2 |
1 |
|
· Postmortem & Process Improvement Plan |
· 事后总结 ,并提出过程改进计划 |
3 |
3 |
总结:经过此次结对项目,让我感受到结对编程的优点及乐趣。同时,经过此次项目的锻炼,让我对后端的学习更急深入,同时培养了我的许多能力,如:如何与别人沟通,如何合作等等。总之,此次作业中收获很大。