zoukankan      html  css  js  c++  java
  • 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:UserswanghDesktop2016012032partnerWorksrc
    (注意根据自己放的位置调整路径,下同)

    2.编译command.java和core.jar:
    javac -cp C:UserswanghDesktop2016012032partnerWorkwebWEB-INFlibcore.jar -encoding utf-8 C:UserswanghDesktop2016012032partnerWorksrcCommand.java
    (-cp表示路径,core.jar包放在web的WEB-INF的lib文件夹下)

    3.运行command.java和core.jar,并输入参数:
    java -cp ..webWEB-INFlibcore.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写的(结合了htmlcssjavaScript)。由于实现了附加功能的多个用户并能查看答题历史记录,因此会有用户登录注册、修改密码、查看历史答题等页面。

    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访问自己的项目了!

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

       

  • 相关阅读:
    项目管理改进实践
    Abount StoneAge Dictionary Project
    词库引擎核心设计
    JDOM / XPATH编程指南
    2007年图灵奖揭晓
    pjscrape: A webscraping framework written in Javascript, using PhantomJS and jQuery
    centos install node.js
    Posts tagged pyqt4
    install python262 写得不错
    WebSPHINX: A Personal, Customizable Web Crawler
  • 原文地址:https://www.cnblogs.com/hiwhz/p/8768293.html
Copyright © 2011-2022 走看看