Pair Programing 结对编程初体验

这是一次关于结对编程的体验

0.写在前边

结对编程(英语:Pair programming)是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作。一个人输入代码,而另一个人审查他输入的每一行代码。输入代码的人称作驾驶员,审查代码的人称作观察员(或导航员)。两个程序员经常互换角色。

在结对编程中,观察员同时考虑工作的战略性方向,提出改进的意见,或将来可能出现的问题以便处理。这样使得驾驶者可以集中全部注意力在完成当前任务的“战术”方面。观察员当作安全网和指南。结对编程对开发程序有很多好处。比如增加纪律性,写出更好的代码等。

结对编程是极端编程的组成部分。

其实结对编程做起来很简单也很有趣,找个水平差的不太远的程序员和自己配成一对。只用一台计算机,大家选一个人坐在键盘前面负责输入,另一个人坐在后面口述。两个人要不断的交流,频率不应低于一分钟一次。整个的设计思想由后面只动口不动手的人主导,而由操键盘的人做实现。由于人的思维速度是快于输入代码的速度的。那么观看的人可以有空闲的时间做额外的思考,观察代码写的有没有问题,结构有没有问题。

1. 项目要求

  • 小学老师要每周给同学出300道四则运算练习题。

分析问题需求:

  1. 需要使用程序生成300道数学题
    • 需要答案在0~100之间
    • 需要中间的过程运算数均在0~100之间
    • 需要考虑到运算优先级
    • 限制在四则运算
  2. 需要给出答案的正确与否
  3. 能够选择答案是否显示
  4. 能够给出正确率
  • 这个程序有很多种实现方式:

    • C/C++

    • C#/VB.net/Java

    • Excel

    • Unix Shell

    • Emacs/Powershell/Vbscript

    • Perl

    • Python

由于我们两人组都对C++相对更加熟悉,所以我们选择使用C++来实现这个项目

2. 开发环境

机器A:Visual Studio Code + mingw64

机器B:Xcode + clang

首先使用机器A编写了最初的程序原型,经过基本的测试和编译后,将代码发送到机器B中,使用

VSC

Xcode

3. 实验内容和步骤

所谓结对编程,核心就是一个人输入代码,而另一个人审查他输入的每一行代码。

所以由我在一旁看另一位同学编码,我一边给出意见和建议,一边理解对方的代码。

第一版原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <time.h>
#include <cmath>
#include <map>
#include <algorithm>
using namespace std;

const int N = 300;
bool flag;
char op[] = {'+', '-', '*', '/'};
map<char, int> f;
int a[N + 5], b[N + 5], c[N + 5], ans[N + 5];

int f2(int a, char op, int b) {
if ((a < 0) || (b < 0)) return -1;
if ((a > 100) || (b > 100)) return -1;
if (op == '+') return(a + b);
if (op == '-') return(a - b);
if (op == '*') return(a * b);
if (op == '/') {
if (!b) return(-1);
else if (a % b) return(-1); else return(a / b);
}
}

int main() {
f['+'] = f['-'] = 0;
f['*'] = f['/'] = 1;
srand(time(NULL));
for (int i = 1; i <= N; i++) {
flag = false;
char ch1, ch2;
while (!flag) {
a[i] = rand() % 100; b[i] = rand() % 100; c[i] = rand() % 100;
ch1 = op[rand() % 4];
ch2 = op[rand() % 4];
if (f[ch1] < f[ch2]) {
ans[i] = f2(b[i], ch2, c[i]);
ans[i] = f2(a[i], ch1, ans[i]);
} else {
ans[i] = f2(a[i], ch1, b[i]);
ans[i] = f2(ans[i], ch2, c[i]);
}
if ((ans[i] < 0) || (ans[i] > 100)) flag = false; else flag = true;
}
cout << '(' << i << ") " << a[i] << ch1 << b[i] << ch2 << c[i] << "=" << ans[i];
if (i % 3) cout << '\t'; else cout << endl;
}
return 0;
}

Stage1

在第一版原型中,我们简单的实现了按照项目要求中的算式需求,生成300道数学题的功能

。我们主要通过 rand() 函数取模运算来限制随机生成的数字。同样,使用 rand() 随机生成不同的运算符号。对于每一个生成的算式,我们对其进行运算,并得到对应的运算结果。

我们再检查运算结果是否在0~100中,如果不在,就再生成一个算式。

这样,我们就很快的实现了最初的原型设计

第二版原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>
#include <time.h>
#include <cmath>
#include <map>
#include <algorithm>
using namespace std;

int N;
const int M = 510;
bool flag;
char op[] = {'+', '-', '*', '/'};
map<char, int> f;
int a[M], b[M], c[M], ans[M], check[M], w[M];
char ch1[M], ch2[M];

int f2(int a, char op, int b) {
if ((a < 0) || (b < 0)) return -1;
if ((a > 100) || (b > 100)) return -1;
if (op == '+') return(a + b);
if (op == '-') return(a - b);
if (op == '*') return(a * b);
if (op == '/') {
if (!b) return(-1);
else if (a % b) return(-1); else return(a / b);
}
return 0;
}

void write(bool flag) {
for (int i = 1; i <= N; i++) {
cout << '(' << i << ") " << a[i] << ch1[i] << b[i] << ch2[i] << c[i] << "=";
if (flag) cout << ans[i];
if (i % 3) cout << "\t"; else cout << endl;
}
}

void init() {
f['+'] = f['-'] = 0;
f['*'] = f['/'] = 1;
srand(time(NULL));
for (int i = 1; i <= N; i++) {
flag = false;
while (!flag) {
a[i] = rand() % 100; b[i] = rand() % 100; c[i] = rand() % 100;
ch1[i] = op[rand() % 4];
ch2[i] = op[rand() % 4];
if (f[ch1[i]] < f[ch2[i]]) {
ans[i] = f2(b[i], ch2[i], c[i]);
ans[i] = f2(a[i], ch1[i], ans[i]);
} else {
ans[i] = f2(a[i], ch1[i], b[i]);
ans[i] = f2(ans[i], ch2[i], c[i]);
}
if ((ans[i] < 0) || (ans[i] > 100)) flag = false; else flag = true;

}
}
}

int main() {
cout << "请输入需要生成的习题数量(0-500):";
cin >> N;
cout << "正在自动生成习题" << endl;
init();
int num = 0;
cout << endl << "请按照题号依次输入答案,按回车自动进入下一道题" << endl;
for (int i = 1; i <= N; i++) {
cout << '(' << i << ')' << a[i] << ch1[i] << b[i] << ch2[i] << c[i] << "=";
cin >> check[i];
if (check[i] != ans[i]) w[++num] = i;
}
cout << "您共回答正确了" << N - num << "道题目,答错" << num << "道" << endl;
if (num) {
cout << "您做错的题目分别为:" << endl;
for (int i = 1; i <= num; i++) {
cout << "第" << w[i] << "道,您输入的答案为" << check[w[i]] << "正确答案为" << ans[w[i]] << endl;
}
} else cout << "都答对了,你真棒!" << endl;
return 0;
}

在第二版中,我们在第一版的基础上添加了几个功能

  • 能够选择需要回答的问题数量
  • 能够和用户交互,输入答案
  • 验证用户输入的答案
  • 统计正确数量,给出错误的题号和正确答案

stage2

4. 结果和分析

经过很短的时间我们的项目原型就已经完成了,回顾上方的需求分析,我们已经完成了所需的功能

  • 需要使用程序生成300道数学题

    • 需要答案在0~100之间

    • 需要中间的过程运算数均在0~100之间

    • 需要考虑到运算优先级

    • 限制在四则运算

  • 需要给出答案的正确与否

  • 能够选择答案是否显示

  • 能够给出正确率

虽然给出了答案的正确与否,但是遗漏了正确率的功能

除此之外,这个程序的交互见面还过于简陋。如果按照需求的分析,项目的涉众是小学老师和小学生,如果还留着简陋的交互界面对于使用者来说必然是不适的,所以我们还可以有更多值得改进的地方

  • 图形界面
  • 友好的提示
  • 更合理的交互流程

5. 实验体会

组员A:相比起原本自己敲代码的情况下,结对编程能够更高的提升效率,帮助我们在有限的时间内尽可能完成所需要做的工作。而且在有人查看的情况下,也更加容易在编码阶段就更容易发现代码中的错误,也能够关注到一开始没有注意到的问题。

组员B:在别人写代码的时候在一旁观看,能够学习到别人的编码风格和设计思维。每个人都会有自己的编程风格,所以不仅需要学会自己写代码,更重要的是学会看懂别人的代码。使用结对编程的方式迫使我们把注意力集中在工作中,而不容易被不相关的事情打断思路。

一些研究发现程序员结对工作与单独工作相比,会写出更短的程序,更好的设计,以及更少的缺陷。研究发现缺陷率降低15%到50%,会由于程序员的经验以及任务的复杂度而不同。结对编程比单独编程相比,通常会考虑更多的设计选项,达成更简单,更易维护的设计;程序员们也会更早地捕捉到设计的缺陷。结对编程与一个程序员承担同一个任务相比工作会完成的更快。结对的程序员经常发现当他们一同工作时表面上“不可能”的问题变得容易,或更加快速,或至少有可能解决。