结对编程之实战
结对编程?这是什么鬼?赶快google了一下,根据维基百科给出的定义,结对编程(英语:Pair programming)是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作。 一个人输入代码,而另一个人审查他输入的每一行代码。 输入代码的人称作驾驶员,审查代码的人称作观察员(或导航员)。 两个程序员经常互换角色。其实说白了就是两个人轮流操作一个机器(人闲机不闲),一人编码,一人审查。体现了1+1>2的思想。废话少说,进入正题。
结对伙伴:李星明
代码托管地址:Coding
一、题目选择
作业博客地址:结对编程第2次作业
经过和结对伙伴商量,这次我们选择题目一作为结对编程的实战项目。
-
题目1:
- 我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子,请实现它,要求:
- 能够自动生成四则运算练习题
- 可以定制题目数量
- 用户可以选择运算符
- 用户设置最大数(如十以内、百以内等)
- 用户选择是否有括号、是否有小数
- 用户选择输出方式(如输出到文件、打印机等)
- 最好能提供图形用户界面(根据自己能力选做,以完成上述功能为主)
- 我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子,请实现它,要求:
二、准备工作
1.软件建模和设计
闲话少说,先贴上软件系统图如下:
软件系统图详解:图中虽然写得是mvc,其实这个设计更接近于mvp的模式。在V层采取java的编程语言,CM层采取的是C语言编程
设计过程:在设计的时候是我们一起本着降低耦合性、模块化、易于维护和可扩展的原则下进行设计和建模,最终选择了上图结构;这样的话,M层都是最基本的模块(戏称为“原子划分”),尤其是Random;C层只依赖于M层,只有一个模块,降低了耦合性;V层依赖于C层;其中M层的功能只暴露给C层,M层的Random模块只暴露给M层的其他模块。这样的话就满足了功能单一和降低耦合性的原则。为了完善题目需求,添加了去重和求值模块
软件建模完成后,我们又一起讨论了其中M层几个模块的具体实现方法:
- 为了方便操作和传送数据,我们对整个运算表达式进行了数字化。例如:用8,4,2,1分别代表+ - * /,用最大数和最大数加1代表左括号和右括号
- 在对运算符组合的处理中用到的用到了位操作
- 求值模块用到了算符优先表和栈
- 运算表达式存储结构是包含二维浮点数数组的结构体
- 各个模块运算表达式结构题的传递采用指针的方式,其他参数的传递都是数值传递,比如题目数量,最大数范围等
2.任务分配
由于我的结对伙伴李同学不擅长java,而我对Java情有独钟;所以经过商量,我们决定用两种语言开发一个软件,分配任务如下:
(1)C、M层:李同学采取c/c++编写,李同学是驾驶员,我是领航员;
(2)V层:即视图层,采用java语言编写;我是驾驶员,李同学是领航员;
(3)测试:由于前面李同学编写了两层的大部分代码,我只编写了一层的代码。因此我来设计测试用例。
补充:编程期间必要时候角色可以互换一段时间,这样可以高效编程,做到人闲机不闲
3.编程环境
操作系统 | window10 |
---|---|
JDK | 1.8.0_71 |
开发语言 | java,C++ |
开发工具 | eclipse Mars Release (4.5.0),Code Blocks |
单元测试工具 | Junit4.12,google test |
三、结对编程
1.工作中的照片
2.软件运行结果展示
4.视图层核心代码展示
package site.yuanrui.four_arithmetic;
import java.awt.EventQueue;
import java.awt.FileDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
import site.yuanrui.Tools.*;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Color;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.awt.event.ActionEvent;
import javax.swing.JTextArea;
/**
* 主窗口类
* @author YUANRUI
*
*/
public class MainFrame extends JFrame {
private JPanel contentPane;
private JTextField textNum;
private JTextField textCount;
private JTextField textMax;
/**
* main函数入口
* @param args参数列表
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SubstanceTools.useSkin();//使用皮肤
SubstanceTools.useTheme();//使用主题
MainFrame frame = new MainFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* 窗口创建函数
*/
public MainFrame() {
setTitle("小学四则运算自动生成程序");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
setBounds(100, 100, 437, 339);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel title = new JLabel("小学四则运算自动生成程序");
title.setForeground(Color.BLACK);
title.setFont(new Font("幼圆", Font.BOLD, 13));
title.setBounds(128, 10, 171, 40);
contentPane.add(title);
JLabel lblNewLabel = new JLabel("题目数量:");
lblNewLabel.setBounds(10, 63, 66, 15);
contentPane.add(lblNewLabel);
textNum = new JTextField();
textNum.setBounds(70, 60, 66, 21);
contentPane.add(textNum);
textNum.setColumns(10);
JLabel label = new JLabel("操作数个数:");
label.setBounds(144, 63, 85, 15);
contentPane.add(label);
textCount = new JTextField();
textCount.setBounds(215, 60, 66, 21);
contentPane.add(textCount);
textCount.setColumns(10);
JLabel label_1 = new JLabel("最大数:");
label_1.setBounds(291, 63, 54, 15);
contentPane.add(label_1);
textMax = new JTextField();
textMax.setBounds(339, 60, 66, 21);
contentPane.add(textMax);
textMax.setColumns(10);
JLabel label_2 = new JLabel("运算符选择:");
label_2.setBounds(10, 100, 85, 15);
contentPane.add(label_2);
JButton OKbtn = new JButton("生成题目");
OKbtn.setBounds(57, 171, 126, 37);
contentPane.add(OKbtn);
JCheckBox checkBoxAdd = new JCheckBox("+");
checkBoxAdd.setBounds(84, 96, 47, 23);
contentPane.add(checkBoxAdd);
JCheckBox checkBoxSub = new JCheckBox("-");
checkBoxSub.setBounds(128, 96, 38, 23);
contentPane.add(checkBoxSub);
JCheckBox checkBoxMul = new JCheckBox("*");
checkBoxMul.setBounds(168, 96, 38, 23);
contentPane.add(checkBoxMul);
JCheckBox checkBoxDiv = new JCheckBox("/");
checkBoxDiv.setBounds(210, 96, 47, 23);
contentPane.add(checkBoxDiv);
JLabel labelDel = new JLabel("小数:");
labelDel.setBounds(10, 132, 45, 15);
contentPane.add(labelDel);
JCheckBox checkBoxDel = new JCheckBox("有");
checkBoxDel.setBounds(48, 128, 47, 23);
contentPane.add(checkBoxDel);
JLabel label_3 = new JLabel("负数:");
label_3.setBounds(168, 132, 47, 15);
contentPane.add(label_3);
JCheckBox checkBoxNeg = new JCheckBox("有");
checkBoxNeg.setBounds(210, 128, 54, 23);
contentPane.add(checkBoxNeg);
JLabel label_4 = new JLabel("括号:");
label_4.setBounds(301, 132, 44, 15);
contentPane.add(label_4);
JCheckBox checkBoxBra = new JCheckBox("有");
checkBoxBra.setBounds(339, 128, 38, 23);
contentPane.add(checkBoxBra);
JTextArea textAreaResult = new JTextArea();
textAreaResult.setBounds(10, 218, 411, 82);
contentPane.add(textAreaResult);
JButton buttonSave = new JButton("保存生成的题目");
buttonSave.setBounds(251, 171, 126, 37);
contentPane.add(buttonSave);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(0, 187, 591, 98);
contentPane.add(scrollPane);
scrollPane.setViewportView(textAreaResult);
/**
* 生成题目点击事件
*/
OKbtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int number = 0;
int max = 10;
int operandNumber = 2;
int decimal = 0;
int negative = 0;
int bracketint = 0;
int opreation = 0;
if (checkBoxDel.isSelected()) {
decimal = 1;
}
if (checkBoxNeg.isSelected()) {
negative = 1;
}
if (checkBoxBra.isSelected()) {
bracketint = 1;
}
if (checkBoxDiv.isSelected()) {
opreation+=1;
}
if (checkBoxMul.isSelected()) {
opreation+=2;
}
if (checkBoxSub.isSelected()) {
opreation+=4;
}
if (checkBoxAdd.isSelected()) {
opreation+=8;
}
//参数验证
try {
number = Integer.parseInt(textNum.getText());
max = Integer.parseInt(textMax.getText());
operandNumber = Integer.parseInt(textCount.getText());
} catch (Exception e2) {
e2.printStackTrace();
JOptionPane.showMessageDialog(null, "【题目数量、操作数、最大数格式错误,请重新输入】", "参数格式错误", JOptionPane.ERROR_MESSAGE);
System.out.println("【题目数量、操作数、最大数格式错误,请重新输入】");
return;
}
if (number>1000||number<=0) {
JOptionPane.showMessageDialog(null, "【题目数量范围1~1000,请重新输入】", "题目数量超出范围", JOptionPane.ERROR_MESSAGE);
System.out.println("【题目数量范围1~1000,请重新输入】");
return;
}
if (max>1000||max<=2) {
JOptionPane.showMessageDialog(null, "【最大值范围1~1000,请重新输入】", "最大值超出范围", JOptionPane.ERROR_MESSAGE);
System.out.println("【最大值范围1~1000,请重新输入】");
return;
}
if (operandNumber>20||operandNumber<=1) {
JOptionPane.showMessageDialog(null, "【操作数个数范围2~20,请重新输入】", "操作数个数超出范围", JOptionPane.ERROR_MESSAGE);
System.out.println("【操作数个数范围2~20,请重新输入】");
return;
}
if (opreation>15||opreation<=0) {
JOptionPane.showMessageDialog(null, "【至少选择一个运算符】", "运算符选择错误", JOptionPane.ERROR_MESSAGE);
System.out.println("【至少选择一个运算符】");
return;
}
String result = ShellTool.execCmd("Four_operation "+number+" "+max+" "+operandNumber+" "+decimal+" "+negative+" "+bracketint+" "+opreation+" 0",null);
System.out.println(number+" "+max+" "+operandNumber+" "+decimal+" "+negative+" "+bracketint+" "+opreation+" 0");
textAreaResult.setText(result);
}
});
/**
* 保存题目点击事件
*/
buttonSave.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (textAreaResult.getText().isEmpty()) {
JOptionPane.showMessageDialog(null, "【请先点击生成题目】", "题目未生成", JOptionPane.ERROR_MESSAGE);
return;
}
FileDialog openFile = new FileDialog(new Frame(), "另存为", FileDialog.SAVE);
openFile.setVisible(true);
String dirName=openFile.getDirectory();
String fileName=openFile.getFile();
if(dirName==null || fileName==null){
return;
}
try {
FileUtil.writeToFile(dirName+fileName+".txt",textAreaResult.getText());
} catch (IOException e1) {
e1.printStackTrace();
System.out.println("文件写入错误!!!");
}
}
});
}
}
四、软件测试
1、设计测试用例
1)随机数生成模块测试用例
测试用例 | 参数 | 期待结果 |
---|---|---|
测试用例1 | 0 | 0 |
测试用例2 | 5 | 0~4 |
2)运算符随机生成模块测试用例
测试用例 | 参数 | 期待结果 |
---|---|---|
测试用例1 | 100,0 | 101 |
测试用例2 | 100,1 | 100,101 |
测试用例3 | 100,5 | 100,101 |
3)操作数生成模块测试用例
测试用例 | 参数 | 期待结果 |
---|---|---|
测试用例1 | 10,0,0 | 10以内的正整数 |
测试用例2 | 10,1,0 | 10,0,0 |
测试用例3 | 10,0,1 | 10以内的整数 |
测试用例4 | 10,1,1 | 10以内的浮点数 |
4)去重模块测试用例
测试用例 | 参数 | 期待结果 |
---|---|---|
测试用例1 | {{12,8,10,4,20},{12,8,10,4,20}} | true |
4)求结果模块测试用
测试用例 | 参数 | 期待结果 |
---|---|---|
测试用例1 | { | 2 |
5)四则运算生成器测试用例
测试用例 | 参数 | 期待结果 |
---|---|---|
测试用例1 | 10, 100, 4, 0, 0, 0, 15, 0 | 10道100以内的加减乘除正整数运算题 |
3、部分测试代码展示
4、测试结果展示
五、对方评价
1.对方性格评价
李同学是一个性格温和的人,善于思考,做事认真,力求完美的人。在结对编程过程中,对于同一个问题有自己的看法,能够指出我编程过程中出现的bug。当我遇到问题时,能提供一些思路,能把自己的想法和看法说出来;李同学也是一个善问的人,在我当驾驶员的时候,当看不懂的时候,提出疑问,正是因为这样才使我通过讲解对自己的算法了解更深。同时李同学还是一个友善的人,善于合作和与交流,在和他的合作学习和探讨活动中,能选择自己擅长的方式(比如记笔记,画图)表述他的想法,互相交流自己的想法,能倾听和讨论我们两个人之间不同的意见。感触最深的是,李同学在软件设计讨论中善于记笔记,这也是我从他身上学到的。记笔记方便后续的查看,也方便进一步讨论理清思路。跟李同学进行合作能体会到合作的愉快和合作的高效率。大概就是这些。
2.对方编程习惯评价
通过上一次结对编程之代码审查,我通过对他的代码进行了审查,并指出了一些不足,比如变量没有初始化;变量命名方式简单,不易读;函数之间的大空间参数传递没有用引用或者指针;重复的代码块未抽取等,在这次结对编程中李同学对这几个方面有特别的注意,并进行的改正。李同学的代码完整规范,有卓越的数据结构功底,这在他编写求值模块的过程中能体会得到;敲代码速度快速,思维广阔,想法独特有创新 。
六、总结
通过结对编程实战,对结对编程的理解更透彻,然后就是通过结对编程实战使我从队友学习了一些自己身上所没有的东西。前面讨论如何分工,其实在真正编程的时候,分工跟预想的并不完全一样,我们首先讨论出了软件的具体架构,还有一些模块的核心算法。在编程中,有些算法因为不合适,改进了一些。那些算法改进的话程序效率提高的最多。其中算法的主要代码是我们共同完成的,其中核心代码是李同学完成的,李同学主要是编写需要C语言编写的M、C层部分,我主要负责V层代码、测试和审查代码。
零零散散历经大约一周时间完成,只有经历了才知道。就个人观点谈下结对编程的优缺点吧,首先说下好处。第一,可以发散思维,两个人一个小时能讨论出来的东西,或许一个人一个星期都没法想出来,毕竟两个人看问题的角度不一样。第二、边编程边复查,当一个人写出一段代码的时候,另一个人作为旁观者往往更容易发现其中的问题,这就所谓“旁观者清,当局者迷”。第三、在技术和性格互补的情况下,可以极大提高开发效率,达到1+1>2的效果,比如李同学用C语言编写后端代码,我用java编写前端视图代码。其次是缺点,最大的问题就是编程习惯问题,不同的代码风格,在一起工作时会产生麻烦,尤其对于有强迫症的人;其次就是结对编程也容易滋生不良气氛,敷衍了事。然后两个人在一起工作可能会出现工作精力不能集中的情况。程序员可能会交谈一些与工作无关的事情,反而分散注意力,导致效率比单人更为低下;还有就是习惯了一个人敲代码,当有个人在旁边看着你敲,感觉也挺别扭,进而影响进度。让我庆幸的是,在我和李同学的结对编程中这些缺点不是很突出,可能是性格和编程习惯各方面原因。
以上就是我对本次结对编程的感想,总的说来,通过结对编程的收获是非常大的,通过这次作业,不仅学到了技术知识,而且更获得了许多宝贵的合作经验,更收获了深厚的友谊,期待下一次和李同学的合作。