前言:
这个结对项目做的我心力交瘁,但是更累的还是我的搭档许征航同学(黑哥),许多代码方面都是他在主导,我只是一个打下手的。在这里由衷感谢黑哥的帮助和指导。
一、Coding.net项目地址
https://git.coding.net/Agustin_Leonard_DPS/TEAM_TWO.git
二、PSP估计
花费时间(单位:h) | |
明确需求 | 0.5 |
开发 | 5 |
需求分析 | 1 |
代码规范 | 0.5 |
具体设计 | 3 |
具体编码 | 10 |
代码复审&测试 | 3 |
报告 | 1 |
测试报告 | 2 |
计算工作量 | 0.5 |
事后总结 | 2 |
三、接口设计
1、Information Hiding
对于面向对象的程序设计而言,信息隐藏是一种重要的软件开发手段,它与对象的封装(encapsulation)与模块化(modularity)密切相关。在我看来,信息隐藏使得一个类把复杂的、敏感的、一旦被外界捕获可能会引起不良后果的内容封装在自身内部,这个类以外的代码得不到此类信息(通过反射等手段可能对得到),以提高程序的安全性与健壮性。
一般有两种信息需要隐藏,一种是复杂化的信息,他对外只提供一个调度的借口。比如在我们的项目中,生成四则运算题目的方法十分复杂,但是在用户界面中不需要体现出来,只需要提供一个接口在GUI方法中调用。
另一种则是变动的信息,比如在这个项目中,如果输入的Command值不规范,他会捕获并处理。错误并不会扩散出去。
2、Interface Design 与 Loose Coupling
这两个方面相辅相成,前者是后者的实现方式,后者是前者的目的。
面向接口编程是软件工程领域常用的设计手段,在这次结对项目中,我们深切体会到它的一大优点:我们只需要面向GUI接口进行编程,实现之后,我们的题目生成的方法就完美的融入了整个程序中。虽然我们在初期并不清楚整个测试程序的工作原理与架构,但是只要我们专注于实现GUI的功能,就能够达到调度的目的。
四、接口实现
我们的计算模块有几个方法,一个是生成题目的方法,一个是计算题目的答案的方法。生成题目的方法是主要部分,它调用了生成随机数、判断符号等方法,最后实现出一个符合要求的题目,最后将问题拼接为String类型,由计算题目的答案算出结果,最后还是由生成题目方法导入并生成。
我们算法的独到之处在于,我们最后GUI引用的只有这个生成题目的方法,实现所有功能。
五、单元测试
我们用单元测试测试了Lib类,代码如下
import static org.junit.Assert.*;
import java.io.IOException;
import org.junit.Test;
public class LibTest {
public static Lib test = new Lib();
public static int ans;
public String[] questionList = new String[100];
@Test
public void testMakeQuestions() {
Lib.makeQuestions(questionList, 5, 5, 1, 100, true, true);
Lib.makeQuestions(questionList, 5, 5, 1, 100, true, false);
Lib.makeQuestions(questionList, 5, 5, 1, 100, false, true);
Lib.makeQuestions(questionList, 5, 5, 1, 100, false, false);
//assertEquals(2, test.getresult());
}
@Test
public void testCalQuestion() {
assertEquals(77, Lib.calQuestion("(34-29)+52*1+10*2"));
assertEquals(47, Lib.calQuestion("3+5*(2+4*2)-6"));
assertEquals(6, Lib.calQuestion("1+2*3-4+9÷3"));
assertEquals(4, Lib.calQuestion("(2+4-3)÷3+5-2"));
//assertEquals(77, Lib.calQuestion("(34-29)+52*1+10*2"));
}
// @Test
// public void testGetw() {
// assertEquals(1, Lib.getw('+'));
// assertEquals(1, Lib.getw('-'));
// assertEquals(2, Lib.getw('*'));
// assertEquals(2, Lib.getw('÷'));
// assertEquals(3, Lib.getw('('));
// assertEquals(-1, Lib.getw(')'));
// assertEquals(-1, Lib.getw('#'));
// }
@Test
public void testFilePrint() throws IOException {
Lib.filePrint(questionList, 5, "test.txt");
}
@Test
public void testFileScanf() throws IOException{
Lib.filePrint(questionList, 5, "test.txt");
Lib.fileScanf(questionList, "test.txt");
}
}
得到的测试覆盖率如下图
六、异常处理
我们将Command类中所有的输入参数的异常情况进行处理。
七、界面模块设计
我的界面设计是,通过GUI为题目数量等参数提供一个JTextField,最后用get方法获取值。设置JButton按钮,并调用Lib方法中的生成题目和写入文件方法,题目生成后,弹出新的窗口(用户界面)供做题和判断。
Cre.addActionListener(new ButtonListener());
private class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
sum1 = Integer.parseInt(sum.getText());
int min1 = Integer.parseInt(min.getText());
int max1 = Integer.parseInt(max.getText());
int ops1 = Integer.parseInt(ops.getText());
String path = "result.txt";
boolean have_Mul = GUI.this.actionPerformed(e ,b2);
boolean have_Bra = GUI.this.actionPerformed(e ,b1);
//System.out.println(sum1+" "+min1+" "+max1+" "+ops1+" "+have_Mul+" "+have_Bra);
String[] questionList = new String[sum1+3];
int[] answerList = new int[sum1 +1];
Lib.makeQuestions(questionList,answerList, sum1, ops1, min1, max1, have_Mul, have_Bra);
new Exercise_GUI();
try {
Lib.filePrint(questionList, sum1, path);
} catch(IOException ioe) {
ioe.printStackTrace();
}
}
}
以上为第一个GUI的生成按钮,并由其中
new Exercise_GUI();
语句跳转到做题界面。
以下为最后成果界面:
八、结对过程
图中为我向黑哥请教他写的一些函数方法的调用问题。
九、结对编程感想
这次比较正式的结对编程让我感受良多。对于我这种基础比较弱的同学,有一个能力强的搭档,我肯定是受益的一方。结对编程可以促进参与项目的程序员自身的提高,一对程序员工作的时候,水平较低的一方会潜移默化地受水平略高的程序员影响,学到一些新的东西。
而黑哥作为水平高的一方同样因为不断地把自己的想法说出来而整理了自己的思路,这是优点之二。
优点之三呢,就是相辅相成的问题。黑哥能力再强,他也有不擅长的地方,就比如这次结对项目的GUI界面,黑哥的GUI界面设计掌握的并不熟练,所以我们共同完成的这个项目中,GUI界面我完成了大部分。
缺点其实也有,最明显的就是两个人并不一定有足够的时间可以在一起完成项目。像上图那样两个人都有时间然后坐在一起讨论分析的极少。
十、实际花费时间
花费时间(单位:h) | |
明确需求 | 0.5 |
开发 | 3 |
需求分析 | 0.5 |
代码规范 | 0.5 |
具体设计 | 4 |
具体编码 | 25 |
代码复审&测试 | 4 |
报告 | 3 |
测试报告 | 3 |
计算工作量 | 1 |
事后总结 | 3 |