结对作业

1)在文章开头给出Coding.Net项目地址

项目克隆地址:https://git.coding.net/Donsparks/Calculator.git

线上测试地址:http://39.105.6.214:80/hxd_war/


2)在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的开发上耗费的时间

结对作业

3)看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的

之前听说过“面向接口设计“和”面向接口编程“,经过这次结对项目,我深刻的体会到这个的意义。软件设计就是从抽象到具体的一个过程,比如从用例图设计到类图设计就是一个这样的过程。接口在java实现上,可以表现为一些set和get方法,它封装了实现的细节。

以前,我以为接口只是函数、模块之间调用的参数,返回值这样子,现在我觉得接口其实反应的是一种模块化的设计思想,也就是经常听到的“高内聚低耦合”;这种设计原则即非常有利于维护,提高系统健壮性;同时,对团队分工合作也是非常好的指导。

我们接口设计是放在用例设计和类图设计之间进行的。如果可以把接口给设计出来,我觉得这个设计就已经相当详细了,设计接口不容易,他要求设计者对整个项目有一个大致的实现思路。

我们这次作业和开始的个人项目相比,很明显我们要实现题目和答案的分离。因此我在产生式子和答案的文件中,特意设计了一个getAns方法。这个接口可以在判断最终结果是否溢出时方便的获得当前式子的答案。我个人项目的时候就没有这样设计,因为当时没有对最终结果有限制这样的要求。

下面是我们出题模块的接口设计:

结对作业

4计算模块接口的设计与实现过程。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处。

我根据需求的不同,我设计了四个类,分别是CaseNN没括号没乘除,CaseNH没括号, CaseHN有括号没乘除,和CaseHH有括号有乘除。我把这四个文件放在一个package 当中。后来,由于我验证用户答案的部分的代码对四种情况是通用的,因此我在定义上述类的实例化对象的时候,我把它的类型都统一为CaseSuper类型;因此,我后来又在package中加了一个CaseSuper类,然后让其他四个类继承CaseSuper 。(这也是我第一次切身感受到超类在设计中的作用)。

出题的算法是困扰我好久的一块。我最先写的是CaseNN这个类,由于没有乘除没有括号(为了后面叙述方便,我将这样的式子叫NN),实现起来比较容易;然后我写的是CaseNH没括号有乘除,这个我先生成一个只带乘除运算的的part,并算出结果partAns,然后调用CaseNN生成两个NN,最后用NN+part+NN这样拼成一个式子;接着是CaseHN有括号没乘除的情况,这种情况我的实现和CaseNH的类似,先产生一个带括号的part,然后在这个part前后拼接两个NN,只是要注意的就是前一个括号和part连接的符号一定要是减号,不然括号的存在不改变优先级,就失去了意义。至于CaseHH既有括号,又有乘除的情况,我是先像CaseHN那样产生一个带有括号的part,然后再调用CaseNH生成两个NH,这样用NH+part+NH得到的就是既有括号,又有乘除的式子了。

结对作业

5) 计算模块接口部分的性能改进。记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图,并展示你程序中消耗最大的函数。(4')

这部分是我搭档东哥负责的,事后他给我介绍了一下JProfiler使用,以下内容摘自他的博客:

通过一番自主学习,我采用JProfiler对代码进行性能调教和分析:

结对作业结对作业结对作业

通过上面的能效分析图,可以看出,代码的性能并非特别完美,还有改进的空间。


6)算模块部分单元测试展示。展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。只需要测试命令行部分,且该部分覆盖率到90%以上,否则单元测试部分视作无效。(6')

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class CommandTest {
    @Test
    public void main() throws Exception {
        Command command = new Command();
        String[] args1 = {"-m", "1", "100", "-n", "7"};//正确参数类型
        String[] args2 = {};//缺少参数
        String[] args3 = {"-m", "1", "100"};//缺少部分参数
        String[] args4 = {"-m", "1", "10", "-n","10"};//参数范围错误

        List<String[]> list = new ArrayList();
        list.add(args1);
        list.add(args2);
        list.add(args3);
        list.add(args4);

        for (int i = 0; i < list.size(); i++)
            Command.main(list.get(i));
    }
}

对Command类进行代码覆盖率测试,结果如下:

结对作业

7)算模块部分异常处理说明。在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。

异常处理主要体现在参数的合法性和运算结果的正确性上面,包括对生成算式的个数,运算符的个数,运算数的范围等,都做了相应的异常处理:

for(int i=0;i<args.length;i++){
    switch(args[i]){
        case "-n":{
            try{
                quesNum=Integer.parseInt(args[++i]);
            }catch(Exception ex){
                System.out.println("输入的题目数量不符合规定,请输入1-1000之间的整数");
                return;
            }
            if(quesNum<1||quesNum>10000){
                System.out.println("输入的题目数量不符合规定,请输入1-1000之间的整数");
                return;
            }
            break;
        }
        case "-m":{
            try{
                minNum=Integer.parseInt(args[++i]);
                maxNum=Integer.parseInt(args[++i]);
            }catch(Exception ex){
                System.out.println("输入的运算数范围不合理,请输入整数值");
                return;
            }
            if(minNum<1||maxNum<50||minNum>100||maxNum>1000){
                System.out.println("输入的运算数范围不合理,请将下界设置于为1-100之间的整数,上界设置为50-1000的整数");
                return;
            }
            if(maxNum<minNum){
                System.out.println("输入的运算数范围不合理,请设置上界参数大于下界参数");
                return;
            }
            break;
        }
        case "-c":ifSetArgsC=true;break;
        case "-o":{
            try{
                operNum=Integer.parseInt(args[++i]);
            }catch(Exception ex){
                System.out.println("输入的运算符个数不合理,请输入1-10之间的整数");
                return;
            }
            if(operNum<1||operNum>10){
                System.out.println("输入的运算符个数不合理,请输入1-10之间的整数");
                return;
            }
            break;
        }
        case "-b":ifSetArgsB=true;break;
    }
}

同样的,针对这些异常情况,我在测试类中设计了一些测试,从而验证这些异常的处理情况结对作业


8.界面模块的详细设计过程。在博客中详细介绍界面模块是如何设计的,并写一些必要的代码说明解释实现过程。(5')

我们这次项目界面展示的部分是由jsp中的html+css+js来实现的。我们主要就是用了经典的表格布局;这一部分我和我的伙伴都有分工;

对用户做题时间进行计时的代码是我用js写的

var sec=0;
var x=setInterval("clock()",1000);
function clock(){
    sec++;
    document.getElementById("a").value=sec;
}

function stop(x){
    window.clearInterval(x);
    alert("你共用时"+sec+"秒");
}

输入要求的出题页面:

结对作业

9.界面模块与计算模块的对接。详细地描述UI模块的设计与两个模块的对接

实现过程:

1.出题过程

用户输入的要求,是否有括号等等,都过form表单传入后台servlet,然后它根据用户传入的信息调用出题模块生成式子和答案,把式子和答案再存到一个List,这个lsit中存放的一个题目Bean,它有题目,id,正确答案,用户答案这些属性。,通过setAttribute(),把list放到session中,然后重定向到做题页面,再在做题的jsp中用<c:forEach>把题目显示出来。如果用户是通过文件上传的,就把上传的文件读到List中。

<tr>
                <td align="center" id="san">题目</td>
                <td align="center" id="si">你的答案</td>
            </tr>
            <c:forEach items="${pros}" var="pros">
                <tr>
                    <td align="right">
                    <c:out value="${pros.pro}"></c:out>=
                    </td>
                    <td>
                    <input type="text" size="5" name="s${pros.id}" >
                       
                    </td>
                </tr>
            </c:forEach>
            <tr>

这时,同时用List中题目的id 属性作为用户输入框的name.

这里有一个特别有意思的细节,就是name="s${pros.id}",这里多了一个多写了一个s;为什么加?往下看。

2.检查过程

点击提交后,后台servlet就通过getParameter()获得用户答案。可是我开始总遇到500错误,由于我不会单步调试,于是我就采用了下面这种方式(被我注释了的地方);结果发现每次获得的用户答案hisAns都是NULL.

后来,我在name="${pros.id}"中加了一个字符,这个问题就解决了。这让我想起在js中遇到的一个问题:用var存放两个整数,但是编译器并不知道这是数字还是字符,因此不能写if(a>b)这样的表达,一种解决的方式就是改写成if(a-b>0);因为0是整数,他就知道了;

这个地方估计也是同样的道理把,你得告诉编译器你写的${pros.id}是字符串

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //System.out.println("1");
        List<Problem> WrongPro=new ArrayList<Problem>(); 
        //System.out.println("2");
         List<?> pros=(List<?>) request.getSession().getAttribute("pros");
        // System.out.println("3");
         for(int k=0;k<pros.size();k++){
        //     System.out.println("4");
             Problem prox=(Problem)pros.get(k);
        //   System.out.println("5");
             String hisAns=request.getParameter("s"+k);
        //     System.out.println("6");
        //     System.out.println("hsha "+hisAns); 
             String corAns=String.valueOf(prox.getAns());
        //     System.out.println("babab "+corAns);
        //     System.out.println("7");
             if(!hisAns.equals(corAns)){
                  prox.setWrong(Integer.parseInt(hisAns));
                  WrongPro.add(prox);
                  
              //    System.out.println("zhengque"+corAns);
               //   System.out.println("youans"+hisAns);
             }
         }
        request.setAttribute("wrongPro",WrongPro); 
        RequestDispatcher requestDispatcher =request.getRequestDispatcher("wrongShow.jsp");
           requestDispatcher.forward(request,response);
        
    }

10)描述结对的过程,提供非摆拍的两人在讨论的结对照片。(1')

结对作业

11)说明结对编程的优点和缺点。同时指出结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。(5')

我觉得结对编程的成功与否关键在于前期的设计,只有设计好了(比如说上文提到的接口)每个人任务明确,工作才能高效。

结对编程优点:锻炼沟通能力,训练表达问题的能力;可以相互鼓励;两个人有时候思路更加开阔

结对编程缺点:由于前期设计做的不到位,会造成大家做一些无用功;不方便,并不是所有编程的时候两个人都坐在一起,有时候不能得到快速的回复

我的缺点:

遇到问题容易慌张,急躁

我的优点:

1.比较专注,

2.善于请教和学习,

3学习热情高

东哥的优点:

1.东哥真的是一枚大暖男,我被代码快要整疯的时候,他总是乐呵呵地鼓励我;

2.东哥遇事冷静,刚好弥补我的不足;

3.学习能力特别强

东哥的缺点:

粗心


十二、完成后实际的PSP

结对作业

相关推荐