软件工程导论结对项目
小学四则运算结对项目
一.项目地址
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 |
总结:经过此次结对项目,让我感受到结对编程的优点及乐趣。同时,经过此次项目的锻炼,让我对后端的学习更急深入,同时培养了我的许多能力,如:如何与别人沟通,如何合作等等。总之,此次作业中收获很大。