博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2016012032四则运算网页版结对项目报告
阅读量:4576 次
发布时间:2019-06-08

本文共 15271 字,大约阅读时间需要 50 分钟。

一、测试的url地址及Coding.net地址

可测试的url地址:http://39.105.6.214/myWeb_war/

Coding.net源码仓库地址:https://git.coding.net/wanghz499/2016012032partnerWork.git

命令行测试command.java类(用到自己做的core.jar):

(注:请使用win+R命令提示符,不要使用PowerShell)

1.打开命令行进入src文件夹:
cd C:\Users\wangh\Desktop\2016012032partnerWork\src
(注意根据自己放的位置调整路径,下同)
2.编译command.java和core.jar:
javac -cp C:\Users\wangh\Desktop\2016012032partnerWork\web\WEB-INF\lib\core.jar -encoding utf-8 C:\Users\wangh\Desktop\2016012032partnerWork\src\Command.java
(-cp表示路径,core.jar包放在web的WEB-INF的lib文件夹下)
3.运行command.java和core.jar,并输入参数:
java -cp ..\web\WEB-INF\lib\core.jar; Command -n 10 -m 1 100 -o 5

运行成功,result.txt生成在与src同级的目录下。

 

二、估计项目开发时间

    已记录在结尾的PSP表格,此处不重复显示。

三、设计接口原则

   在设计接口之前,我们应该明白接口是做什么的,它有什么设计原则,怎样才能设计出好的接口。于是我去知乎上浏览了相关内容,有几点是我印象比较深刻的:

    1.职责单一化,不要想着做件大事,提供各种通用性,尽量接口拆细,交给调用方自己去组合。

    这句话表明一个接口就是做一件事的,因此我们在设计接口的时候要明确接口的作用,且作用不能广泛,应该清晰而单一。

    2.业务的独立性,尽量对外屏蔽你的业务细节,同时也对调用者更加友好。

    接口是提供给用户调用的,因此最好将接口的信息隐藏,而用户不必知道接口内的具体代码实现。而在本次结对项目中,我们的接口用户就是自己,尽管如此,我们也应该将它的具体实现隐藏起来,因此我们最终会将接口做成一个core.jar包。

    3.拼写要准确,接口函数一旦发布就不能改了。函数最好是动宾结构doSomething,如openFilesetName站在使用者的角度去思考,API设计也要讲究用户体验

    这些是接口命名的规范,接口的命名要能一眼就看出它的作用,这样会有更好的用户体验。并且在设计接口时,要从使用者的角度出发,如果这个接口让用户觉得使用起来很复杂,那么这就是个失败的接口了。

 

四、计算模块接口的设计与实现过程

    在设计接口前,我先分析了下这个接口的作用:产生一定数量的符合条件的式子。因此这个接口需要接收关于数量和条件限制的参数,分别是以下6个参数:题目数量n,数值下界downBound,数值上界upBound,最大运算符数largeOperatorCount,是否有乘除hasMulDiv,是否有括号hasBracket。

    首先,这个接口里共有2个类:Creat类和Calculator类,前者作用是产生规定数量的式子,后者的作用是判断某条式子是否符合条件。在Create类中,专门设计了一个方法generate()来接收并判断上述6个参数正确性,随后generate()方法再通过for循环调用n次createProblem()方法,createProblem()每次产生一条式子,在产生式子时createProblem()方法会调用Caculator类的algorithm()方法来预先计算这条式子的答案,由于algorithm()方法是用的调度场算法和后缀表达式求值,因此可以判断当前式子在计算过程中是否会产生小数、负数等,以此来达到筛选符合条件的式子的作用。这个接口最终返回的是一个装满符合条件式子的字符串数组。最后再考虑到接口信息隐藏的特性,将这两个类封装成了一个core.jar包,然后就可以直接调用了。core.jar内部结构示意图:

         

    实现的关键在于generate()接收的6个参数在以上各个方法之间的传递,如generate()调用createProblem()时会将6个参数都传过去,随后createProblem()根据参数进行条件判断产生相应的式子;在createProblem()调用algorithm()时,会将数值下界downBound和上界upBound传过去以筛选计算过程和最终结果都在数值范围内的式子。

   

五、计算模块接口部分的性能改进

    起初我的计算模块只是稍微改了一下第一次个人作业的代码,多接收了几个参数而已。后来发现当限制的数值范围比较小的时候,控制台报栈溢出异常。我去网上搜才发现是递归调用过度,线程已满导致程序崩溃。后来我检查我的代码的确好几处都用了递归,比如当前生成的式子不满足条件时就再递归调用生成式子的createProblem方法,再如运算符下标数组只要全部一样(即式子的运算符全一样)我也会再递归调用index()方法重新生成一个下标数组,总之多处用到了递归。当数值范围比较小时,生成的式子大多是不满足条件的,于是会频频递归产生新式子,当运算符个数比较少时,下标数组也很容易一样,会频繁递归调用index()方法,最终导致程序跑不了。

    在知道是递归调用过度的原因后,根据报错信息我得知index()方法是程序中消耗最大的函数,于是我修改了index()方法,使其不使用递归。修改思路:当下标数组的前n-1个都一样时,第n个一定与前n个不一样,这样就保证了下标数组至少有2个不同,即保证了一条式子至少有2中运算符。经过这次栈溢出异常后,我明白了递归要慎用,虽然它简单,但它及可能会拖慢程序速度或使程序崩溃,我也是第一次意识到代码性能分析的重要性。下面是效能分析的图。

 

六、计算模块部分单元测试展示

    我是用junit进行单元测试的。测试的类有Command类和Creat类

    Command类负责接收命令行的参数,然后再调用Creat类产生式子。测试的函数有主方法main()。构造测试数据时主要依据还没被测到的代码,哪一行代码还未被执行,就输入那行代码相应的异常参数。将各种参数异常的情况都测一遍,代码覆盖率就会逐步提高。Command类最终的代码覆盖率达到了97%:

Command类测试代码:

import org.junit.Before;import org.junit.Test;public class CommandTest {    @Before    public void setUp() throws Exception {        Command command = new Command();    }    @Test    public void main() throws Exception {        String[] args = {"-n","10","-m","1","999","-o","9","-c","-b"};        Command.main(args);        String[] args1 = {"-n","10","-m","1","999","-o","9"};        Command.main(args1);        String[] args2 = {"-n","10","-m","1","999"};        Command.main(args2);        String[] args3 = {"啦啦啦"};        Command.main(args3);        String[] args4 = {"-n","10","-m","110","999","-o","9"};        Command.main(args4);        String[] args5 = {"-n","10","-m","1","99999","-o","9"};        Command.main(args5);        String[] args6 = {"-n","10","-m","99","60","-o","9"};        Command.main(args6);        String[] args7 = {"-n","0","-m","99","60","-o","9"};        Command.main(args7);        String[] args8 = {"-n","a","-m","99","60","-o","9"};        Command.main(args8);        String[] args9 = {"-n"};        Command.main(args9);        String[] args10 = {"-n","3","-m","60","-o","9"};        Command.main(args10);        String[] args11 = {"-n","3","-m"};        Command.main(args11);        String[] args12 = {"-o","-2","-m"};        Command.main(args12);        String[] args13 = {"-o"};        Command.main(args13);        String[] args14 = {"-o","b"};        Command.main(args14);        String[] args15 = {"-m","1","999","-o","9"};        Command.main(args15);        String[] args16 = {"-n","10","-m","1","999","-o","9","-b"};        Command.main(args16);        String[] args17 = {"-n","10","-o","9","-c","-b"};        Command.main(args17);    }}

 

    Creat类负责接收Command类传来的各种条件参数,产生符合的题目数组。测试的函数有generate(),createProblem(),index(),构造测试数据时主要是按照函数的参数列表,根据参数个数和类型构造合适的测试数据。起初代码覆盖率只有70%多,原因是很多if语句没被执行,后来我又构造满足if判断条件的参数进行测试,代码覆盖率便逐步提升。最终我把各种if语句的条件都测试到了,代码覆盖率达到了100%:

    Creat类测试代码:

import org.junit.Before;import org.junit.Test;import static org.junit.Assert.*;public class CreateTest {    private Create create;    @Before    public void setUp() throws Exception {         create = new Create();    }    @Test    public void generate() throws Exception {        create.generate(10,10,100,3,true,true);        create.generate(10,100,10,12,true,true);        create.generate(10,10,1000,12,true,true);        create.generate(10,1,10000,8,true,true);        create.generate(10,101,1000,8,true,true);        create.generate(0,1,1000,8,true,true);        create.generate(10,1,1000,8,true,true);        create.generate(10,1,1000,8,false,true);        create.generate(10,1,1000,8,true,false);        create.generate(10,1,1000,8,false,false);    }    @Test    public void createProblem() throws Exception {        create.generate(10,1,1000,8,true,true);        create.generate(10,1,1000,8,false,true);        create.generate(10,1,1000,8,true,false);        create.generate(10,1,1000,8,false,false);    }    @Test    public void index() throws Exception {        create.index(3,4);    }}

 

 

七、计算模块部分异常处理说明   

    计算模块共需接收的参数有6个,异常都围绕这6个参数展开。

   1.关于题目数量n的参数异常有3种:数量范围越界、未输入题目数量和非法字符输入。

 

case "-n": {                    try {                        n = Integer.parseInt(args[i + 1]);                        if (n < 1 || n > 10000) {                            System.out.println("对不起,题目数量只能是1-10000!");                            return; //结束运行                        }                    } catch (ArrayIndexOutOfBoundsException e) { //未输入数字时args[i+1]会数组越界                        System.out.println("未输入题目数量!");                        return;                    }catch (NumberFormatException e) { //输入非数字字符等                        System.out.println("对不起,题目数量只允许输入1-10000的数字!");                        return; //结束运行                    }                    break;                }

 

    单元测试样例:

@Test    public void main() throws Exception {        String[] args7 = {"-n","0","-m","99","60","-o","9"};        Command.main(args7);        String[] args8 = {"-n","a","-m","99","60","-o","9"};        Command.main(args8);        String[] args9 = {"-n"};    }

    2.关于数值上下界的异常有4种:数值下界大于上界、数值上下界没有在相应规定的范围、未输入数值上下界、非法字符输入。

case "-m": {                    try {                        downBound = Integer.parseInt(args[i + 1]);                        upBound = Integer.parseInt(args[i + 2]);                        if (downBound >= upBound) {                            System.out.println("数值下界不能大于等于上界!");                            return;                        } else if (downBound < 1 || downBound > 100 || upBound < 50 || upBound > 1000) {                            System.out.println("数值下界只能是1-100,数值上界只能是50-1000!");                            return;                        }                    } catch (ArrayIndexOutOfBoundsException e) {                        System.out.println("未分别输入数值上下界!");                        return;                    }catch (NumberFormatException e) {                        System.out.println("对不起,数值上下界只允许数字格式!");                        return;                    }                    break;                }

    单元测试样例:

@Test    public void main() throws Exception {        String[] args6 = {"-n","10","-m","99","60","-o","9"};        Command.main(args6);        String[] args11 = {"-n","3","-m"};        Command.main(args11);        String[] args4 = {"-n","10","-m","110","999","-o","9"};        Command.main(args4);        String[] args18 = {"-n","10","-m","hhh","kkk","-o","9"};        Command.main(args18);

    3.关于最大运算符数量的异常3种:不在规定范围、未输入、非法输入

case "-o": {                    try {                        largeOperatorCount = Integer.parseInt(args[i + 1]);                        if (largeOperatorCount < 1 || largeOperatorCount > 10) {                            System.out.println("对不起,运算符数量只能是1-10!");                            return;                        }                    } catch (ArrayIndexOutOfBoundsException e) {                        System.out.println("未输入最大运算符数量!");                        return;                    }catch (NumberFormatException e) {                        System.out.println("对不起,运算符数量只允许输入1-10的数字!");                        return;                    }                    break;                }

    单元测试样例:

 

@Test    public void main() throws Exception {        String[] args12 = {"-o","-2","-m"};        Command.main(args12);        String[] args13 = {"-o"};        Command.main(args13);        String[] args14 = {"-o","b"};        Command.main(args14);    }

 

4.什么相关的参数都没输,只输非法字符等:

if (n == 0 && downBound == 0 && upBound == 0) {            System.out.println("参数格式有误!");            return;        } else if (n == 0) {            System.out.println("未输入题目数量!");            return;        } else if (downBound == 0 || upBound == 0) {            System.out.println("未分别输入数值上下界!");            return;        }

    单元测试样例:

 

@Test    public void main() throws Exception {        String[] args3 = {"啦啦啦"};        Command.main(args3);    }

 

 

八、界面模块的详细设计过程

    本次项目是运用jsp+servlet做的四则运算网页版,jsp负责视图层,servlet负责逻辑业务处理。因此前端界面都是在jsp写的(结合了html\css\javaScript)。由于实现了附加功能的多个用户并能查看答题历史记录,因此会有用户登录注册、修改密码、查看历史答题等页面。

1.用户登录界面:有判空、识别用户不存在和密码错误等功能。界面采用了小学生喜欢的风格。

2.注册页面:有判断用户名已存在、两次密码输入不一致的功能。

3.出题大厅界面:用户在此可以设定出题条件。文本框采用了html5的number类型,保证用户只能输入限定范围的数字。

题目数量:数值范围: -

 

4.点击开始出题,会跳到result.txt文件下载页面:

5.点击下载:

6.下载完后,点击开始做题,进入答题界面:此页面有计时器。

7.答完题后,点击提交,进入成绩反馈界面:会显示答对多少题,答错多少题,所用时长,以及错题卡片(包含错题与正确答案),可供孩子巩固复习。

8.随后点击查看历史答题记录,进入历史答题界面:可以看到刚刚的答题已经呈现在历史列表里。此页面会按时间先后顺序展示以往的答题记录,并会显示历史最佳成绩,还可显示该练习的类型是系统产生还是用户自己上传的。

9.点击其中一条答题记录,查看该次练习的详情。点击橙色下拉按钮可查看具体答题记录(做对和做错的题都显示)。再点击按钮可收起,此效果由javascript实现。

 

10.点击侧栏“上传题目”,用户可上传自己的题目进行答题:这里有个文件格式限制,若上传正确格式文件,会出现一个开始答题按钮;若上传错误格式的文件开始答题的按钮是不会出现的。此效果是通过javascript识别后台传来el表达式的值实现的。

 

11.点击开始答题就会进入答题界面,题目内容是用户自己上传的题。界面与上述答题界面一样就不展示啦。

12.修改密码界面:要求输入原密码与新密码。有原密码输入错误判断,有一定安全性。

13.点击退出账号,退回登录页面并显示退出成功。

 

 

九、界面模块与计算模块的对接

    界面模块与计算模块的对接即jsp与servlet的对接。由于计算模块已经封装好了,servlet只需调用计算模块的Create类产生题目,将答案存于另一个数组中,并将题目置于ArrayList中利用request.setAttribute()传给jsp,在jsp中利用jstl标签<c:foreach>循环列出。用户答完题后通过Form表单将答案传给servlet,servlet用一个数组接收用户答案,再将用户答案数组与正确答案数组对比,即可判断对错。

    相关代码如下:

 

/**     * 生成题目     * @param request     * @param response     * @throws ServletException     * @throws IOException     */    private void create(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException    {        int n = Integer.parseInt(request.getParameter("n"));        int downBound = Integer.parseInt(request.getParameter("downBound"));        int upBound = Integer.parseInt(request.getParameter("upBound"));        int MulDiv = Integer.parseInt(request.getParameter("hasMulDiv"));        int Bracket = Integer.parseInt(request.getParameter("hasBracket"));        int largeOperatorCount = Integer.parseInt(request.getParameter("largeOperatorCount"));        RequestDispatcher rd;        if(downBound>=upBound){            request.setAttribute("msg","数值下界不能大于上界!");            rd = request.getRequestDispatcher(WebContents.MAIN);            rd.forward(request,response);        }        boolean hasBracket = false;        boolean hasMulDiv = false;        if(Bracket==1){            hasBracket=true;        }        if(MulDiv==1){            hasMulDiv=true;        }        //将生成的result.txt放在web/file下        Create create = new Create();        String[] result = create.generate(n,downBound,upBound,largeOperatorCount,hasMulDiv,hasBracket);        //将题目(不含答案)存入session        String[] question = new String[n];        for(int i=0;i
list= readFile.getFileContent(file,n); request.setAttribute("list",list); //将读的文件内容赋给list,给前台遍历 request.setAttribute("n",n); RequestDispatcher rd; rd = request.getRequestDispatcher(WebContents.BEGIN); rd.forward(request,response); } /** * 检查用户答案 * @param request * @param response * @throws ServletException * @throws IOException */ private void check(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException { String timelong = request.getParameter("timelong"); System.out.println(timelong); int n = Integer.parseInt(request.getParameter("n")); String[] userAnswer = new String[n]; for(int i=0;i
feedBackList = new ArrayList<>(); //答错的题+正确答案 for(int i=0;i

 

    由于实现了用户功能,所以此次项目用到了数据库,数据库中有2张表——用户表和练习题表。

                             

 

        整个项目结构如下:

                          

其中entity是实体,dao层负责数据库的连接实现数据的增删改查,servlet负责业务逻辑处理,sql封装了所有用到的sql语句,util是servlet需要用到的的一些工具类,test放着所有的测试类(包括增删改查方法测试、计算模块Create类测试、命令行测试),web的WEB-INF下放着jar包和jsp等。

   

十、结对过程描述

    我的结对小伙伴是邓旭,由于她要参加省运会,因此上周她都在进行高强度的训练并参加最终的比赛,没有时间和我一起做项目啦,所以这个项目大部分是我完成的。好在我们提早商量一致决定做网页版,确定目标后首先开始修改上一次作业的计算模块,随后就开始定制网页版的需求,设计数据库开始写jsp和servlet.整个项目制作持续了一个多星期。邓旭比赛回来后,项目也正好做完,她很积极主动地找我想看看这个项目,于是我就详细地将整个项目的实现过程向她介绍了一遍,从数据库的设计、到代码底层与数据库的连接再到jsp与servlet的交互,在给她讲的过程中我对整个项目的结构与实现更加清晰了,她也会时不时地询问一些细节的问题比如界面的友好设计,有问题的地方我就会进行相应的修改,相当于代码复审了。

    项目确定最终版本后,我们将项目放到了自己的阿里云服务器上,由于是第一次接触云服务器,将这个项目放上去可谓是一波三折,在购买云主机、配置云主机上就花了很多时间、查阅了很多博客。将项目放到云主机后,起初加载不出图片和样式,查了很久最后发现是路径的问题。当我们在自己的浏览器通过url地址成功访问项目时,我们心中的喜悦是无与伦比的。最后,我们两个一起完成了博客。

               

十一、结对编程的优缺点

      优点:我觉得结对编程的优点在于2个人会互相督促,加快项目进度,而且因为都不想拖后腿,所以态度会很积极。当遇到困难时,两个人共同面对会使心理压力小很多,会降低对bug和其他技术困难的畏惧。由于结对编程时每一行代码都由两个人思考过,因此出错和修改代码的几率会小很多,从而提高编程效率。最重要的是,结对编程能够加深两个人的友谊,觉得对方就是与自己共患难的战友。

    缺点:结对编程最大的缺点就是两个人的工作量不一致,很可能会出现一人奋斗、一人打酱油的局面。如果在很多小问题上没沟通好两人容易发生矛盾。

    我对邓旭的评价:优点是态度很积极,而且很谦虚,会倾听我的建议,总是给人带来正能量,会鼓励支持我。缺点就是这次去参加比赛,没能和我一起敲大部分的代码,不过这也没办法呀,省运会比较重要,我很理解~总之这次与邓旭一起的结对项目还是比较愉快的!

 

十二、PSP

 

SP2.1

任务内容

计划共完成需要的时间(h)

实际完成需要的时间(h)

Planning

计划

74.5

108.5

·        Estimate

·   估计这个任务需要多少时间,并规划大致工作步骤

1

1

Development

开发

67.5

98.5

·        Analysis

·         需求分析 (包括学习新技术)

3

5

·        Design Review

·         代码设计

4

5

·        Coding Standard

·         代码规范 (为目前的开发制定合适的规范)

0.5

0.5

·        Design

·         具体设计

3

5

·        Coding

·        在计算模块花费的时间

          在UI模块花费的时间

          在后台处理模块花费的时间

15

10

30

20

10

40

·        Code Review

·         代码复审

2

3

·        Test

·         测试(自我测试,修改代码,提交修改)

2

10

Reporting

报告

6

9

·         Test Report

·         测试报告

5

8

·         Size Measurement

·         计算工作量

0.5

0.5

·         Postmortem & Process Improvement Plan

·         事后总结, 并提出过程改进计划

0.5

0.5

 

十三、总结

    为了完成这次项目,我过去一个多星期的时间除了吃饭睡觉上课,其余的每分每秒几乎都花在了这个项目上。过程虽然很辛苦,但收获确实是很大,我学到了很多新的东西。感触比较深的是对javascript这门语言的认识了,我是个做javaweb后端的,以前没怎么写过js,但是这次项目组没有前端的小伙伴,所以我就自己写前端页面了,在写的过程中会遇到很多前台页面的事件处理,我想实现某个功能可我不会写相应的前台代码,所以我就去百度搜,就这么一步步认识了javascript,它是门很棒很方便的语言。其中在将前台用户的答案传给后台时js帮了我很大的忙,未知的多个循环出的input框要判空后才能传给后台,这就是利用js实现的。

    还有一项感触比较深的要属云服务器的配置了,以前从来没接触过。这次我用的是阿里云,果然实际操作和想象中的一样难,对于我这个服务器小白来说把项目放在云主机的过程十分地艰辛。但是这个过程让我受益匪浅,我知道了什么是云服务器,它有个云主机,就像我们普通的电脑一样可以远程操控,真的是个神奇的东西,十分佩服开发云服务的团队。项目完成后将其打成war包放在云主机tomcat的webapps下,再启动tomcat就可以在其他电脑通过url访问自己的项目了!

    最后,做完这次项目我有一种更上一层楼的感觉,真的学到了很多,内容很丰富,也尝试了很多从未尝试过的东西,这些东西给我带来惊喜和快乐。学习就是一件痛苦并快乐的事情。我始终坚信这么简单的一句话:世上无难事,只怕有心人。加油!

   

转载于:https://www.cnblogs.com/hiwhz/p/8768293.html

你可能感兴趣的文章
iOS中通讯录的开发
查看>>
怎么让table中的<td>内容向上对齐
查看>>
[Java] 遍历HashMap和HashMap转换成List的两种方式
查看>>
mongodb
查看>>
LeetCode 46. Permutations
查看>>
jmeter- 性能测试3:聚合报告(Aggregate Report )
查看>>
JavaScript高级程序设计---学习笔记(二)
查看>>
vim 插件的学习
查看>>
Uncaught SyntaxError: Unexpected token ILLEGAL
查看>>
一个预处理定义的问题
查看>>
ANDROID L——Material Design综合应用(Demo)
查看>>
自我介绍以及关于软件工程的问题
查看>>
struts (一)
查看>>
【新番推荐】工作细胞
查看>>
NYOJ 16 矩形嵌套
查看>>
Leetcode中的SQL题目练习(二)
查看>>
dubbo 集群容错源码
查看>>
Collection接口的子接口——Queue接口
查看>>
LINUX安装NGINX
查看>>
服务器启动项目抛错 没有到主机的路由
查看>>