当前课程知识点:软件理论与工程 > 第6章 项目管理 > 6.4 项目版本控制及调试 > 6.4 项目版本控制及调试
大家好
我们今天继续软件理论与工程
第六章 第四节 项目版本控制及调试
我是主讲人 高广宇
我们上一节课的时候
其实也提到了版本控制的内容
我们今天主要给详细的介绍一下版本控制
以及软件调试相关的内容
首先 什么是版本控制
版本控制的英文叫VSC
全称叫Version Control Systems
什么是版本控制
版本控制
它实际上是完整的记录一个文件集合
当然文件可以指代是代码
也可以指代是各种各样
我们前期的软件工程的文档
它整个随着时间变化
它的一个变化过程
这就是版本控制
所以说 早期的版本控制
是通过什么样的方式
通常是通过各种各样文件命名的方式
来实现的
为什么我们又需要版本控制
从图示上
我相信大家感同身受
因为我们在很多时候
当我们要完成文档
完成代码的时候
我们在不断的修改的过程中
就是随着时间前面提到的
我们是通过不断的去更改命名的方式
去对它进行修改
但是大家会发现一个问题
就是说有的时候
我们已经命名到final了
发现 好像我们又得修改
时候又变成final final
但时候发现
永无止境
没有任何一个规范可言
甚至到了最后
你自己连哪个是最新的
都已经搞不清楚了
所以在这种情况下
实际上 我们如果有版本控制
我们实际上就能够有效地解决问题
就能很规范化的管理起来
我们的代码和文档
所以说 我们为什么要使用版本控制
第一个是 我们容易去回溯
我们前期的文档
或者程序的版本
第二个是比较适合
大家去做共同协作开发
还有我们能够去回溯
或者说 寻找到整个软件或者文档
它的一个变化的一个过程
也适合去做一个备份
或者说一个文件的一个恢复
所以 这是版本控制的特点或者优势
图是我们的GitHub上的
一个版本控制的图示
有些同学经常用GitHub
当然就知道它的优势或者特点
知道它干嘛的
到底谁需要版本控制
或者谁在用版本控制
实际上 在整个软件工程过程中
所涉及到的所有人员
都可能需要用到版本控制
比如说程序员
它需要通过版本控制
来管理整个项目或者系统
包括所有的文档和程序
使用者
application 应用
也需要版本控制
包括咱们普通用户
我们平时在撰写Word
或者说PPT Excel表格的时候
我们都希望有一个版本控制
能够来保持它的一个更改变化的追溯
当然还包括管理人员
所以 实际上版本控制
它的应用范围非常广泛
它的使用者也非常的多
版本控制的话
通常包含两大类
一类就是集中式的版本控制
对于这类版本控制而言
它通常是有一个单一的叫central
一个中心的节点
然后来存储所有的资源
然后 每一个开发者
它拥有一个自己的local working copies
就有一个局部的工作
拷贝一个备份
它在它的上面去更改
但是是从central
集中式的节点
所复制和down下来的
对于这种集中式的控制来说
它的图示 如PPT所示
比较典型的集中式的版本控制的
系统或者工具
有包括像CVS和SVN
尤其是SVN
原来实用的是非常广泛
它们控制的一个工具或者一个软件
集中式的版本控制
它的一个工作流程
这里面有一个例子
我们首先看一下
比如说 我现在要作为一个普通的用户
我要去修改我的一个
比如说一个工程
我怎么做
我首先update
我的一个拷贝
简单来说就是
我首先从集中式的服务器上down下来
或者去更新一下最新的一个版本
然后 我在上面去做编辑
做修改
修改完了之后
我再去update一下
服务器上的版本
当然时候
因为别的人可能也在同时进行修改甚至上传
所以 有可能出现就是
原来修改的个版本
跟你最近down版本之间有不一致
所以时候
如果有必要的话
需要把改变去进行合并
合并完了之后
你确定完了最后的一个版本
然后 你把它去做一个commit
做一个提交
因为可能就是说你的修改
跟你最后一次从服务器上down下的版本的修改
合并到一起
你提交到服务器
服务器当然还需要去处理冲突或者并发
因为其他的合作者
可能同样在做修改和提交
所以 他也需要去做
所以修改了合并
对于这种集中式的版本控制
它其实有一些问题
比如它的一些问题包括
第一个就是
如果我没有网络环境怎么办
因为它需要不断地去从个服务器上
update最新的version
最新的一个版本
如果没有网络怎么办
还有一个如果我要提交的
是一个非常大的一个更改 一个变动
我需要不断地跟个服务器
去update
去download 去commit
其实这个时候依赖于整个网络带宽
网络的环境影响也会很大
还有一个就是说
如果我想去知道整个项目或者工程
它的一个变化的一个历史记录
怎么办
实际上在集中式版本控制里面
只有中心服务器节点是知道的
对于普通的一个developer
每个开发者来说其实是
没有完整的历史信息的
所以整个集中式的版本控制
尤其在面向大规模软件开发的时候
是面临诸多的问题的
因此 就衍生出来
所谓的distributed分布式的版本控制
当然集中式和分布式
在整个计算机领域里面
是一个经常成对出现的两个词
分布式大家也很容易理解
自然而然就是说它不像集中式一样
有一个单一的服务器的集中节点
而是 大家分布的共同来承担
几乎相当的责任
共同来维护一个核心
所以说 在分布式的版本控制里面的话
每一个开发者自己的本地机器上
都维护了一个完整的工程的一个copy
也就是说
我的所有的更改的日志
都是在这有记录的
然后 在没有网络的环境下
我也可以去跟我本地的这个仓库
去进行一些交互
然后 来完成我相应的工作
这就是所谓的分布式的概念
也就是分散在各自的节点上
每一个节点之间
基本上可以理解为是公平的 平等的
当然 它也有一个简单的
轻量级的master节点
来共同协调不同的节点
不同的局部节点之间的交互
在这个时候
它要进行一个任务交互的时候
它的方式就是首先
我从master节点上
先去down下来一个最新的仓库
然后 我更新我自己的版本
我编辑它
然后我再提交它
同样 如果有必要的话
我再去跟我master节点
也就是跟所有的网络上其它节点去交互
去进行一个更新
在没有网络的情况下
我也可以跟自己的本地仓库
去进行版本的更改 控制或更新等等
对于集中式的版本控制和分布式的版本控制
其实各有优缺点
也谈不上说 谁绝对好 谁绝对的不好
通常是依赖于我们实际的
一个开发的任务
有的时候
可能集中式的开发构成 效率很高
然后 能够有效地去完成任务
因为有的时候应用分布式的这种方式
它也有问题
因为它需要去在每个机器上都留一个存储
一个备份
整个系统还是比较庞大的
对于中小软件和中小系统的开发来说
有的时候不见得是必然的
所以说
像集中式的版本控制
现在其实还是有很大的用武之地的
这个需要去理解分布式的版本控制
包括因为大家知道分布式版本控制里
比较经典的就是像Git
像包括有名的 像GitHub等等
好像是非常广泛
但是并不见得它是绝对的好
大家需要去理解
除了版本控制之外
我们接下来再讲一下软件调试
软件调试的话
我们首先 稍微区分几个概念
软件调试需要跟验证Validation要区分一下
Validation 是去发现问题
同时 去增加可行性
而Debugging是要去找出来问题
同时要把问题给解决掉
而Debugging还跟一个概念特别容易混淆
就是测试 Testing
测试是要去找出问题
但是 它并不关注于问题的解决
Debugging不一样
Debugging是要找出了问题
而且要精准到问题的一个位置
同时 要去找出来导致问题的原因
而且我们还要想办法去解决它
这是Debugging要做的事
而且Debugging在很多时候
在程序员或者说在开发人员角度去考虑
像Testing 通常是在设计人的角度考虑的比较多
那Debugging
它是要去找Bug
Bug实际上这是个英文词了
它为什么叫Bug
有一个渊源
大概在上个世纪40年代的
那个时候的计算机是电子计算机
它体系非常的庞大
它也是需要通过电子元器件的机械
或者电子的运算
才能去得到最终的数据的计算
然后 它所有的这些程序代码
数据的输入输出
也是需要通过指代
通过其它的东西去输入
它中间日志过程也不像现在
它不会自动去输出
需要人为去记录
在上个世纪40年代
有一个工程师叫Grace
他在记录日志的过程中发现
计算机发生了一次故障
然后 当他在寻找问题的时候发现
导致计算机故障的原因
是某一个电子元器件的位置
有一个bug
bug的英文叫虫子
有一个小虫子导致电路短路了
所以没法去运行
所以 当他手工描述日志记录文档的时候
他就把小虫子贴到了上面
同时详细地描述了问题
因此 我们就对计算机运行过程中
导致它发生错误的东西叫什么
叫bug
当然这只是一个说法
并不是绝对的是这么个意思
大家有这么一个传统 就这么去描述
Debugging实际上就是说去找
使得整个程序发生问题的一个位置以及原因
就是bug
对于一个bug来说
它是怎么样产生的
它通常
最开始是来自于人
也就是说程序员写代码的时候的错误
才留下了一些隐患
同时 因为这些隐患
导致我们整个计算过程中就发生错误
这些计算的错误
使得最后我们程序的功能实现
就会出现问题
使得它最后呈现出来的结果
就发生了一些不一致
或者发生了错误
所以 我们整个Debugging
是在我们找到或者发现了
这种可看见的错误
或者不一致了之后才开始的
我们这个是要反过去找
到底在哪个位置
发生了什么问题
所以说 我们怎么样去做Debugging
或者怎么样去防止
这些错误的发生
通常来说
其实除了Debugging之外
Debugging其实是最后一道关卡
我们为了防止错误的发生
也有其它的方式
比如说我们使得错误
在最开始的时候
就不可能被发生
比如说通过语言机制
我们都知道Java语言
就自己的内存管理机制
它能够防止内存泄露
第二个是说
我们尽量地去确保这些错误
在第一次出现的时候就被发现
不让它在后面发生
第三个来说
因为错误 你不能让它被隐藏
你必须让它在第一时间 可见
就尽快地呈现出来
这样才能在最开始的时候
我们去解决它
当然我们最后一道关卡就是Debugging
如果它真的发生了
这个时候再回过去去找它
到底在哪
对于整个Debugging来说
我们也有各种各样的方式
刚才前面也提到了
比如说 我们这里面有一个例子
我们这是一个简单的语句
我们一个while循环
我们去判断一下
对于一个数组中到底有没有k的元素
这里面是说
int i等于0
while true就循环
我们用了一个永真的条件
然后去判断
如果说a[i] i++
不断去迭代
如果有一个a[i]==k
我就认为我找到了 我就break
这个 程序简单看起来没有什么问题
没有什么错误的
本身也没什么错误
你去Debugging
你去调试什么应该也是能过得去的
但是 这个程序
实际上是隐藏了一个bug的
为什么这么说
大家看到
因为 现在是一个永真的判别条件
循环的退出完全依靠if语句
所以 如果if语句不成立的情况
永远不成立的情况下
这个循环就变成一个死循环了
而且永远在这就出不去了
而且 虽然你认为就是说
写程序的时候认为我们的应用场景
它可能是应该会有
k应该是能出去的
但是 当我们换了一个应用场景的时候
你不能够一直确保条件是存在了
所以 像这个就是隐藏的bug
但实际上在最早期的时候
其实是不太容易被发现
也没法通过语言机制去发现
怎么办
实际上我们可以做一些小的调整
比如说
我刚才发现
它是因为循环退不出去的这种问题
我们加一个简单的判别条件
如果i小于数组的长度的时候
我们就可以去执行循环
如果它超过长度了
我人为的 让它去退出
其实也是没有问题
至少能够确保它避免死循环问题
但实际上
对于这个代码来说
它还会有问题
大家可以自己想一下
看一下是什么问题
实际上 我们能够发现
我们现在 如果循环退出了
前面 用永真的方式的时候
我们知道循环退出是因为break出来的
现在整个循环出去了
有两个出口
一个是break
一个是i大于等于a.length
所以 当循环退出的时候
就不确定是通过哪个出口出来的
如果我后续的代码
又依赖于前面的两个条件里面
我要确定是因为哪个条件
在这种情况下
这个代码就有可能有问题
就是有可能数组里
没有k元素
但是也退出来了
所以在这种情况下
这个代码其实也是有一定的问题
有这个bug
实际上
我们从Debugging角度上来说
我们可以加入一些assert
也就是 中文叫断言
循环退出了之后
加一个断言去判断
它到底是通过哪个条件出来的
或者说通过哪个条件出来之后
我是不要对程序做一个中断
就比如说如果没有k值
后面的操作就没有意义了
就中断它了 不执行它了
所以 这个是关于我们在Debugging过程中
比如说通过刚才说的这种不同的
去更改程序的一个编写方式
或者加入断言
加入其它工具的操作过程
然后 能够有效地去避免问题
到底我们在什么时候
可能我们需要去 像加入断言
或者去做这么一个事
实际上 这个东西也不是尽然的
跟实际的场景有关系
有的时候 我需要加这么一个断言
因为如果它不满足的条件就直接退出
因为后面有的程序
可能严格依赖于这个条件
有的时候可能就是说
这个bug或者这个断言
对于整个程序来说
它的整个系统来说
它的影响不大
我后面的程序对它依赖性很小
但在这种情况下
其实我不用加断言
就让它从哪个出口出去都无所谓
所以 对于这种纠正
或者对于这种debug的纠正
其实 它是依赖于一个上下文的一个环境的
在这里最后
再给大家讲一个就是
这个Debugging的一个好处
或者说它到底会有什么问题
其实一个最典型的问题就是说
它的灾难性的后果
比如说在欧洲的Ariane-5
这个火箭发射的过程
当时 就是因为一个小小的bug所导致的
它当时是因为
它把Ariane-4的这一个程序
直接挪到了Ariane-5上来用
但是 它忽略了整个Ariane-5上用的处理器变了
它的一个字段的长度发生了变化
所以 发生了溢出
像这种问题的话
如果通过编译或者说检查代码
是检查不出来的
我们就需要去通过什么
通过Debugging的方式
认真地去在中间
加入像断言
或者其它的判断
或者操作的方式
去避免这样的问题的发生
这个就是一个Ariane-5的火箭的示意图
这就是刚才提到的灾难性的一个后果
我们这一节 包括我们这一章
就讲到这里
好 谢谢大家
-课程概述
-1.1 软件的本质
-1.2 软件工程
--1.2 软件工程
-1.3 软件过程结构
-1.4 过程模型
--1.4 过程模型
-1.5 敏捷开发方法
-第1章 习题
--第1章 习题
-2.1 需求工程过程
-2.2 需求获取
--2.2 需求获取
-2.3 需求分析
--2.3 需求分析
-2.4 过程建模
--2.4 过程建模
-2.5 面向对象建模
-第2章 习题
--第2章 习题
-3.1 设计概述
--3.1 设计概述
-3.2 设计的概念
-3.3 设计模型元素
-3.4 体系结构概述
-3.5 体系结构风格
-3.6 构件级设计
-3.7 UI设计
--3.7 UI设计
-3.8 基于模式的设计
-第3章 习题
--第3章 习题
-4.1 UML概述
-4.2 UML 及UML中的事物
-4.3 UML关系和图
-4.4 UML 图细节(上)
-4.4 UML 图细节(下)
-第4章 习题
--第4章 习题
-5.1 软件测试策略
-5.2 测试传统的应用系统
-5.3 测试面向对象的应用系统
-5.4 测试web应用系统
-5.5 测试移动应用系统
-第5章 习题
--第5章 习题
-6.1 软件项目估算
-6.2 软件过程管理
-6.3 软件配置管理
-6.4 项目版本控制及调试
-第6章 习题
--第6章 习题