当前课程知识点:软件工程 >  第2章 编写高质量代码 >  2.6 结对编程实践 >  讲课视频

返回《软件工程》慕课在线视频课程列表

讲课视频在线视频

下一节:讲课视频

返回《软件工程》慕课在线视频列表

讲课视频课程教案、知识点、字幕

前面我们讲过

代码复审是一种用以

确认方案设计

和检查代码缺陷的有效手段

那么为什么我们不把这种

好的方法用到极致呢

结对编程就是把编程和复审

进行有机的结合

它是敏捷开发方法中

极限编程所大力提倡的

一种实践

简单的说

结对编程就是两名程序员

并排坐在一台电脑前一起工作

他们一起分析问题

一起设计程序

一起写测试用例

一起编程实现

一起做单元测试和集成测试

一起写文档

这种方法有助于提升

代码的质量

提高开发效率

同时还可以促进团队能力的

提升和知识的传播

在现实生活中

类似的搭档关系比比皆是

例如越野赛车里面的驾驶员

和领航员

飞机上的驾驶和副驾驶

战斗机编组里面的长机和僚机

这些任务都有一个共同的特点

就是他们都是在高速度中

完成任务

而且任务有比较高的技术

要求任务失败的代价都很高

结对编程也有两个角色

一个是驾驶员

负责用键盘编写程序

另一个是领航员

起到领航和提醒的作用

其中驾驶员的主要任务

是对程序进行设计

编写代码

并进行单元测试

领航员的任务是

检查驾驶员的工作

考虑单元测试的覆盖程度

以及代码是否需要修改完善

同时帮助驾驶员

解决具体的技术问题

比如查询API手册等

在编程过程中

两个人的角色是轮流互换的

结对编程不仅仅涉及编程活动

也包括分析 设计 测试等

全程活动

结对编程需要两个人

不断地会话和讨论

驾驶员需要不停地向伙伴

解释自己的做法和想法

如果发现自己的伙伴

不能够投入

就需要及时的沟通

领航员需要时时提出自己的

疑问和意见

指出可能存在的问题

因此口渴的指数

是核实伙伴交流程度的

一个考核标准

在结对编程过程中

领航员来控制开发的时间

在合适的时候提出来休息

或者提议交换角色

当然驾驶员感到累的时候

也可以主动要求换角色

领航员需要注意的是

要给驾驶员一点时间

去发现和找到自己的错误

不要让对方觉得你很烦

常用的资料 规范以及书籍

都应该放在手边

这样方便领航员快速的查阅

以提供帮助

在结对开始之前

双方要协调沟通

互相通告希望对方关注些什么

自己喜欢做什么等

要主动地参与

任何的一个任务都是

共同的责任

没有你的我的这样的代码

只有我们共同的代码

要坚持代码的标准

和流程的规范

要注意倾听伙伴的意见

有效的结对编程

并不是一天就能做到的

它是一个相互学习

相互磨合的渐进过程

开发人员需要一定的时间

来适应这种新的开发模式

刚一开始的时候

结对编程有可能不如

单独开发效率高

但是在度过了学习阶段之后

结对编程的开发质量和

开发时间

通常会比两个人单独开发

有明显的改善

这里需要说明的是

并不是所有的项目

都适合结对编程

对于探索阶段的项目

一个人单独钻研更为有效

在后期维护时

如果维护的技术含量不高

只需要做有效的复审就可以

如果验证测试

需要运行很长时间

那么两个人在那儿等结果

确实也是浪费时间

团队成员在多个项目中工作时

难以保证足够的结对编程时间

这样成员就经常会需要等待

反而影响效率

结对编程的关键是如何

最大限度地发挥领航员的作用

如果领航的用处不大

也就没有必要结对了

另外也不是所有的人

都适合结对编程

像不合格的程序员

不合群的程序员

以及抵触结对编程的程序员

都很难适合这种工作方式

现在大家已经对结对编程

有了初步的了解

下面由两位程序员

来演示一下

他们是如何结对编写

生命游戏的

大家好 我是赵雷彧

大家好 我是王昳晗

今天我们给大家演示一下

结对编程在实际应用中的例子

我们会以大家耳熟能详的

生命游戏中的地图模块为例

在实际的编写过程中

给大家展示一下

结对编程是怎么样的

并体现它的优点

在编制它的过程中

我就是那个写代码的

我是负责观察的

首先我们先看一下整个

这个东西应该

整个这个模块已经有了接口

和它们需要的流程吧

我们现在是要实现这个类

然后首先这个类的话

大家看一下它这个接口

它有两个属性

是它地图的属性

然后还有用来重

因为这应该是用来重设地图的

根据这个注释的作用

然后这个应该是对某一个

具体的那个位置的细胞状态

进行设定

后面这些应该是跟

输入输出有关

以及地图变更有关的

然后就并没有太大那个

然后我们现在先讨论一下整个

就是每一个接口

需要有的东西吧

好 首先我们一个一个接口的

来看吧

我们首先是构造函数

构造函数这里可以看到它只有

两个参数

一个是行 一个是列

是的 然后

而且我们可以看到

在下面是它行和列

以属性的形式呈现出来

并且它的属性是只有读取属性

没有设置的

所以说可以看出

这个在构造函数确定之后

是只读的

对的

所以我们应该在构造的时候

就把它的那个合法性判定

给做了

所以在这里注释中

我们先写入一些

参数的合法性

然后对于构造函数来说

可能它传下来的参数

并不是我们想要的那种

比如说我们可能对于行数

和列数来说

只能要一个整数

所以说我们需要在这个函数

实现中判断一下

是不是我们需要的类型

然后还有就是它传过来

如果是零或者负数的话

我们也需要对它进行判定

对对 是这意思

然后除此之外

哦 对 还有一个事

就是为了防止这个类

已经建立了之后

外面的使用者

就是在没有对它进行reshuffle之前就采取读

或者写的操作

我们是不是应该在这儿

先初始建立一张地图

比如说我们可以调用一个reshuffle

或者是我们先建立

一张空白地图

我倾向于建立一个空白的吧

然后这两函数都没什么好说的

因为都是只读的

我们直接返回就行

应该不需要任何判定

然后reset它应该是用

random为来填充整个地图

然后它传进来一个参数

possibility
所以说我们还是要对它进行

参数的校验

也叫合法性的判断 对

possibility一定要是一个float吧

比如说

然后是0到1了

是的

然后除此之外应该没有什么

可以那个了

然后下面就get和set

get和set首先

因为它是对某一个位置

进行确定的嘛

所以说自然我们要

就是我们要确定这个位置

是整数

并且是在整个地图的

下标范畴之内

是的 也就是说也依旧需要

进行参数合法性的判断

然后但对于set来说

val是不是应该有别的那个

对 val

所谓的val在地图上来说

就是代表的细胞的状态

也就是说

只可能是死或者活

这两种状态

所以说val可能也是

只有两种值的

对 看这个上面注释的话

应该是只能是0和1

所以说这个就简单

判定一下就行

那么0和1实际上我们可以

规定成一种常量

就是作为一种

类里面的常量进行对外的调用

可以

然后get neighbor count

这个东西是获取在某行某列的

一个指附近的那个各种元素的

那样各种活的细胞的数目

然后它除了需要判定这两个的

合法性之外

似乎也没有什么特别明显

明显需要的东西

干的东西

在具体实现上

可以到时候再讨论

对 可以可以

然后这个就不说了

什么参数都没有

它只是把上一个

neighbor count

对每一个那个跑一遍

然后set map的话

它的意思似乎是

传进来一张新的地图

然后把新的地图直接覆盖成

那一张新的

对于这个map的参数来说的话

实际上判断它还是存在一些

应该是诸多的限制因为

怎么说呢

首先我们

因为我们这个类的地图尺寸

是固定的

这是不能调的

所以说我们应该

让这个map具有跟这个地图

初始的时候相同的属性

然后完了我们还要判定

map中的每一个元素是

跟这个val是一致的

是的

还需要判断它的数据格式

是符合map的样子的

所以这里应该是一个比较

麻烦的东西

所以说对于map来说

一定要是一个list

然后map中的每一个元素

也一定要是一个list

然后
而且对于它的元素来说

它的第一维的维数应该是row

然后每一个那个都应该是

rows

都应该是cols

然后主要就是这样

然后

下面是print map了

print map的话

关于这个print map这个函数

它传了两个都有默认值的参数

第一个的话

它应该是从那个细胞状态

0 1这两个状态

到一个具体的打印值的

一个映射

所以说我感觉它默认值

如果这样设并不太好

我觉得如果这样会不会好一些

对 就是默认

如果是活细胞就打印1

如果是死细胞就打印0这样

对 然后

如果是按这个格式来的话

我们应该让cell maps

变成一个

是一个list

然后它的维数至少是

要大于那个的

它是大于等于2的

大于等于2的

然后 另外的话

是大于等于2还是

还是就规定为2呢

应该等于2吧

也可以

因为这

毕竟只有两状态

对 可以直接让它等于2

然后这儿是分隔符

分隔符就是在每个细胞

里面那个

它默认是一个空格

这个你只要是个srting就好

是一个长度为1的string

或者不一定

不一定为1 都可以的

就行

好 也就是说我们刚才就是

做了一些每一个接口的参数的

一些限定

那我们可以看到

很多的参数实际上都是要

规定它的类型

规定它的range

所以我们实际上可以把

判断类型 判断range

这个一整套工作

作为一个函数来进行封装

那我们应该可以

首先单独提出来一个函数

来检验整数的合法性

就是首先它要是一个整数

并且要是可以那个

处于某个范围中的整数

如果 因为这个东西它并

它的重用性很强

而且对类没有特定的要求

所以说我们大体把它变成一个

固定的静态类型

然后参数的话

首先一个是

带检查的那个number

然后我们可以设一个上下限

但是上下限可以那个的

可以为空的

这样方便row扩展

可以有一个min

还有一个max

然后这个就行了

然后另外就是判定你一个单元

是不是有效的状态

这个我们还需不需要

单独写一个函数

尽管我们知道它是非0即1

但是我觉得如果直接用这个

函数检查

是不是有点不具有语义性

对 因为0和1实际上对外

并不是一个非常直观的东西

就是说我们还是需要

在这儿进行一些

所以进行一些常量的定义

然后

空格

然后中间空格

好 我们讨论完成之后

开始写代码

但是在实际的结对编程过程中

因为经常写代码的和观察者

会交换角色

所以说现在我和王昳晗

就交换角色

首先我们来实现一下这个

check integer这儿的函数

最开始我们要判断

传入的三个参数都是int类型的

是的

所以然后就是

isinstance

isinstance

int或者是

isinstance

如果是出现这三个

不是两种情况

那么我们就抛出type error

就抛出一个TypeError吧

好 OK

然后接下来我们干的事

应该是检查n在范畴之内

首先我们对min和max是否

为none

然后判断n是否在

满足那个条件

在那个min和max之间

那么就是如果min

并且

n是小于min的

那么就要raise

这时候raise TypeError

应该不太合适

我们是要自己定义一个

exception么

那么我们可以自己定义一个

exception

比如认为它是RangeError

嗯 我们先写上

看到了

刚才多点了个点

min ok

我们之后再

再去补一下

用相同的方式来进一步

判定max

这儿是max

就n大于max

它应该

max

max

我们下一步应该就是定义

一下raise

RangeError

我们之前已经实现了一个

RangeError

然后我们略过中间实现过程

然后现在我们把import进来

然后直接用就行了

那接下来我们实现

下一个模块吧

好 下面我们就可以来实现

构造函数

是的

构造函数

首先我们要对传入的参数

进行一个合法性的检测

那我们就调用之前

我们实现的check integer

然后row

这儿应该是rows

min应该是1

至少1好了

对 至少要有1行

max不设

max可以不要 对

cols也是一样的

cols也是1

接下来我们应该建立一个

内部的存储

把这两个值给存进来

比如说叫做什么呢

size吧

就叫size

它就先是等于一个行数

然后等于一个列数

接下来我们应该写一个那个

地图的数组来存储细胞状态

就是用一个map来描述

所有的细胞状态

它应该就是一个二维数组的

形式

那么我们可以初始化

它全都是死细胞

这个记着用上面定义好的常量

那就是CELL

CELL DEAD

CELL DEAD

然后对于一个in range cols

我们可以看到这里有一个

错误提示

对 没有使用

我们用下划线代替

没有使用

那么我们可以直接用

下划线代替
代替也一样是rows维度的

好 我们先来实现print map

我们已经规定了

我们需要首先执行的类型检查

就是cell map一定要

是要是一个长度为2的list

那么

if
if
isinstance

not

not isinstance cell

maps

list

这儿是list


for

也行 这儿这样

那么就raise

这前面可以加或其实

把长度也变成0

如果 如果你要把

想定义成不同的错误类型的话

就分开

我觉得可以

可以直接遗弃吧

那就or

or

len

not len cell maps

如果它不等于2 对

那么raise TypeError

TypeError

我们可以这里明确的指示

就是cell maps

好 那sep也一样的进行判断

sep只需要判断它

是一个字符串就可以

长度不受限制

它应该是

str

str

ok

可以再拷贝(20:57)

好 接下来我们就可以

进行一个打印

打印的话我们

用两个for就好了

一行一行打印

for row in range self点

rows 点 yes

for row

for cols in range self.cols

那个col不要加s吧 最好

哦 对

因为只有1


那我们怎么print呢

需要注意应该是

因为print每次后面要手动

加一个东西

如果不加的话

就需要特殊判定

你这个元素是不是每一行

最后的一个元素

挺复杂的

我觉得我们可以用join

也就是说每一行

因为它每一行都已经是一个

单独的数组了

但是那个数组是一个int了

而且int只能是0到1的一个值

所以说可以直接当做

cell map的下标来使

应该建立一个0到1的下标

和那个输出的

和输出cell map的一个映射

对 我们可以用lambda表达式

用lambda表达式

然后用join

对 然后再join

先print吧

然后应该是join

应该是sep.join

然后map int

map

嗯 第一个应该是个函数
就lambda表达式

一个是lambda

lambda

然后先不写

然后后面的应该是

应该是

self

self 地图的那个

self map row

然后lambda怎么写

lambda应该是

从x到cell maps[x]

是 应该我们就行了

好 那我们来测试一下

嗯 就直接简单地来print一次

好 我们直接在下面

新建一个类

然后现在它应该全是0

对 比如说是5个

对 无所谓

然后来print一下

game map print

然后我们跑一下

好 我们来跑一下

哦 错了 17行

17行它说这里有错

哦 有一个问题就是

那个min和max可能是none

但是none并不是int

那么我们可以把这里换一下

比如说or min and这个

or

or这个

and这个

max

max and

and这个

然后要一个括号

应该是不需要括号的

因为and比or优先级高

我觉得加上括号可读性

可能会更强一点

行 没问题

那就这样

我们再跑一下试试 好

ok

我们得到了正确的结果

现在我们继续实现下面的函数

由于时间关系

我们刚刚在私下完成了

其他大部分接口的实现

留下了最后两个

最重要的接口

分别是那个

get neighbor count

也就是获取某行某列

一个细胞周围8格的

活细胞数目

以及生成一张那个

就是活细胞数目的地图

也就是对每一个位置

应用上面一个函数

然后我们大家先可以开始写了

另外提一句

就是我们又交换了

互换了角色

然后现在由赵雷彧

来写代码

是的

然后我们首先干第一件事

还是检查参数的合法性

还是用我们之前的

实现的check integer row

要是从0

因为是数组下标

应该从0开始

从0开始到rows减1

然后

对 col也是一样的

cols减1

ok

然后我们现在就是要

记录某一个位置四

应该是四周8格的那个

活细胞数目

但是我们应该怎么写呢

优雅一些

应该是八连通的话

对于每一个格子来说

都是它相邻的8个格子

所以我们可以用一种

比如类似于方向的东西

来规定它

每个方向可以

对 你可以枚举

然后用一个数组

但是如果为了那个

语义性好一点的话

我就用

可以用一个字典来做

比如说我们可以这样

DIRECTION

对 实际上每一个方向都是

一个对下标的操作

是的

比如说上的话应该是

row减1 col不变

然后

对于

应该是row加1 col不变

这儿应该用逗号

然后左就是0 1

哦 0 负1

0 负1 对

右 0 1

然后

接着上左

四个角 对

是负1 负1

上左 对

然后 上右

是负1 1

然后是下左

1 负1

和下右

规定好了这8个方向以后

我们就可以去遍历这个字典了

对 我们就for了一个in

DIRECTION

接下来我们要考虑一个事情

就是由于在边界的细胞

它们的判定是穿越边界的

也就是说最左边的细胞的

最左边

相当于是地图最右边的细胞

然后

因为对于负数的下标的话

Python是自己可以处理的

但是对于正数超过了下标

我们是不是应该手动取个模

对 其实模一下其实就可以

对 所以说我们应该临时的

取一个trow等于row加

direction dire

然后取一个模

模自己的行数

对 row在加的时候

应该是加direction的

direction的0

第一个

对对对

然后同0做了一个col

然后这个应该是自己的列数

好 然后

在这之前我们应该确定一个

能返回的值

counter 初始化为0

然后

那么进行一些判断

如果在自己的地图的

这个位置

位置的元素

tcol

是那个

如果是活的

我们之前定义过的一个常量

应该counter加

counter加

好 接下来就要返回

counter应该就够了 对吧


代码分隔

加一些空格

然后上面

上面要相等

好的

在实现这个函数之后

我们就应该可以实现

下来这个函数

对 这个就是对外的

一个调用的函数

这个只需要返回一个二维的

数组

所以说我们需要

这个函数本质上就是

遍历map里的每一个格子

然后生成每一个格子的那个

里面的活着的数量

是的

所以我们只需要简单的

二重遍历

二重循环

self.rows

然后我们需要弄一个临时的行

再遍历第二个cols

然后我们需要

每一个都要判断进去

对 我们需要把得到的结果

给压进去

self点 点neighbor count

neighbor count

row col

对的

然后没完成这一步之后

我们需要把得到的这个

这个数组

append进去

最终返回整个数组

这个函数应该还是比较显然的

是的

好 那我们应该也

也就已经完成了整个生命游戏

所需要的全部函数

对 我们

我们测试一下整个程序吧

由于main函数什么的

是已经提供好的

我们可以直接调用

直接在python里面写一下

看看结果

我们可以看到它正在迭代

对 然后规则的话

应该也是按照我们预想的

在执行

大体上没有问题

然后这就是结对编程

在编写生命游戏的

一个模块中的一个具体实现

嗯 是这样的

软件工程课程列表:

第1章 初识软件工程

-1.1 软件无处不在

--讲课视频

-1.2 软件的本质特性

--讲授视频

-1.3 软件工程的产生与发展

--讲授视频

-1.4 软件工程的基本概念

--讲授视频

-1.5 软件质量实现

--讲授视频

-1.6 业界人士谈软件工程

--海芯科技创始人施侃乐访谈

-测验题--作业

-讨论题

--讨论题

-作业题

--第一张 作业题

第2章 编写高质量代码

-2.1 编程过程与规范

--讲课视频

-2.2 良好的编程实践

--讲课视频

-2.3 Python集成开发环境

--讲课视频

-2.4 代码静态检查

--讲课视频

-2.5 代码性能分析

--讲课视频

-2.6 结对编程实践

--讲课视频

-2.7 刘贺谈软件工程

--讲课视频

--讨论

-测验题--作业

-作业题

--第二章 作业题

第3章 单元测试

-3.1 单元测试概述

--讲课视频

-3.2 黑盒测试方法

--黑盒测试方法

-3.3 白盒测试方法

--基本概念

--代码覆盖标准

--基本路径测试

-3.4 单元测试工具

--单元测试工具

--html

-测验题--作业

-作业题

--第三章 作业题

--作业题附件

第4章 软件开发过程

-4.1 软件过程

--讲课视频

-4.2 软件过程模型

--讲课视频

-4.3 敏捷开发过程

--讲课视频

-4.4 微软公司开发过程

--邹欣经理自我介绍

--微软开发过程之一

--微软开发过程之二

-测验题--作业

第5章 团队开发管理

-5.1 团队组织与管理

--讲课视频

-5.2 项目沟通管理

--讲课视频

-5.3 软件项目计划

--讲课视频

-5.4 软件项目估算

--讲课视频

-测验题--作业

-讨论题

--讨论

第6章 敏捷开发与配置管理

-6.1 敏捷开发之Scrum

-- 敏捷开发之Scrum

--html

-6.2 用户故事与估算

--讲课视频

-6.3 团队协作工具Tower

--Tower工具介绍(1)

--Tower工具介绍(2)

-6.4 配置管理

--讲课视频

-6.5 配置管理工具Git

--讲课视频

-测验题--作业

-作业题--作业

第7章 需求获取

-7.1 需求工程师

--讲课视频

-7.2 需求定义

--讲课视频

-7.3 需求的类型

--讲课视频

--讲课视频(2)

-7.4 需求工程过程

--讲课视频

-7.5 需求的主要来源

--讲课视频

-7.6 需求获取技术

--讲课视频

--讲课视频二

--讲课视频三

-7.7 撰写需求文档

--讲课视频

-测验题--作业

-讨论题

--讨论

第8章 用例建模

-8.1 用例建模概念

--讲课视频

-8.2 用例建模过程

--讲课视频

-8.3 用例建模精讲

--讲课视频

-8.4 建模工具介绍

--讲课视频

-8.5 微信抢票应用案例

--讲课视频

-测验题--作业

-讨论题

--讨论

第9章 面向对象分析与设计

-9.1 面向对象分析

--讲课视频

-9.2 CRC卡片分拣法

--讲课视频-1

--讲课视频-2

-9.3 面向对象设计

--讲课视频-1

--讲课视频-2

-9.4 类图建模

--讲课视频-1

--讲课视频-2

-第9章 面向对象分析与设计--测验题

-讨论题

--讨论

第10章 行为建模

-10.1 顺序图概念

--讲课视频

-10.2 顺序图建模

--讲课视频

-10.3 顺序图风格

--讲义视频

-10.4 状态建模

--讲课视频

-10.5 状态图

--讲课视频

-10.6 状态图精讲

--讲义视频

-测验题--作业

-讨论题

--讨论

第11章 软件系统设计

-11.1 软件体系结构概念

--讲授视频

-11.2 软件设计原则

--讲授视频

-11.3 软件体系结构风格(一)

--讲授视频

-11.4 软件体系结构风格(二)

--讲授视频

-11.5 软件体系结构风格(三)

--讲授视频

-11.6 软件设计过程

--讲授视频

-11.7 Web系统架构设计

--讲授视频

-11.8 数据库选择策略

--讲授视频

-测验题--作业

-作业题

--html

--html

--html

-作业题--作业

第12章 软件交互设计

-12.1 交互设计概述

--讲授视频

-12.2 交互设计目标

--讲授视频

-12.3 GUI设计原则

--讲课视频

-12.4 KLM效率模型

--Video

-12.5 Fitts定律

--讲授视频

-12.6 交互设计过程

--讲授视频

-测验题--作业

第13章 软件系统测试

-13.1 软件测试概念

--讲课视频

-13.2 软件测试类型

--讲课视频

-13.3 软件功能测试

--讲课视频

-13.4 软件性能测试

--讲课视频

-测验题--作业

第14章 软件交付与维护

-14.1 软件部署与交付

--讲课视频

-14.2 软件演化与维护

--讲课视频

-测验题--作业

第15章 期末考试与总结

-第一部分:基础知识

-第二部分:编程与测试(选做)

--编程与测试(选做)

讲课视频笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。