2016012032四则运算网页版结对项目报告

一、测试的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类型,保证用户只能输入限定范围的数字。

题目数量:<input placeholder="1-10000" type="number" min="1" max="10000" name="n"/>
数值范围:<input placeholder="1-100" type="number" min="1" max="100" name="downBound"> -
<input placeholder="1-100" type="number" min="50" max="1000" name="upBound">

 

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

5.点击下载:

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

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

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

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

<script language="javascript" type="text/javascript">
    function aaa(){
        if(document.getElementById("contentTable").style.display == 'none')
        {document.getElementById("contentTable").style.display='';}
        else
        {document.getElementById("contentTable").style.display='none';}
    }
</script>

 

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

<script type="text/javascript">
    var msg = "${msg}";
    if(msg=='题目上传成功!'){
        document.getElementById('begin').style.display="inline-block";
    }else {
        document.getElementById('begin').style.display="none";
    }
</script>

 

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<n;i++){
            int index = result[i].indexOf("=");
            question[i]=result[i].substring(0,index);
        }
        request.getSession().setAttribute("question",question);

        //将正确答案存入session
        String[] realAnswer = new String[n];
        for(int i=0;i<n;i++){
            int index = result[i].indexOf("=");
            realAnswer[i]=result[i].substring(index+1);
        }
        request.getSession().setAttribute("realAnswer",realAnswer);


        MakeFile2 makeFile2 = new MakeFile2();
        String path = request.getSession().getServletContext().getRealPath("file")+"/";
        File f = new File(path); //文件夹也是个文件
        if(!f.exists()){
            f.mkdirs();  //如果该文件夹不存在,则创建该文件夹
        }
        String fileName = UUID.randomUUID()+"result.txt";
        File file = makeFile2.creatFile(result,path+fileName);

        //将该练习存入数据库
        String relativePath = "../../../file/"+fileName;
        User user = (User) request.getSession().getAttribute("user");
        int userId = user.getId();
        String creatType = "系统产生";
        PracticeDao practiceDao = new PracticeDaoImpl();
        Practice practice = new Practice(relativePath,creatType,userId);
        practiceDao.insertPractice(practice);
        Practice practice1 = practiceDao.selectPracticeByPath(relativePath);
        request.getSession().setAttribute("practiceId",practice1.getId());

        request.setAttribute("filepath",path+fileName);
        request.getSession().setAttribute("file",file);
        request.setAttribute("n",n);

        rd = request.getRequestDispatcher(WebContents.DOWNLOAD);
        rd.forward(request,response);
    }

/**
     * 开始答题(返回题目内容)
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void begin(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {
        File file = (File)request.getSession().getAttribute("file");
        int n = Integer.parseInt(request.getParameter("n"));
        ReadFile readFile = new ReadFile();
        List<Problem> 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<n;i++){
            userAnswer[i] = request.getParameter("s"+i); //获得用户答案
        }

        String[] question = (String[]) request.getSession().getAttribute("question");
        String[] userContent = new String[n];
        for(int i=0;i<n;i++){
            userContent[i]=question[i]+"="+userAnswer[i];
        }

        String[] realAnswer = (String[])request.getSession().getAttribute("realAnswer");

        int correctCount=0;
        int wrongCount=0;

        List<FeedBack> feedBackList = new ArrayList<>(); //答错的题+正确答案

        for(int i=0;i<n;i++){
            if(!userAnswer[i].equals(realAnswer[i])){
                wrongCount++;
                String a = userContent[i];
                String b =  realAnswer[i];
                FeedBack feedBack = new FeedBack(a,b);
                feedBackList.add(feedBack);
            }else correctCount++;
        }


        String[] wrongProblem = new String[feedBackList.size()];
        String[] correctAnswer = new String[feedBackList.size()];
        for(int i=0;i<feedBackList.size();i++){
            wrongProblem[i] = feedBackList.get(i).getWrongUserContent();
            correctAnswer[i] = feedBackList.get(i).getCorrectAnswer();
        }


        //将此次练习信息存入数据库
        int practiceId = (int)request.getSession().getAttribute("practiceId");
        Practice practice = new Practice(practiceId,Arrays.toString(wrongProblem),Arrays.toString(correctAnswer),Arrays.toString(userContent),correctCount,wrongCount,timelong);
        PracticeDao practiceDao = new PracticeDaoImpl();
        practiceDao.updatePractice(practice);

        request.setAttribute("correctCount",correctCount);
        request.setAttribute("wrongCount",wrongCount);
        request.setAttribute("timelong",timelong);
        request.setAttribute("feedBackList",feedBackList);

        RequestDispatcher rd;
        rd = request.getRequestDispatcher(WebContents.FEEDBACK);
        rd.forward(request,response);

    }

 

    由于实现了用户功能,所以此次项目用到了数据库,数据库中有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访问自己的项目了!

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

   

posted on 2018-04-10 08:16  -亲爱的缤纷筒  阅读(425)  评论(2编辑  收藏  举报