结对项目刘畅2016012040

一、Coding地址

 https://git.coding.net/liuc144/GUIhomework.git

二、PSP

PSP

任务内容

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

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

Planning

计划

0.2

1

·        Estimate

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

0.2

1

Development

开发

36.5

66

·        Analysis

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

1

4

·        Design Spec

·         生成设计文档

0.1

2

·        Design Review

·         设计复审 (和同事审核设计文档)

1

2

·        Coding Standard

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

0.1

1

·        Design

·         具体设计

10

12

·        Coding

·         具体编码

20

15

·        Code Review

·         代码复审

3

10

·        Test

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

1

20

Reporting

报告

3

10

·         Test Report

·         测试报告

0.5

6

·         Size Measurement

·         计算工作量

0.5

2

·         Postmortem & Process Improvement Plan

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

2

2

   

三、封装性、接口设计、耦合性

  1、Information Hiding

  百度对Information Hiding的解释如下:

  信息隐藏是结构化设计与面向对象设计的基础。在结构化中函数的概念和面向对象的封装思想都来源于信息隐藏。软件业对这个原则的认同也是最近十年的事情。      David Parnas在1972年最早提出信息隐藏的观点。他在其论文中指出:代码模块应该采用定义良好的接口来封装,这些模块的内部结构应该是程序员的私有财产,外部是不可见的。      Fred Brooks在《人月神话》的20周年纪念版中承认了当时自己对Parnas的批评是错误的。他说道:“我确信信息隐藏--现在常常内建于面向对象的编程中--是唯一提高设计水平的途径”。以下列举了一些信息隐藏原则的应用。    

  1 多层设计中的层与层之间加入接口层; 

     2 所有类与类之间都通过接口类访问;

     3 类的所有数据成员都是private,所有访问都是通过访问函数实现的.

  我们是详细按照该原则敲写代码的

  

  如图所示,该项目的核心模块Core封装良好,控制合法的输入输出,命名规范。

   2、Interface Design

  

   由于该项目再设计之初就进行了良好的接口设计,所以代码的模块化很明显。

   3、 Loose Coupling

  

  耦合性是编程中的一个判断代码模块构成质量的属性,不影响已有功能,但影响未来拓展,与之对应的是内聚性。

  耦合性:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。

  内聚性:又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。

因此,现代程序讲究高内聚低耦合,即将功能内聚在同一模块,模块与模块间尽可能独立,互相依赖低。没有绝对没有耦合的模块组,只有尽量降低互相之间的影响。使模块越独立越好。

  所以我们设计项目之初就做好了模块化设计。

  

  如文件目录显示一样,逻辑清晰,结构明显。其中CommandTest是单元测试,生成代码覆盖率的文件。

  Command是用于接受String[] args字符串数组命令,并识别命令,然后输出result.txt文件。

  Core是用于接受参数,生成表达式,通过数组传到外面。

  ToSuffix是解答表达式,给与一个中缀表达式,传回去它的值。

  三模块稳定健壮,可以在设计额外功能的时候,添加模块并互不影响。部分接口如图所示。

  

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

  

 目录中各类的调用关系如图所示,简单清晰。

 

  Command.java,负责通过命令行输入的字符串数组进行识别,通过Switch判断命令是否合法(比如,命令太短或则命令太长,命令本应小写却大写,等等),通过Core获取题目后,依据文件输入输出流将题目输出到result.txt文件里。

  Core.java 负责接受参数然后生成算术题。

  其算法核心在于生成合法的算术题(字符串),核心流程很简单,就是算术题=数字+操作符+数字+操作符...

  如何生成数字,Math.random(),其中数字大小,靠传进来的参数,lowerBoundNumber和upperBoundNumber控制。

  如何生成操作符,Operator[Math.random()],通过一个存储了操作符的字符串常量数组里随机控制下标来随机生成操作符。

  括号是附加功能,可完全不影响核心算法的实现。

    其代码如下,简单易懂.

  ToSuffix.java负责解答中缀表达式

  其算法思想就是将中缀表达式转换为后缀表达式,然后通过一系列栈运算将后缀表达式的值求出。

  其中,可以很简单的限制中间结果的范围.

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

  这里深刻的感谢助教刘乾老师,他不厌其烦的回答我的问题,让我领悟到大幅度改进性能的方法.

  

  我在优化性能这个地方大概花了7、8个小时,不断的deBug和进行单元测试,始终不得眉头,我知道哪部分出了问题——难以生成携带乘除法的算术题,后来在生成一些特殊的算术题时,我对数字随机性进行一些限制。比如,当算术题中出现除法的时候,它的除数就不应该是简单的随机数,而应该是除数的因子,这样,大幅度的提高生成正确算术题的效率。同样的思想适用与乘法。

  在这里贴下关键代码

性能分析:

通过JProfiler,进行性能分析。

性能分析总图:包括磁盘利用率,内存使用,CPU和线程概况。

 

性能分析过程:

 

F4后,发现内存使用率比较高,只有一部分没有使用,而且即使文档都关闭了。

 

我们继续对代码进行完善,最后内存占用率几乎100%.

 

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

  我设计了很多测试,一共二十余种,其中一些测试可以合并。

    编写测试用例的思路是:正常命令

               非正常命令

                  非正常命令包括,输入值越界,忘记输入数字(例如,只有-n),大小写错误等等

    这里列举了部分测试,更详细的请看Coding里的代码.

   

 测试结果如下

  

  部分未覆盖的代码经过详查是一些提示代码和未来可拓展的代码

  可扩展的代码虽然对现在的项目没有用,但是会对未来可能产生的一些拓展功能方便嵌入。

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

  控制台print给出错误出现场景,以下各种错误都在单独的CommandTest里用例,助教检查时运行一下就好.

  1、对n(题目的数量)进行范围限制,出现异常报错并提示用户相关信息.x

  错误用例

  错误提示

  2、对随机数的下界进行范围限制,出现异常报错并提示用户相关信息.

错误用例:

错误提示

  3、对随机数的上界进行限制,出现异常报错并提示用户相关信息.

错误用例:

错误提示:

  4、对是否有乘除法进行限制,出现异常报错并提示用户相关信息.

错误用例:

错误时的提示信息

 

  5、对操作符的数量进行限制,出现异常报错并提示用户相关信息.

错误用例

错误提示

除此之外:还有一些是识别命令是否输入正确的异常,不过在Command模块里

  1、命令长度非法

  

  2、-n后面没有数据

  

  3、m后面数据输入错误

  

  4、程序运行时间过长

  

  结合

  

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

界面有主部分和副部分,完成之后的界面如图:

 

  主部分是:

  UI.java,JFrame框架,承载其它面板,我将面板作为实现功能的地方,这样以后想添加什么其它功能的时候方便添加,我认为这一地方的设计极大的增强了扩展性,同时遵循了模块化的原则.

  副部份是:  

  UI_cmd.java,我通过绝对定位布局

  部分代码如下

  

  

  绝对定位是一种很简单的布局方法,由于我GUI不太熟练,所以我用的绝对定位,以后有功夫会学一学其它布局方式.

  布局很简单,稍微复习下GUI然后依据绝对定位就可以设置一个简单的界面

  难的是监听器的填写.

  我设计的是当你开始出题的时候,就显示做题界面

  然后就可以开始做题

  做题结束会告诉你花费的时间和做对的题数

  或者你可以上传题目

然后点击做题,同样可以做题。

这是一个监听器的编写样例.

preToDo.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// TODO
time = Calendar.getInstance().getTimeInMillis();
if(url.equals(""))
url="result.txt";
File file = new File(url);

if (!file.exists()) {
JOptionPane.showMessageDialog(null, "出错了", "您需要先上传题目", JOptionPane.ERROR_MESSAGE);
;
return;
}
labelRight.setEnabled(false);
labelRight.setVisible(false);
answer.setEnabled(false);
answer.setVisible(false);
preToDo.setVisible(false);
preToDo.setEnabled(false);
finish.setVisible(true);
finish.setEnabled(true);
nextOne.setEnabled(true);
nextOne.setVisible(true);
upLoadQuestion.setVisible(false);
upLoadQuestion.setEnabled(false);
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
String tempString = null;
int line =1;
// 一次读入一行,直到读入null为文件结束
arr[0]="2016012040";
while ((tempString = reader.readLine()) != null) {
// 显示行号
if(tempString.equals("2016012040"))
continue;

arr[line] = "第" + line + "道题:" + tempString;
line++;
}
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
});

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

  我设计的对接模式非常简单。

  通过获得界面参数生成一个字符串数组,然后传入Command.java的主方法

preToCore.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO
                labelTime.setEnabled(false);
                labelTime.setVisible(false);
                labelRight.setEnabled(false);
                labelRight.setVisible(false);
                answer.setEnabled(false);
                answer.setVisible(false);
                String[] args = new String[9];
                try {
                    int count = 0;
                    int operator=1;
                    int n = Integer.parseInt(numberOfProblems.getText());
                    if(!numberOfOperators.getText().equals(""))
                     operator = Integer.parseInt(numberOfOperators.getText());
                    int lowerBoundNumber = Integer.parseInt(lowerBound.getText());
                    int uppperBoundNumber = Integer.parseInt(upperBound.getText());
                    Boolean isBracket = isBracketBox.isSelected();
                    Boolean isEasy = isEasyBox.isSelected();

                    if (n < 1 || n > 10000 || operator < 1 || operator > 10 || lowerBoundNumber > uppperBoundNumber
                            ||  lowerBoundNumber < 1 || lowerBoundNumber > 100 || uppperBoundNumber < 50
                            || uppperBoundNumber > 1000)
                        throw new Exception();

                    args[count++] = "-n";
                    args[count++] = n + "";
                    args[count++] = "-m";
                    args[count++] = lowerBoundNumber + "";
                    args[count++] = uppperBoundNumber + "";
                    if (!numberOfOperators.getText().equals("")) {
                        args[count++] = "-o";
                        args[count++] = operator + "";
                    }

                    if (isBracket)
                        args[count++] = "-b";
                    if (isEasy)
                        args[count++] = "-c";
                    for (int i = 0; i < 9; i++)
                        if (args[i] == null)
                            args[i] = "NotExit";
                    Command.main(args);
                    JOptionPane.showMessageDialog(null, "成功生成文件", "亲爱的学生您好:", JOptionPane.ERROR_MESSAGE);
                } catch (Exception e2) {
                    // TODO: handle exception
                    e2.printStackTrace();
                    JOptionPane.showMessageDialog(null, "出错了", "请重新设置参数", JOptionPane.ERROR_MESSAGE);
                }
            }
        });

软件可完成的功能:

  1、出题功能:

  

   2、做题功能

  

  3、上传题目功能

  4、验算并记录时间和做对题数功能

除此之外还有强大的用户友好性

光是编写用户友好性就比我实际编写核心算法的时间要长,大概花了有三十个小时.

十、描述结对的过程

  

结对编程其实不是件容易的事情,你从一个人怎么高兴怎么编程,变成了需要考虑对方情绪,照顾对方,帮助对方,为对方分担压力,共享思路,修改代码的两人合作。

 十一、结对编程的好处和坏处

  结对编程的好处:

  1,在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作解决问题的能力更强。

  2,对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。

  3,在企业管理层次上,结对能更有效的交流,相互学习和传递经验,分享知识,能更好地应对人员流动。总之如果运用得当,结对编程可以取得更高的投入产出比。

  它也有很多缺点:

  1、与合不来的人一起编程容易发生争执,不利于团队和谐。

  2、经验丰富的老手可能会对新手产生不满的情绪。

  3、一山不容二虎,开发者之间可能就某一问题发生分歧,产生矛盾,造成不必要的内耗。

  4、开发人员可能会在工作时交谈一些与工作无关的事,分散注意力,造成效率低下。

  李瑞超的优点:1、做事认真细心

         2、对于分配好的任务会马上去做,而且很快就会做好.

         3、态度积极

      缺点:编码基础不是很好

  我的优点:1、快速设计好程序的框架。

       2、心思周全,有关程序的方方面面都有考虑到。

       3、对java语言比较熟悉.

       缺点:编码效率有些低。

  后记

  现在才明白,代码复查纠错,提升用户友好性,要比编写核心算法复杂太多了,果然实践出真知,以前还不明白为什么,敲代码是个细节活呀.

原文地址:https://www.cnblogs.com/anheqiao/p/8763205.html