软件工程
2020-05-14 15:00:25 1 举报
AI智能生成
2020春《软件工程》课程脑图
作者其他创作
大纲/内容
团队开发管理
团队组织与管理
人员选择
越上面的越重要
• 应该考虑团队中的技术、经验和个性是否整体均衡 。
• 选择性格互补的成员组成的团队可能比仅仅根据技术能力选择成员的团队更有效率。
• 团队的领导力来自于成员的尊重,而不是名义上的头衔。
• 选择性格互补的成员组成的团队可能比仅仅根据技术能力选择成员的团队更有效率。
• 团队的领导力来自于成员的尊重,而不是名义上的头衔。
团队的概念
团队是由若干人组成的一个群体,他们具有互补的技能,对一个共同目的、
绩效目标及方法做出承诺并彼此负责。
绩效目标及方法做出承诺并彼此负责。
• 设定具有挑战性的团队目标
• 营造一种支持性的环境(沟通交流,团队学习)
• 团队成员的自豪感
• 让每一位成员的才能与角色相匹配
• 正确的绩效评估
• 营造一种支持性的环境(沟通交流,团队学习)
• 团队成员的自豪感
• 让每一位成员的才能与角色相匹配
• 正确的绩效评估
建立高效的项目团队
- 召开项目会议
- 确立团队身份
- 创建共同的目标
- 管理决策制定
- 建立奖励体系
- 管理冲突
- 激发项目团队活动
团队建设活动
绩效评估
绩效评估是通过对团队成员工作绩效的考察与评价,
反映团队成员的实际能力和业绩以及对某种工作职位的适应度。
反映团队成员的实际能力和业绩以及对某种工作职位的适应度。
项目沟通管理
沟通的重要性
普林斯顿大学研究发现:“智慧、专业技术、 经验三者只占成功因素的25%,其余75%决定于良好的人际沟通。”
沟通的概念
沟通是为了达到一定的目的,将信息、思想、情感在个人或群体之间进行传递或交流的过程。
目的
- 交换有价值的信息
- 获得对方的理解和支持
沟通是你被理解了什么而不是说了什么
沟通的复杂性
n 个人的沟通渠道数量 = n(n – 1)/ 2
Brooks法则
向一个进度延迟的软件项目中增加人员可能会使其进度更加推迟
沟通的模型
常用沟通方式
口头
谈话、讨论、演讲、汇报、谈判、会议
肢体语言:面部表情、手势、眼神、姿态、仪表
观察他人的肢体语言获得他人的信息,同时也要注意管理自己的肢体语言!!!
观察他人的肢体语言获得他人的信息,同时也要注意管理自己的肢体语言!!!
书面
合同、报告、会议纪要、报表、备忘录、便条
电子
电话、Email、BBS、Blog、即时通讯、协作工具
项目沟通管理
项目沟通管理是为了确保项目信息及时且恰当地收集和传递,
对项目信息的内容、传递方式和传递过程等进行的管理活动。
对项目信息的内容、传递方式和传递过程等进行的管理活动。
项目团队内部的沟通
沟通需求:
• 职责、协调、状态、授权
• 任务分配清晰
工作会议:
• 项目启动会、成员进度汇报、项目进展会
• 设置沟通期望
• 及时、公开、恰到好处
• 职责、协调、状态、授权
• 任务分配清晰
工作会议:
• 项目启动会、成员进度汇报、项目进展会
• 设置沟通期望
• 及时、公开、恰到好处
与管理层和客户的沟通
• 和谁进行沟通?为什么?
• 需要什么类型的信息?
• 详尽程度和频率如何?
• 沟通的目标是什么?
• 采用何种方式沟通比较好?
• 需要什么类型的信息?
• 详尽程度和频率如何?
• 沟通的目标是什么?
• 采用何种方式沟通比较好?
项目会议
项目启动会议
(至关重要)
(至关重要)
• 目标
- 项目概况:范围与目标、总体进度、方法和程序 - 确定项目人员的角色和任务
- 确立团队的工作模式
• 形式
- 重大项目:精心准备、集中1-2 天;前期介绍与建立基本规则
- 一般项目:简单有效;回顾项目范围与成员互相自我介绍
• 建立基本规则
- 计划决策、追踪决策、管理变动决策、关系决策
- 项目概况:范围与目标、总体进度、方法和程序 - 确定项目人员的角色和任务
- 确立团队的工作模式
• 形式
- 重大项目:精心准备、集中1-2 天;前期介绍与建立基本规则
- 一般项目:简单有效;回顾项目范围与成员互相自我介绍
• 建立基本规则
- 计划决策、追踪决策、管理变动决策、关系决策
项目计划会议
(通常在每一阶段开始时)
(通常在每一阶段开始时)
• 制定当前阶段的项目计划
• 将工作任务明确分配给项目成员
• 将工作任务明确分配给项目成员
项目阶段进展会议
(每月或每季度一次)
(每月或每季度一次)
• 向项目干系人和高层管理者汇报项目进展
• 解决需要高层管理者支持的问题
• 解决需要高层管理者支持的问题
项目组工作例会
(每日或每周一次)
(每日或每周一次)
• 通报项目组成员的工作进展
• 了解成员在工作中遇到的困难,并寻找资源解决
• 确定后续的工作计划
• 了解成员在工作中遇到的困难,并寻找资源解决
• 确定后续的工作计划
召开有效的会议
三星公司开会的三个原则:
1. 周三不开会;
2. 会议时长1小时,最多不超过1.5小时;
3. 将会议的内容整理成一张纸。
三星公司开会的七项规定:
1. 严格遵守时间;
2. 在会议材料中写明投入会议的经费;
3. 参加者限制为必要的合适人选和负责人,会议规模尽可能小;
4. 明确会议目的;
5. 事先分发会议资料;
6. 要让所有参加者发言;
7. 尽可能地减少会议记录,仅记录决定了的事项并进行保存。
1. 周三不开会;
2. 会议时长1小时,最多不超过1.5小时;
3. 将会议的内容整理成一张纸。
三星公司开会的七项规定:
1. 严格遵守时间;
2. 在会议材料中写明投入会议的经费;
3. 参加者限制为必要的合适人选和负责人,会议规模尽可能小;
4. 明确会议目的;
5. 事先分发会议资料;
6. 要让所有参加者发言;
7. 尽可能地减少会议记录,仅记录决定了的事项并进行保存。
项目绩效报告
项目绩效报告是收集和发布关于项目进展等绩效信息,
通常使用文字说明、 曲线图形和报表等形式进行描述。
通常使用文字说明、 曲线图形和报表等形式进行描述。
远程协作开发的沟通问题
问题
缺少亲切感
不经常交流
反馈比较慢
解决
• 尽可能保证清楚地表达
• 保持经常不断地交流
• 保持立刻反馈和文件共享
关键:交流、交流、再交流
• 保持经常不断地交流
• 保持立刻反馈和文件共享
关键:交流、交流、再交流
软件项目计划
软件项目计划是对软件项目实施所涉及的活动、资源、任务、进度等进行规划。
按时交付是软件项目的最大挑战,合理地安排进度是软件项目计划的关键内容。
按时交付是软件项目的最大挑战,合理地安排进度是软件项目计划的关键内容。
计划流程
开发问题描述
问题描述是描述系统应该说明的问题、目标
环境、客户交付和验收标准的简短文档。
问题描述是对系统所表述问题的共同认识, 通常是由项目团队和客户共同开发形成的,
它定义了问题提出的背景、需要支持的功能和性能以及系统运行的目标环境等。
环境、客户交付和验收标准的简短文档。
问题描述是对系统所表述问题的共同认识, 通常是由项目团队和客户共同开发形成的,
它定义了问题提出的背景、需要支持的功能和性能以及系统运行的目标环境等。
定义顶层设计
顶层设计描述了最初从系统到子系统的分解,它描述了系统的软件体系结构。
• 子系统分解应该是高层的,专注于功能,并且要保持稳定。
• 每一个子系统可以被分配给一个团队或一个人,由他负责其定义和实现。
• 子系统分解应该是高层的,专注于功能,并且要保持稳定。
• 每一个子系统可以被分配给一个团队或一个人,由他负责其定义和实现。
=
定义项目工作分解
项目工作分解是将项目整体分解成较小的、易于管理和控制的若干子项目或工作单元,
直到可交付成果定义的足够详细,足以支持项目将来的活动。
直到可交付成果定义的足够详细,足以支持项目将来的活动。
建立初始时间表
在项目工作分解的基础上,进一步估算活动所需的时间和资源,
并按照一定的顺序将这些活动进行组织和调度,从而创建项目的进度计划表。
并按照一定的顺序将这些活动进行组织和调度,从而创建项目的进度计划表。
制定进度计划需要在资源、时间和实现功能之间不断平衡,并需要定期更新。
软件项目估算
用例建模
用例建模概念
为什么需要用例建模
描述系统的功能性需求
• 关联干系人需要以及软件需求
• 确认与系统交互的人或对象(参与者)
• 定义系统的边界
• 捕捉和传达系统的理想行为(用例)
• 验证或确认需求
• 规划工具
• 确认与系统交互的人或对象(参与者)
• 定义系统的边界
• 捕捉和传达系统的理想行为(用例)
• 验证或确认需求
• 规划工具
用例图的主要元素
用例建模过程
(1) 找出系统外部的参与者和外部系统,确定系统的边界和范围;
(2) 确定每一个参与者所期望的系统行为;
(3) 把这些系统行为命名为Use Case;
(4) 使用泛化、包含、扩展??等关系处理系统行为的公共或变更部分;
(5) 编制每一个Use Case的脚本;
(6) 绘制Use Case图;
(7) 区分主事件流和异常情况的事件流,可以把表示异常情况的事件流作为单
独的Use Case处理;
(8) 细化Use Case图,解决Use Case间的重复与冲突问题。
(2) 确定每一个参与者所期望的系统行为;
(3) 把这些系统行为命名为Use Case;
(4) 使用泛化、包含、扩展??等关系处理系统行为的公共或变更部分;
(5) 编制每一个Use Case的脚本;
(6) 绘制Use Case图;
(7) 区分主事件流和异常情况的事件流,可以把表示异常情况的事件流作为单
独的Use Case处理;
(8) 细化Use Case图,解决Use Case间的重复与冲突问题。
面向对象分析与设计
向对象分析
(Object-Oriented Analysis, OOA)
(Object-Oriented Analysis, OOA)
• 向对象分析技术关注应用领域中的实体,并将其建模为对象
• 向对象分析技术主要基于分类、泛化、聚合关系在对象集合之间建立结构
• 对象的行为是执行预定的动作(服务/活动)
• 对象通过执 动作来完成状态变迁
• 向对象分析技术主要基于分类、泛化、聚合关系在对象集合之间建立结构
• 对象的行为是执行预定的动作(服务/活动)
• 对象通过执 动作来完成状态变迁
软件系统建模原则
支持修改和重用
有经验的工程师重用已有的设计
• 模型组件重用
• 模型结构重用
有智慧的工程师规划未来
• 建立可重用的模型组件
• 建立易于修改的模型结构
• 模型组件重用
• 模型结构重用
有智慧的工程师规划未来
• 建立可重用的模型组件
• 建立易于修改的模型结构
五大有力武器
抽象:关注重点,暂忘细节
• 在对象间找出共性,忽略不相关细节
• 关注对象间的一般/特殊关系
• 将具有相同属性或角色的对象放入同一个类集合中
• 通过父子关系,将由共性的类定义为同一个父类的子类
• 关注对象间的一般/特殊关系
• 将具有相同属性或角色的对象放入同一个类集合中
• 通过父子关系,将由共性的类定义为同一个父类的子类
继承(一般->特殊结构)
• 一般-特殊结构将类组织成基于继承关系的分类层次结构
• 自底向上是从特殊到 般的类 (generalization)
• 自顶向下是从 般到特殊的类(specialization).
• 自底向上是从特殊到 般的类 (generalization)
• 自顶向下是从 般到特殊的类(specialization).
分解:将问题切分,分而治之
表达整体部分关系,细分为聚合和组合
多视角映射:切换关注点,分别讨论
从多个视角分别建模问题的不同方面,
一如建筑施工中的不同视角的图纸
一如建筑施工中的不同视角的图纸
例如:
需要进行飞行器需求建模,投影建议分别建模:
安全性、容错性、实时性 ...
需要进行飞行器需求建模,投影建议分别建模:
安全性、容错性、实时性 ...
投影 vs 分解
• 分解定义整体-部分关系
• 投影定义视图
• 分解的假设是子模块间依赖性较小
• 投影定义视图
• 分解的假设是子模块间依赖性较小
模块化:建立稳定结构、缩小变化影响范围
模式:多次成功应用的模型结构
CRC 卡片分拣法
CRC是类(Class)、责任(Responsibility)和协作(Collaboration)的简称,
CRC分析法根据类所要扮演的职责来确定类
CRC分析法根据类所要扮演的职责来确定类
类识别
从名词开始
类对象通常对应一个命名实体,因此,用名词表达,于是我们先从名词开始
切记:别想一步到位
切记:别想一步到位
对象几乎无处不在
• 外部实体
与建模中的系统存在交互的人,设备和其他系统
• 事物
建模的应用领域中存在的事物:报表,信号,文字输出
• 事件
系统上下文中发生的资源传递,控制命令发出
• 角色
由与系统交互的人扮演的角色
• 组织单元
与应用领域相关的部分:分支机构、群组,团队
• 位置、地点
建模中的问题的物理上下文:厂房、车间、货架
• 结构体
定义类或者对象组合:传感器、车辆、计算机
不能定义为对象的事物:
• 过程 (打印、转换)
• 属性 (蓝色, 50Mb)
与建模中的系统存在交互的人,设备和其他系统
• 事物
建模的应用领域中存在的事物:报表,信号,文字输出
• 事件
系统上下文中发生的资源传递,控制命令发出
• 角色
由与系统交互的人扮演的角色
• 组织单元
与应用领域相关的部分:分支机构、群组,团队
• 位置、地点
建模中的问题的物理上下文:厂房、车间、货架
• 结构体
定义类或者对象组合:传感器、车辆、计算机
不能定义为对象的事物:
• 过程 (打印、转换)
• 属性 (蓝色, 50Mb)
从原始资料中识别类:
从其他来源识别类:
• 背景信息调查
• 用户及干系人提供
• 分析模式
最好识别出尽可能多的候选类
• 之后逐步按照其价值功用进行选择
• 明确判断后排除一个类要比压根不考虑来的合理
- 找出干系人提交的问题描述中名词及短语
- 如果他描述应用领域中的信息结构或本质,则加入模型
从其他来源识别类:
• 背景信息调查
• 用户及干系人提供
• 分析模式
最好识别出尽可能多的候选类
• 之后逐步按照其价值功用进行选择
• 明确判断后排除一个类要比压根不考虑来的合理
识别类的功能职责
功能职责关乎行为动作,因此是问题描述中的动词。
注意:
1. 并非所有动词均将成为类职责
2. 有时多个动作合并为一个职责
3. 随着分析过程深入会发现新的职责
4. 不断修正类定义和职责定义
5. 当两个类分享职责时,为二者同时添加该职责
注意:
1. 并非所有动词均将成为类职责
2. 有时多个动作合并为一个职责
3. 随着分析过程深入会发现新的职责
4. 不断修正类定义和职责定义
5. 当两个类分享职责时,为二者同时添加该职责
识别类交互协作关系
用UML用例图
注意: 目的并非写出所有场景,而是对类和职责定义进行精化
面向对象设计
关于对象设计
面向对象方法本身并不能保证你的设计成为优秀的设计
面向对象设计过程
• 进行适当的领域分析
• 撰写问题描述,确定系统的开发任务
• 基于问题描述抽取需求
• 开发用户界面原型
• 识别对象类
• 定义每个类的职责
• 确定类之间的交互关系
• 建立系统的设计模型
• 撰写问题描述,确定系统的开发任务
• 基于问题描述抽取需求
• 开发用户界面原型
• 识别对象类
• 定义每个类的职责
• 确定类之间的交互关系
• 建立系统的设计模型
向对象思维方式的
核心理念
核心理念
区分接口与实现
从具体到抽象
最小接口原则
开闭原则
软件实体在扩展性应该是开放的, 在更改性应该是封闭的。
依赖倒置原则
依赖倒置原则指的是依赖关系应该是尽量依赖接口(或抽象类),
而不是依赖于具体类。
而不是依赖于具体类。
接口分离原则
在设计时采用多个和特定客户类(client)有关的接口要比采用一个通用的
接口要好。
接口要好。
好的系统设计的特征
类图建模
行为建模
软件系统设计
软件体系结构概念
软件的复杂性
编写程序的主要考虑点
处理复杂性
随着软件系统的规模和复杂性不断增加,对系统的全局结构设计和规划变得
比算法的选择和数据结构的设计明显重要得多。
比算法的选择和数据结构的设计明显重要得多。
软件体系结构概念
软件体系结构(Software Architecture)包括
- 构成系统的设计元素的描述、
- 设计元素之间的交互、
- 设计元素的组合模式
- 以及在这些模式中的约束。
软件体系结构 =构件 +连接件 +约束
• 如何将复杂的软件系统划分成模块
• 如何规范模块的构成
• 如何将这些模块组织成为完整的系统
• 如何保证系统的质量要求
• 如何规范模块的构成
• 如何将这些模块组织成为完整的系统
• 如何保证系统的质量要求
构件
构件是具有某种功能的可复用的软件结构单元,
表示系统中主要的计算元素和数据存储。
表示系统中主要的计算元素和数据存储。
连接
连接是构件间建立和维护行为关联与信息传递的途径。
连接件
连接件表示构件之间的交互并实现构件之间的连接。
软件体系结构的目标
风格、模式和框架
• 体系结构风格:用于描述某一特定应用领域中系统组织的惯用模式,反映了领域中众多系统所共有的结构和语义特性。
• 设计模式:描述了软件系统设计过程中常见问题的一些解决方案,通常是从大量的成功实践中总结出来的且被广泛公认的实践和知识。
• 软件框架:软件框架是由开发人员定制的应用系统的骨架,是整个或部分系统的可重用设计,由一组抽象构件和构件实例间的交互方式组成。
• 设计模式:描述了软件系统设计过程中常见问题的一些解决方案,通常是从大量的成功实践中总结出来的且被广泛公认的实践和知识。
• 软件框架:软件框架是由开发人员定制的应用系统的骨架,是整个或部分系统的可重用设计,由一组抽象构件和构件实例间的交互方式组成。
框架和体系结构的关系
• 体系结构的呈现形式是一个设计规约,而框架则是“半成品”的软件;
• 体系结构的目的是指导软件系统的开发,而框架的目的是设计复用。
• 体系结构的目的是指导软件系统的开发,而框架的目的是设计复用。
软件设计原则
设计原则是系统分解和模块设计的基本标准,
应用这些原则可以使代码 更加灵活、易于维护和扩展。
应用这些原则可以使代码 更加灵活、易于维护和扩展。
抽象
抽象是关注事物中与问题相关部分而忽略其他无关部分的一种思考方法。
封装
封装和信息隐藏是指每个软件单元对其他所有单元都隐藏自己的设计决策,
各个单元的特性通过其外部可见的接口来描述。
各个单元的特性通过其外部可见的接口来描述。
要求
应将单元接口设计得尽可能简单,并将单元对于环境的假设和要求降至最低。
模块化
模块化是在逻辑和物理上将整个系统分解成多个更小的部分,
其实质是 “分而治之”,即将一个复杂问题分解成若干个简单问题,然后逐个解决。
其实质是 “分而治之”,即将一个复杂问题分解成若干个简单问题,然后逐个解决。
系统分解的目标
高内聚、低耦合
层次化
复用
复用(Reuse)是利用某些已开发的、对建立新系统有用的软件元素
来生成新的软件系统,其好处在于提高生产效率,提高软件质量。
来生成新的软件系统,其好处在于提高生产效率,提高软件质量。
软件体系结构风格
软件体系结构风格(Architectural Styles)是描述特定系统组织方式的惯用范例,
强调了软件系统中通用的组织结构。
强调了软件系统中通用的组织结构。
常见的体系结构风格
调用/返回
层次结构
客户机/服务器结构
客户机/服务器体系结构(Client/Server)是一种分布式系统模型,
作为服务器的子系统为其他客户机的子系统提供服务,
作为客户机的子系统负责与用户的交互。
作为服务器的子系统为其他客户机的子系统提供服务,
作为客户机的子系统负责与用户的交互。
三层C/S结构
B/S结构
浏览器/服务器(Browser/Server)结构
事件风格
事件系统是将应用看成是一个构件集合,
每个构件直至发生对它有影响的事件时才有所动作。
每个构件直至发生对它有影响的事件时才有所动作。
实现策略
发布订阅
观察者
软件设计过程
Web 系统架构设计
软件系统测试
软件交付与维护
初识软件工程
软件的本质特性
软件的定义
软件 = 程序 + 数据 + 文档
本质特性
(软件开发困难的
根本原因)
(软件开发困难的
根本原因)
复杂性
一致性
- 软件不能独立存在,需要依附于一定的环境(如硬件、网络以及其他软件)
- 软件必须遵从人为的惯例并适应已有的技术和系统
- 软件需要随接口变化而变化
可变性
- 人们总是认为软件是容易修改的,但忽视了修改所带来的副作用
- 不断的修改可能导致软件的退化,从而结束其生命周期
不可见性
- 软件是一种“看不见、摸不着”的逻辑实体,不具有空间的形体特征
- 开发人员可以直接看到程序代码,但是源代码并不是软件本身
- 软件是以机器代码的形式运行,但是开发人员无法看到源代码是如何执行的
软件工程的产生和发展
软件开发面临的挑战
客户不满意
• 交付的许多功能不是客户需要的
• 交付的日期没有保障
• 客户使用时发现许多Bug
• 交付的日期没有保障
• 客户使用时发现许多Bug
项目过程失控
• 客户需求变化频繁,无力应对
• 无法预见软件的交付质量
• 对流程盲目遵从,忽视客户业务价值
• 无法预见软件的交付质量
• 对流程盲目遵从,忽视客户业务价值
风险与成本问题
• 开发团队专注技术,忽视风险(听意思是时间成本)
• 无能力预测成本,导致预算超支
• 无能力预测成本,导致预算超支
无力管理团队
• 无法评估开发人员能力及工作进度
• 困扰于如何提升团队的能力与效率
• 困扰于如何提升团队的能力与效率
探讨软件之道
软件工程一直致力于探索软件开发问题的解决之道
软件工程的历史
1956-1967
史前时代
• 软件开发没有方法可循
• 软件设计是在开发人员头脑中完成的隐藏过程
• 60世纪中期的软件危机
• 软件设计是在开发人员头脑中完成的隐藏过程
• 60世纪中期的软件危机
1968-1982
瀑布过程模型
• 1968年提出“软件工程”
• 结构化开发方法
• 瀑布式软件生命周期模型成为典型
• 结构化开发方法
• 瀑布式软件生命周期模型成为典型
1983-1995
质量标准体系
• 面向对象开发方法
• 软件过程改进运动
• CMM/ISO9000/SPICE等质量标准体系
• 软件过程改进运动
• CMM/ISO9000/SPICE等质量标准体系
20世纪90年代至今
• 敏捷开发方法流行
• 更紧密的团队协作
• 有效应对需求变化
• 快速交付高质量软件
• 迭代和增量开发过程
• 更紧密的团队协作
• 有效应对需求变化
• 快速交付高质量软件
• 迭代和增量开发过程
软件工程的基本概念
工程的含义
程是将理论和知识应用于实践的科学,
以便经济有效地解决问题。
以便经济有效地解决问题。
共同特征
• 大规模的设计与建造
• 复杂问题与目标分解
• 团队协作与过程控制
• 复杂问题与目标分解
• 团队协作与过程控制
软件工程的定义
1. 将系统性的、规范化的、可定量的方法应用于软件的开发、运行和维护, 即将工程化应用到软件上;
2. 对上面所述方法的研究
2. 对上面所述方法的研究
软件工程的目标
创造“ 足够好”的软件
注意,不是“完美”,而是“足够好”
注意,不是“完美”,而是“足够好”
• 较低的开发成本
• 按时完成开发任务并及时交付
• 实现客户要求的功能
• 具有良好性能、可靠性、可扩展性、可移植性等 • 软件维护费用低
• 按时完成开发任务并及时交付
• 实现客户要求的功能
• 具有良好性能、可靠性、可扩展性、可移植性等 • 软件维护费用低
软件工程的基本要素
过程
支持软件开发各个环节的控制和管理
流程
问题定义
• 构想文档
• 用户故事
• 用户故事
需求开发
• 分析模型
• 软件需求规格说明
• 软件需求规格说明
软件设计
• 设计模型
• 软件体系结构文档
• 软件详细设计文档
• 软件体系结构文档
• 软件详细设计文档
工具
• 软件建模工具
• 数据库设计工具
• 数据库设计工具
软件构造
• 源程序
• 目标代码
• 可执行构件
• 目标代码
• 可执行构件
工具
- 程序编辑器
- 程序编译器
- 程序解释器
- 程序调试器
- 集成开发环境
软件测试
• 测试规程
• 测试用例
• 测试报告
• 测试用例
• 测试报告
软件维护
软件开发管理
(贯穿整个流程)
(贯穿整个流程)
- 软件项目管理计划
- 软件配置管理计划
- 软件质量保证计划
- 评审记录......
方法
完成软件开发任务的技术手段
发展过程
面向过程
以算法作为基本构造单元,强调自顶向下的功能分解,将功能和数据进行一定程度的分离
面向对象
以类为基本程序单元,对象是类的实例化,对象之间以消息传递为基本手段
面向构件
寻求比类的粒度更大的且易于复用的构件, 期望实现软件的再工程
面向服务
在应用表现层次上将软件构件化,即应用业务过程由服务组成,而服务由构件组装而成
工具
为软件开发方法提供自动的或半自动的软件支撑环境
按流程分
软件设计
• 软件建模工具
• 数据库设计工具
• 数据库设计工具
软件构造
- 程序编辑器
- 程序编译器
- 程序解释器
- 程序调试器
- 集成开发环境
软件测试
• 单元测试工具
• 静态分析工具
• 自动化测试工具 • 性能测试工具
• 缺陷跟踪工具
• 静态分析工具
• 自动化测试工具 • 性能测试工具
• 缺陷跟踪工具
软件维护
• 代码重构工具
• 逆向工程工具
• 逆向工程工具
软件开发管理
• 需求管理工具
• 项目管理工具
• 配置管理工具
• 测试管理工具
• 项目管理工具
• 配置管理工具
• 测试管理工具
软件开发的基本策略
软件复用
• 构造一个新的系统不必从零做起,直接复用已有的构件进行组装
• 构件是经过反复使用验证的,由其组成的新系统具有较高的质量
• 构件是经过反复使用验证的,由其组成的新系统具有较高的质量
软件复用是利用将已有的软件制品,直接组装或者合理修改形成新的软件系统,
从而提高开发效率和产品质量,降低维护成本。
从而提高开发效率和产品质量,降低维护成本。
软件复用不仅仅是代码复用
• 库函数、类库
• 模板(文档、网页等) • 设计模式
• 组件
• 框架
• 模板(文档、网页等) • 设计模式
• 组件
• 框架
分而治之
• 将一个复杂的问题分解成若干个简单的问题,然后逐个解决
• 来源于人们生活与工作的经验,完全适合于技术领域
• 来源于人们生活与工作的经验,完全适合于技术领域
软件工程是一项解决问题的工程活动,通过对问题进行研究分析,
将一个复杂问题 解成可以理解并能够处理的若干小问题,然后再逐个解决。
将一个复杂问题 解成可以理解并能够处理的若干小问题,然后再逐个解决。
逐步演进
• 软件开发是自底向上逐步有序的生长过程
• 小步快跑:每走完一步再调整并为下一步确定方向,直到终点
• 小步快跑:每走完一步再调整并为下一步确定方向,直到终点
软件更像一个活着的植物,其生长是一个逐步有序的过程。
软件开发应该遵循软件的客观规律,不断进行迭代式增量开发,最终交付符合客户价值的产品。
软件开发应该遵循软件的客观规律,不断进行迭代式增量开发,最终交付符合客户价值的产品。
优化折中
• 优化:优化软件的各个质量特性,如运行速度、资源利用、用户体验
• 折中:通过协调各个质量特性,实现整体质量的最优
• 折中:通过协调各个质量特性,实现整体质量的最优
软件工程师应当把优化当成一种责任,不断改进和提升软件质量;
但是优化是一个多目标的最优决策,在不可能使所有目标都得到优化时,需要进行折中实现整体最优。
但是优化是一个多目标的最优决策,在不可能使所有目标都得到优化时,需要进行折中实现整体最优。
软件工程学科发展
软件工程的
Wasserman规范
Wasserman规范
抽象
软件建模方法
用户界面原型化
软件体系结构
软件过程
软件复用
度量
工具与集成环境
软件质量实现
什么是好的软件
不同人的视角
用户
功能质量
- 软件符合指定需求
- 软件几乎没有缺陷
- 软件性能正常
- 软件容易上手,操作方便
开发人员
结构质量
- 代码可测试性
- 代码可维护性
- 代码可读性
- 代码效率:高效管理资源
- 代码安全:可预防常见威胁
投资者
过程质量
- 软件按时交付
- 软件满足预算
- 可复用的开发过程,确保交付质量
三方面
软件过程
过程质量
过程测量
软件产品
内部质量
内部测量
外部质量
外部测量
产品效用
使用质量
使用质量测量
正确的软件与软件运行正确
正确的软件
一个软件要能够满足用户的需求,为用户创造价值。
这里的价值可以体现在两个方面,即为用户创造利润和减少成本。
这里的价值可以体现在两个方面,即为用户创造利润和减少成本。
用户价值/商业价值,关系到软件的成败
软件运行正确
软件没有或者有很少缺陷,具有很强的扩展性、良好的性能以及较高的易用性等。
软件质量的好坏
高质量的软件产品
• 做了用户想要它做的事情
• 正确有效地使用计算机资源
• 易于用户学习和使用
• 设计良好、代码良好且易于测试和维护
• 正确有效地使用计算机资源
• 易于用户学习和使用
• 设计良好、代码良好且易于测试和维护
产品质量维度
David Garvin
质量维度
(通用产品评价体系,
不是专门为软件定制,
但同样适用于软件产品)
质量维度
(通用产品评价体系,
不是专门为软件定制,
但同样适用于软件产品)
性能
产品特色
可靠性
符合性
耐久性
可服务性
维修时间可接受
审美
感知
品牌会影响感知
ISO9126 质量模型
(评价软件质量的通用模型)
(评价软件质量的通用模型)
外部质量
功能性
• 适合性:当软件在指定条件下使用,其满足明确和隐含要求功能的能力。
• 准确性:软件提供给用户功能的精确度是否符合目标。
• 互操作性:软件与其它系统进行交互的能力。
• 安全性:软件保护信息和数据的安全能力。
• 准确性:软件提供给用户功能的精确度是否符合目标。
• 互操作性:软件与其它系统进行交互的能力。
• 安全性:软件保护信息和数据的安全能力。
可靠性
• 成熟性:软件产品避免因软件中错误发生而导致失效的能力。
• 容错性:软件防止外部接口错误扩散而导致系统失效的能力。
• 可恢复性:系统失效后,重新恢复原有的功能和性能的能力。
• 容错性:软件防止外部接口错误扩散而导致系统失效的能力。
• 可恢复性:系统失效后,重新恢复原有的功能和性能的能力。
易用性
• 易理解性:软件显示的信息要清晰、准确且易懂,使用户能够快速理解软件。
• 易学习性:软件使用户能学习其应用的能力。
• 易操作性:软件产品使用户能易于操作和控制它的能力。
• 吸引性:软件具有的某些独特的、能让用户眼前一亮的属性。
• 易学习性:软件使用户能学习其应用的能力。
• 易操作性:软件产品使用户能易于操作和控制它的能力。
• 吸引性:软件具有的某些独特的、能让用户眼前一亮的属性。
效率/性能
• 时间特性:在规定的条件下,软件产品执行其功能时能够提供适当的响应时间和处理时间以及吞吐率的能力。
• 资源利用:软件系统在完成用户指定的业务请求所消耗的系统资源,诸如CPU占有率、内存占有率、网络带宽占有率等。
• 资源利用:软件系统在完成用户指定的业务请求所消耗的系统资源,诸如CPU占有率、内存占有率、网络带宽占有率等。
内部质量
可维护性
• 易分析性:软件提供辅助手段帮助开发人员定位缺陷原因并判断出修改之处。
• 易改变性:软件产品使得指定的修改容易实现的能力。
• 稳定性:软件产品避免由于软件修改而造成意外结果的能力。
• 易测试性:软件提供辅助性手段帮助测试人员实现其测试意图。
• 易改变性:软件产品使得指定的修改容易实现的能力。
• 稳定性:软件产品避免由于软件修改而造成意外结果的能力。
• 易测试性:软件提供辅助性手段帮助测试人员实现其测试意图。
可移植性
• 适应性:软件产品无需做任何相应变动就能适应不同运行环境的能力。
• 易安装性:在平台变化后,成功安装软件的难易程度。
• 共存性:软件产品在公共环境与其共享资源的其他系统共存的能力。
• 替换性:软件系统的升级能力,包括在线升级、打补丁升级等
• 易安装性:在平台变化后,成功安装软件的难易程度。
• 共存性:软件产品在公共环境与其共享资源的其他系统共存的能力。
• 替换性:软件系统的升级能力,包括在线升级、打补丁升级等
实现软件质量
软件质量是如何实现的?
如何才能有效地提高软件质量?
如何才能有效地提高软件质量?
软件产品开发过程
客户需求
分析
设计
实现
测试
客户满意的产品
高质量软件开发之道
高质量的设计(设计阶段)
规范的编码(实现阶段)
有效的测试(测试阶段)
商业环境下的软件质量
商业目标决定质量目标
• 商业目标决定质量目标,不应该把质量目标凌驾于商业目标之上
• 质量是有成本的,不可能为了追求完美的质量而不惜一切代价
• 理想的质量目标不是“零缺陷”,而是恰好让广大用户满意
• 质量是有成本的,不可能为了追求完美的质量而不惜一切代价
• 理想的质量目标不是“零缺陷”,而是恰好让广大用户满意
编写高质量代码
编程过程与规范
编程是软件工程师的基本能力
编写优雅的代码是每一个程序员的不懈追求
编程是一门艺术,它能够展示结构之美、构造之美、表达之美
编写优雅的代码是每一个程序员的不懈追求
编程是一门艺术,它能够展示结构之美、构造之美、表达之美
软件编程工作
软件编程是一个复杂而迭代的过程,
它不仅仅是编写代码,还应该包括代码审查、单元测试、代码优化、集成调试等一系列工作
它不仅仅是编写代码,还应该包括代码审查、单元测试、代码优化、集成调试等一系列工作
系统模型
编写代码
单元测试
代码审查
集成调试
系统构建
源代码
代码优化
软件编程规范
软件编码规范是与特定语言相关的描写如何编写代码的规则集合。
现实
• 软件全生命周期的 70% 成本是维护
• 软件在其生命周期中很少由原编写人员进行维护
• 软件在其生命周期中很少由原编写人员进行维护
目的
• 提高编码质量,避免不必要的程序错误
• 增强程序代码的可读性、可重用性和可移植性
• 增强程序代码的可读性、可重用性和可移植性
Google 编程规范
https://github.com/google/styleguide
编程规范
注释
只编写够用的注释,过犹不及,重视质量而不是数量
• 好的注释解释为什么,而不是怎么样
• 当修改代码时,维护代码周围的所有注释
• 当修改代码时,维护代码周围的所有注释
命名
编写自文档化的代码
• 唯一能完整并正确地描述代码的文档是代码本身
• 编写可以阅读的代码,其本身简单易懂
• 编写可以阅读的代码,其本身简单易懂
良好的编程实践
• 看:阅读优秀的代码,学习别人的代码
• 问:https://github.com/seajs/seajs/issues/545
• 练:亲自动手编写代码,实践、实践、再实践
• 问:https://github.com/seajs/seajs/issues/545
• 练:亲自动手编写代码,实践、实践、再实践
软件开发的工程思维
高质量的设计
模块化设计
模块化程序设计
基本思想
将一个大的程序按功能分拆成一系列小模块
优点
• 降低程序设计的复杂性
• 提高模块的可靠性和复用性
• 缩短产品的开发周期
• 易于维护和功能扩展
• 提高模块的可靠性和复用性
• 缩短产品的开发周期
• 易于维护和功能扩展
WEB应用系统模块化
基于水平划分
将一个系统按照业务类型进行水平划分成多个功能模块
用户管理
权限管理
...
业务功能
基于垂直划分
将一个系统按照系统层次进行垂直划分成多个模块
应用服务层
业务逻辑层
数据存储层
基于易变与稳定
认识和识别变与不变的部分,并将之科学地分离开
易变
业务逻辑1...业务逻辑M
稳定
通用功能1...通用功能N
基于单一职责
类或者函数应该只做一件事,并且做好这件事
• 单一职责 ≠ 单一功能
• 单一职责:只有一个引起变化的原因
• 单一职责:只有一个引起变化的原因
面向抽象编程
在模块化设计的基础上,我们可以先设计出各个模块的“骨架”,或者说对各个模块进行“抽象”,定义它们之间的“接口”。
定义各个模块互相关联的部分,这些部分在未来开发中不应该发生改变。
定义各个模块互相关联的部分,这些部分在未来开发中不应该发生改变。
错误与异常处理
错误
是导致程序崩溃的问题,
例如Python程序的语法错误 (解析错误)或者未捕获的异常(运行错误)等。??意思是包括了异常?
例如Python程序的语法错误 (解析错误)或者未捕获的异常(运行错误)等。??意思是包括了异常?
异常
运行时期检测到的错误,即使一条语句或者表达式 在语法上是正确的,当试图执行它时也可能会引发错误(比如栈溢出)
异常处理
用于管理程序运行期间错误的一种方法,
这种机制将程序中的正常处理代码和异常处理代码显式地区别开来,
提高了程序的可读性
这种机制将程序中的正常处理代码和异常处理代码显式地区别开来,
提高了程序的可读性
• 细化具体的异常类型,更有针对地处理
• 减少 try/catch 块中的代码量(代码块越大,越容易出现预期之外的异常)
• 在关键部分(前端主要是检查函数参数)应该检查变量合法性,包括类型和取值范围等,避免“雪球效应” (错误的数据在未来积累成了更大的错误)
• 减少 try/catch 块中的代码量(代码块越大,越容易出现预期之外的异常)
• 在关键部分(前端主要是检查函数参数)应该检查变量合法性,包括类型和取值范围等,避免“雪球效应” (错误的数据在未来积累成了更大的错误)
代码静态检查
代码审查
(Code Review)
(Code Review)
是一种用来确认方案设计和代码实现的质量保证机制,它通过阅读代码来检查源代码与编码规范的符合性以及代码的质量。
作用
• 检查设计的合理性
• 互为 Backup(后备人员)
• 分享知识、设计、技术
• 增加代码可读性
• 处理代码中的“地雷区
• 互为 Backup(后备人员)
• 分享知识、设计、技术
• 增加代码可读性
• 处理代码中的“地雷区
缺陷检查表
编程规范
按照具体编程语言的编码规范进行检查,包括命名规则、程序注释、缩进排版、声明与初始化、语句格式等
面向对象设计
• 类的设计和抽象是否合适
• 是否符合面向接口编程的思想
• 是否使用合适的设计模式
• 是否符合面向接口编程的思想
• 是否使用合适的设计模式
性能
• 在出现海量数据时,队列、表、文件在传输、上载等方面是否会出 现问题,是否控制如分配的内存块大小、队列长度等
• 对 Hashtable、Vector 等集合类数据结构的选择和设置是否合适
• 有无滥用 String 对象的现象
• 是否采用通用的线程池、对象池等高速缓存技术以提高性能
• 类的接口是否定义良好,如参数类型等应避免内部转换
• 是否采用内存或硬盘缓冲机制以提高效率?
• 并发访问时的应对策略
• I/O 方面是否使用了合适的类或采用良好的方法以提高性能(如减少序列化、使用 buffer 类封装流等)
• 同步方法的使用是否得当,是否过度使用?
• 递归方法中的迭代次数是否合适(应保证在合理的栈空间范围内)
• 如果调用了阻塞方法,是否考虑了保证性能的措施
• 避免过度优化,对性能要求高的代码是否使用profile工具
• 对 Hashtable、Vector 等集合类数据结构的选择和设置是否合适
• 有无滥用 String 对象的现象
• 是否采用通用的线程池、对象池等高速缓存技术以提高性能
• 类的接口是否定义良好,如参数类型等应避免内部转换
• 是否采用内存或硬盘缓冲机制以提高效率?
• 并发访问时的应对策略
• I/O 方面是否使用了合适的类或采用良好的方法以提高性能(如减少序列化、使用 buffer 类封装流等)
• 同步方法的使用是否得当,是否过度使用?
• 递归方法中的迭代次数是否合适(应保证在合理的栈空间范围内)
• 如果调用了阻塞方法,是否考虑了保证性能的措施
• 避免过度优化,对性能要求高的代码是否使用profile工具
资源释放处理
• 分配的内存是否释放,尤其在错误处理路径上(如 C/C++)
• 错误发生时是否所有对象被释放,如数据库连接、Socket、文件等
• 是否同一个对象被释放多次(如 C/C++)
• 代码是否保存准确的对象引用计数
• 错误发生时是否所有对象被释放,如数据库连接、Socket、文件等
• 是否同一个对象被释放多次(如 C/C++)
• 代码是否保存准确的对象引用计数
程序流程
• 循环结束条件是否准确
• 是否避免了死循环的产生
• 对循环的处理是否合适,应考虑到性能方面的影响
• 是否避免了死循环的产生
• 对循环的处理是否合适,应考虑到性能方面的影响
线程安全
• 代码中所有的全局变量是否是线程安全的
• 需要被多个线程访问的对象是否线程安全,检查有无通过同步方
法保护
• 同步对象上的锁是否按相同的顺序获得和释放以避免死锁,注意
错误处理代码
• 是否存在可能的死锁或是竞争,当用到多个锁时,避免出现类似
情况:线程A获得锁1,然后锁2,线程B获得锁2,然后锁1
• 在保证线程安全的同时,注意避免过度使用同步,导致性能降低
• 需要被多个线程访问的对象是否线程安全,检查有无通过同步方
法保护
• 同步对象上的锁是否按相同的顺序获得和释放以避免死锁,注意
错误处理代码
• 是否存在可能的死锁或是竞争,当用到多个锁时,避免出现类似
情况:线程A获得锁1,然后锁2,线程B获得锁2,然后锁1
• 在保证线程安全的同时,注意避免过度使用同步,导致性能降低
数据库处理
• 数据库设计或SQL语句是否便于移植(注意与性能会存在冲突)
• 数据库资源是否正常关闭和释放
• 数据库访问模块是否正确封装,便于管理和提高性能 • 是否采用合适的事务隔离级别
• 是否采用存储过程以提高性能
• 是否采用 PreparedStatement 以提高性能
• 数据库资源是否正常关闭和释放
• 数据库访问模块是否正确封装,便于管理和提高性能 • 是否采用合适的事务隔离级别
• 是否采用存储过程以提高性能
• 是否采用 PreparedStatement 以提高性能
通讯方面
• Socket 通讯是否存在长期阻塞问题
• 发送接收的数据流是否采用缓冲机制
• Socket 超时处理和异常处理
• 数据传输的流量控制问题
• 发送接收的数据流是否采用缓冲机制
• Socket 超时处理和异常处理
• 数据传输的流量控制问题
异常处理
• 每次当方法返回时是否正确处理了异常,如最简单的处理是记录日 志到日志文件中
• 是否对数据的值和范围是否合法进行校验,包括使用断言
• 在出错路径上是否所有的资源和内存都已经释放
• 所有抛出的异常是否都得到正确的处理,特别是对子方法抛出的异
常,在整个调用栈中必须能够被捕捉并处理
• 当调用导致错误发生时,方法的调用者应该得到一个通知
• 不要忘了对错误处理部分的代码进行测试,很多代码在正常情况下
执行良好,而一旦出错整个系统就崩溃了?
• 是否对数据的值和范围是否合法进行校验,包括使用断言
• 在出错路径上是否所有的资源和内存都已经释放
• 所有抛出的异常是否都得到正确的处理,特别是对子方法抛出的异
常,在整个调用栈中必须能够被捕捉并处理
• 当调用导致错误发生时,方法的调用者应该得到一个通知
• 不要忘了对错误处理部分的代码进行测试,很多代码在正常情况下
执行良好,而一旦出错整个系统就崩溃了?
方法(函数)
• 方法的参数是否都做了校验
• 数组类结构是否做了边界校验
• 变量在使用前是否做了初始化
• 返回堆对象的引用,不要返回栈对象的引用
• 方法的 API 是否被良好定义,即是否尽量面向接口编程,以便于
维护和重构
• 数组类结构是否做了边界校验
• 变量在使用前是否做了初始化
• 返回堆对象的引用,不要返回栈对象的引用
• 方法的 API 是否被良好定义,即是否尽量面向接口编程,以便于
维护和重构
安全
• 对命令行执行的代码,需要详细检查命令行参数
• WEB 类程序检查是否对访问参数进行合法性验证
• 重要信息的保存是否选用合适的加密算法
• 通讯时考虑是否选用安全的通讯方式
• WEB 类程序检查是否对访问参数进行合法性验证
• 重要信息的保存是否选用合适的加密算法
• 通讯时考虑是否选用安全的通讯方式
其他
• 日志是否正常输出和控制
• 配置信息如何获得,是否有硬编码
• 配置信息如何获得,是否有硬编码
代码分析工具
Web 开发中用的多的是 ESLint
代码性能分析
代码性能优化
优化是对代码进行等价变换,使得变换后的代码运行结果与变换前的代码运行结果相同,
但执行速度加快或存储开销减少。
但执行速度加快或存储开销减少。
• 代码性能优化是一门复杂的学问
• 根据 80/20 原则,实现程序的重构、优化、 扩展以及文档相关的事情通常需要消耗 80% 的工作量
• 根据 80/20 原则,实现程序的重构、优化、 扩展以及文档相关的事情通常需要消耗 80% 的工作量
• 在满足正确性、可靠性、健壮性、可读性等质量因素的前提下,设法提高程序的效率
• 以提高程序的全局效率为主,提高局部效率为辅
• 在优化程序效率时,应先找出限制效率的“瓶颈”
• 先优化数据结构和算法,再优化执行代码
• 时间效率和空间效率可能是对立的,应当分析哪一个因素更重要,再做出适当的折衷
• 以提高程序的全局效率为主,提高局部效率为辅
• 在优化程序效率时,应先找出限制效率的“瓶颈”
• 先优化数据结构和算法,再优化执行代码
• 时间效率和空间效率可能是对立的,应当分析哪一个因素更重要,再做出适当的折衷
• 从一开始就要考虑程序性能,不要期待在开发结束后再做一些快速调整
• 正确的代码要比速度快的代码重要,任何优化都不能破坏代码的正确性
• 正确的代码要比速度快的代码重要,任何优化都不能破坏代码的正确性
性能优化流程
证明需要
进行优化
进行优化
找出优化
关键部分
关键部分
测试代码
优化代码
评测优化
后的代码
后的代码
• 认真选择测试数据,使其能够代表实际的使用状况
• 永远不要在没有执行前后性能评估的情况下尝试对代码进行优化
• 永远不要在没有执行前后性能评估的情况下尝试对代码进行优化
实践
借助性能测试工具
找到性能瓶颈
找到性能瓶颈
性能分析工具能够有效地描述程序运行的性能状况,
提供各种统计数据帮助程序员找出程序中的性能瓶颈
提供各种统计数据帮助程序员找出程序中的性能瓶颈
实践时要在 改进得到的效果 和 改进要投入的精力 之间进行平衡,
优先完成有能力做,而且效果比较明显的性能优化
优先完成有能力做,而且效果比较明显的性能优化
• 性能优化的关键是如何发现问题,寻找解决问题的方法
• 有效的测试是不可缺少的,通过测试找出真正的瓶颈,并分析优化结果
• 要避免不必要的优化,避免不成熟的优化,不成熟的优化是错误的来源
• 有效的测试是不可缺少的,通过测试找出真正的瓶颈,并分析优化结果
• 要避免不必要的优化,避免不成熟的优化,不成熟的优化是错误的来源
结对编程实践
结对编程
两个人轮流驾驶,角色互换
结对编程是由两名程序员在同一台电脑上结对编写解决同一问题的代码。
把编程和复审有机结合。
把编程和复审有机结合。
- 驾驶员:负责用键盘编写程序
- 领航员:起到领航、提醒的作用
两个人轮流驾驶,角色互换
• 结对编程不仅意味着编程活动,也包括分析、设计、测试、优化、写文档等全程活动
• 结对编程有助于按时完成项目,并且保证高质量的代码
• 结对编程有助于按时完成项目,并且保证高质量的代码
• 结对编程的基础是会话和讨论, 不是一个人自得其乐
• 口渴指数是核实伙伴交流程度的一个考核标准
• 口渴指数是核实伙伴交流程度的一个考核标准
• 驾驶员不要连续工作超过1个小时,每1个小时休息10分钟
• 给驾驶员一点时间去发现和找到错误,别让伙伴觉得你很烦
• 常用的资料和规范(可以打印出来)以及书籍等应该放在手边
• 结对开始之前要协调沟通,彼此互相通告希望对方关注些什么,自己喜欢做什么
• 主动参与,任何一个任务都是共同的责任,只有“我们的代码
• 坚持代码标准和流程规范
• 注意听取伙伴的意见
• 给驾驶员一点时间去发现和找到错误,别让伙伴觉得你很烦
• 常用的资料和规范(可以打印出来)以及书籍等应该放在手边
• 结对开始之前要协调沟通,彼此互相通告希望对方关注些什么,自己喜欢做什么
• 主动参与,任何一个任务都是共同的责任,只有“我们的代码
• 坚持代码标准和流程规范
• 注意听取伙伴的意见
下面的一些情况不适合结对编程:
• 处于探索阶段的项目(一个人单独钻研更有效)
• 后期维护的技术含量不高
• 验证测试需要运行很长时间(两个人一起等待会浪费时间)
• 团队的人员要在多个项目中工作
• 领航的用处不大
另外,也不是所有的人都适合结对编程(不合格、不合群、抵触结对编程的程序员)
• 处于探索阶段的项目(一个人单独钻研更有效)
• 后期维护的技术含量不高
• 验证测试需要运行很长时间(两个人一起等待会浪费时间)
• 团队的人员要在多个项目中工作
• 领航的用处不大
另外,也不是所有的人都适合结对编程(不合格、不合群、抵触结对编程的程序员)
我的理解:结对编程的必要条件:
- 两人水平相当
- 代码的设计、编写、测试对于两人而言有一定挑战
单元测试
单元测试概述
现实的开发问题
经常把单元测试任务堆积到系统测试阶段
- 大量故障堆积在项目 中后期,项目后10% 的工作占用了项目 90%的时间
- 故障难以定位,而且 飘忽不定,开发和测 试人员疲于奔命
单元测试
单元是构造软件系统的基础,只有使每个单元得到足够的测试,
系统的质量才能有可靠的保证,即单元测试是构筑产品质量的基石
系统的质量才能有可靠的保证,即单元测试是构筑产品质量的基石
单元测试(Unit Testing)是对软件中的最小可测试单元进行检查和验证
优点
验证代码
设计更好
文档化行为
具有回归性
程序员必须对自己的代码质量负责,单元测试是对自己代码质量的基本承诺
程序=UT+CODE
测试人员有权利对没有做过UT的代码说No
测试人员有权利对没有做过UT的代码说No
在现实中,代码质量最好、开发速度最快的程序员是单元测试做得最好的
(我的理解:单元测试的思维会促使程序员写出质量更好的代码)
(我的理解:单元测试的思维会促使程序员写出质量更好的代码)
单元测试内容
模块接口
对通过所有被测模块的
数据流进行测试
数据流进行测试
局部数据结构
检查模块中的数据结构
是否正确的定义和使用
是否正确的定义和使用
边界条件
检查数据流或控制流中条件或
数据处于边界时的出错可能性
数据处于边界时的出错可能性
独立路径
检查由于计算错误、判定错误、
控制流错误导致的程序错误
控制流错误导致的程序错误
出错处理
检查可能引发错误处理的路径
以及进行错误处理的路径
以及进行错误处理的路径
单元测试原则
快速
单元测试应能快速运行,如果运行缓慢,就不会愿意频繁运行它
独立
单元测试应相互独立,某个测试不应为下一个测试设定条件。
当测试相互依赖时,一个没通过就会导致一连串的失败,难以定位问题。
当测试相互依赖时,一个没通过就会导致一连串的失败,难以定位问题。
可重复
单元测试应该是可以重复执行的,并且结果是可以重现的
自我验证
单元测试应该有布尔输出,无论是通过或失败,
不应该查看日志文件或手工对比不同的文本文件来确认测试是否通过
不应该查看日志文件或手工对比不同的文本文件来确认测试是否通过
及时
及时编写单元测试代码,应恰好在开发实际的单元代码之前
单元测试过程
找出潜在的最大问题区
确定要做的单元测试
编写或修改单元测试用例
执行单元测试
满足测试质量
单元测试质量
测试通过率
测试通过率是指在测试过程中执行通过的测试用例所占比例,
单元测试通常要求测试用例通过率达到100%
单元测试通常要求测试用例通过率达到100%
测试覆盖率
测试覆盖率是用来度量测试完整性的一个手段,通过覆盖率数据,可以了解测试是否充分以及弱点在哪里。
代码覆盖率是单元测试的一个衡量标准,但也不能一味地去追求覆盖率。
代码覆盖率是单元测试的一个衡量标准,但也不能一味地去追求覆盖率。
代码覆盖率
(达到70%-80%就可以了,
过高覆盖率可能会造成测试
成本大大增加)
(达到70%-80%就可以了,
过高覆盖率可能会造成测试
成本大大增加)
语句覆盖
判定覆盖??
条件覆盖??
判定条件覆盖??
条件组合覆盖
路径覆盖??
见白盒测试方法
单元测试方法
静态测试
通过人工分析或程序正确性证明??的方式来确认程序正确性
动态测试
通过动态分析??和程序测试等方法来检查和确认程序是否有问题
黑盒测试(Black Box Testing)
/功能测试
/功能测试
将测试对象看做一个黑盒子, 完全不考虑程序内部的逻辑结构和内部特性,
只依据程序的需求规格说明书,检查程序的功能是否符合它的功能说明
只依据程序的需求规格说明书,检查程序的功能是否符合它的功能说明
白盒测试(White Box Testing)/
结构测试
结构测试
把测试对象看做一个透明的盒子,允许测试人员利用程序内部的逻辑结构及有关信息,
设计或选择测试用例, 对程序所有逻辑路径进行测试
设计或选择测试用例, 对程序所有逻辑路径进行测试
测试系统中的模块
上层模块
驱动模块
测试结果
被测模块
测试用例
下层模块1
下层模块2
...
下层模块N
下层模块2
...
下层模块N
桩模块1
桩模块2
...
桩模块N
桩模块2
...
桩模块N
测试工具
xUnit
适用于以下场景的测试
• 单个函数、一个类或者几个功能相关类的测试
• 尤其适用于纯函数测试或者接口级别的测试
• 尤其适用于纯函数测试或者接口级别的测试
无法适用于复杂场景的测试,
但这些场景才是重点和难点
但这些场景才是重点和难点
• 被测对象依赖关系复杂,甚至无法简单创建出这个对象
• 对于一些失败场景的测试
• 被测对象中涉及多线程合作
• 被测对象通过消息与外界交互的场景
• 对于一些失败场景的测试
• 被测对象中涉及多线程合作
• 被测对象通过消息与外界交互的场景
Mock
在测试过程中对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象(即Mock对象)来创建以便测试的方法
关键
需要应用针对接口的编程技术,即被测试的代码通过接口来引用对象,
再使用Mock对象模拟所引用的对象及其行为,因此被测试模块并不知道它所引用的究竟是真实对象还是Mock对象
再使用Mock对象模拟所引用的对象及其行为,因此被测试模块并不知道它所引用的究竟是真实对象还是Mock对象
黑盒测试方法
测试用例的重要性
设计良好的测试用例是关键
- 降低软件测试成本
- 保证测试工作质量
- 评估和检验测试效果
测试用例的设计要求
- 具有代表性和典型性
- 寻求系统设计和功能设计的弱点
- 既有正确输入也有错误或异常输入
- 考虑用户实际的诸多使用场景
黑盒测试技术
黑盒测试是将测试对象看做一个黑盒子,完全不考虑程序内部的逻辑结构和内部特性,
只依据程序的需求规格说明书,检查程序的功能是否符合它的功能说明
只依据程序的需求规格说明书,检查程序的功能是否符合它的功能说明
等价类划分
等价类划分是将输入域划分成尽可能少的若干子域,
在划分中要求每个子域两两互不相交,每个子域称为一个等价类
在划分中要求每个子域两两互不相交,每个子域称为一个等价类
有效等价类
是对规格说明有意义、合理的输入数据构成的集合,
能够检验程序是否实现了规格说明中预先规定的功能和性能
能够检验程序是否实现了规格说明中预先规定的功能和性能
无效等价类
是对规格说明无意义、不合理的输入数据构成的集合,
以检查程序是否具有一定的容错性
以检查程序是否具有一定的容错性
边界值分析
(有效等价类的边界)
(有效等价类的边界)
边界值分析是对输入或输出的边界值进行测试的一种方法,
它通常作为等价类划分法的补充,这种情况下的测试用例来自等价类的边界
它通常作为等价类划分法的补充,这种情况下的测试用例来自等价类的边界
• 先确定边界: 通常输入或输出等价类的边界就是应该着重测试的边界情况
• 选取正好等于、刚刚大于或刚刚小于边界的值作为测试数据,而不是选取等价类中的典型值或任意值
• 选取正好等于、刚刚大于或刚刚小于边界的值作为测试数据,而不是选取等价类中的典型值或任意值
实践表明: 大多数故障往往发生在输入定义域或输出值域的边界上,而不是内部。
因此,针对各种边界情况设计测试用例,通常会取得很好的测试效果。
因此,针对各种边界情况设计测试用例,通常会取得很好的测试效果。
5个边界值
min、min+、nom??、max- 和 max
健壮性测试
健壮性测试是作为边界值分析的一个简单的扩充,
它除了对变量的5个边界值分析取值外,还要增加一个略大于最大值(max+)以及略小于最小值(min-)的取值,
检查超过极限值时系统的情况
它除了对变量的5个边界值分析取值外,还要增加一个略大于最大值(max+)以及略小于最小值(min-)的取值,
检查超过极限值时系统的情况
错误推测法
错误推测法是人们根据经验或直觉推测程序中可能存在的各种错误,
从而有针对性地编写检查这些错误的测试用例的方法
从而有针对性地编写检查这些错误的测试用例的方法
• 软件缺陷具有空间聚集性,80%的缺陷常常存在于 20% 的代码中。
因此,应当记住常常光临代码的高危多发“地段”,这样发现缺陷的可能性会大得多
• 列举程序中所有可能的错误和容易发生错误的特殊情况,根据可能出现的错误情况选择测试用例
因此,应当记住常常光临代码的高危多发“地段”,这样发现缺陷的可能性会大得多
• 列举程序中所有可能的错误和容易发生错误的特殊情况,根据可能出现的错误情况选择测试用例
白盒测试方法
基本概念
白盒测试技术
白盒测试是将测试对象看做一个透明的盒子,
允许测试人员利用程序内部的逻辑结构及有关信息,设计或选择测试用例,
对程序所有逻辑路径进行测试
允许测试人员利用程序内部的逻辑结构及有关信息,设计或选择测试用例,
对程序所有逻辑路径进行测试
控制流图
控制流图(CFG, Control Flow Graph)是一个过程或程序的抽象表示。
控制流图的基本符号:
• 矩形代表了连续的顺序计算,也称基本块
• 节点是语句或语句的一部分,边表示语句的控制流
控制流图的基本符号:
• 矩形代表了连续的顺序计算,也称基本块
• 节点是语句或语句的一部分,边表示语句的控制流
代码覆盖标准
代码覆盖率描述的是代码被测试的比例和程度,
通过代码覆盖率可以得知哪些代码没有被覆盖,
从而进一步补足测试用例
通过代码覆盖率可以得知哪些代码没有被覆盖,
从而进一步补足测试用例
语句覆盖
最弱的逻辑覆盖准则
判定覆盖(分支覆盖)
具有比语句覆盖更强的测试能力,但仍是弱的逻辑覆盖
条件覆盖
条件覆盖只能保证每个条件至少有一次为真,而没有考虑整个判定结果
判定条件覆盖
没有考虑条件的各种组合情况
条件组合覆盖
覆盖了所有组合,但覆盖路径有限
路径覆盖
覆盖程序中的所有可能的执行路径
可能没有覆盖所有条件组合
如何看待测试覆盖率
• 覆盖率数据只能代表测试过哪些代码,不能代表是否测试好这些代码。
• 较低的测试覆盖率能说明所做的测试还不够,但反之不成立。
• 路径覆盖 > 判定覆盖 > 语句覆盖
• 测试人员不能盲目追求代码覆盖率,而应该想办法设计更好的测试用例。
• 测试覆盖率应达到多少需要考虑软件的整体要求以及测试成本。
• 较低的测试覆盖率能说明所做的测试还不够,但反之不成立。
• 路径覆盖 > 判定覆盖 > 语句覆盖
• 测试人员不能盲目追求代码覆盖率,而应该想办法设计更好的测试用例。
• 测试覆盖率应达到多少需要考虑软件的整体要求以及测试成本。
基本路径测试
基本路径测试是在程序控制流图基础上,通过分析控制构造的环路复杂性,
导出基本可执行路径集合,从而设计测试用例的方法
导出基本可执行路径集合,从而设计测试用例的方法
流程
绘制控制流图
计算环路复杂度
V(G) = 区域数目
V(G) = 边数-节点数+2
V(G) = 判断节点数+1(这个数起来最简单)
V(G) = 边数-节点数+2
V(G) = 判断节点数+1(这个数起来最简单)
确定基本路径
找出基本独立路径集
环路复杂度 == 程序的独立路径数
我的想法:这应该是先计算环路复杂度的意义,
可以用它来验证寻找出的独立路径数量对不对
可以用它来验证寻找出的独立路径数量对不对
去掉无法达到的路径
设计测试用例
循环测试
目的
检查循环结构的有效性
类型
简单循环(次数为 n)
• 完全跳过循环
• 只循环1次
• 只循环2次
• 循环m(m<n)次
• 分别循环 n-1, n, n+1 次
• 只循环1次
• 只循环2次
• 循环m(m<n)次
• 分别循环 n-1, n, n+1 次
嵌套循环
• 从最内层循环开始,所有外层循环次数设为最小值;
• 对最内层循环按照简单循环方法进行测试;
• 由内向外进行下一个循环的测试,本层循环的所有外层循环仍取最小值,而由本层循环嵌套的循环取某些 “典型”值;
• 重复上一步的过程,直到测试完所有循环。
• 对最内层循环按照简单循环方法进行测试;
• 由内向外进行下一个循环的测试,本层循环的所有外层循环仍取最小值,而由本层循环嵌套的循环取某些 “典型”值;
• 重复上一步的过程,直到测试完所有循环。
串接循环
• 独立循环:分别采用简单循环的测试方法;
• 依赖性循环:采用嵌套循环的测试方法。
• 依赖性循环:采用嵌套循环的测试方法。
单元测试工具
软件开发过程
软件过程
过程含义
过程是一组将输入转化为输出的相互关联或相互作用的活动
过程方法
过程方法是系统地识别和管理组织内所使用的过程,保证更有效地获得期望的结果
软件开发活动
问题定义
人们通过开展技术探索和市场调查等活动,
研究系统的可行性和可能的解决方案,
确定待开发系统的总体目标和范围
研究系统的可行性和可能的解决方案,
确定待开发系统的总体目标和范围
需求开发
在可行性研究之后,分析、整理和提炼所收集到的用户需求,
建立完整的需求分析模型,编写软件需求规格说明
建立完整的需求分析模型,编写软件需求规格说明
• 分析模型
• 软件需求规格说明
• 软件需求规格说明
软件设计
根据需求规格说明,确定软件体系结构,
进一步设计每个系统部件的实现算法、数据结构及其接口等
进一步设计每个系统部件的实现算法、数据结构及其接口等
• 设计模型
• 软件体系结构文档
• 软件详细设计文档
• 软件体系结构文档
• 软件详细设计文档
软件构造
概括地说是将软件设计转换成程序代码,这是一个复杂而迭代的过程,
要求根据设计模型进行程序设计以及正确而高效地编写和测试代码
要求根据设计模型进行程序设计以及正确而高效地编写和测试代码
• 源程序
• 目标代码
• 可执行构件
• 目标代码
• 可执行构件
软件测试
检查和验证所开发的系统是否符合客户期望,
主要包括单元测试、子系统测试、集成测试和验收测试等活动
主要包括单元测试、子系统测试、集成测试和验收测试等活动
• 测试规程
• 测试用例
• 测试报告
• 测试用例
• 测试报告
软件维护
系统投入使用后对其进行改进,以适应不断变化的需求。
完全从头开发的系统很少,将软件系统的开发和维护看成是一个连续过程更有意义。
完全从头开发的系统很少,将软件系统的开发和维护看成是一个连续过程更有意义。
软件开发管理与支持活动
软件项目管理
软件项目管理是为了使软件项目能够按照预定的成本、进度、质量顺利完成,
而对成本、人员、进度、质量和风险进行控制和管理的活动。
而对成本、人员、进度、质量和风险进行控制和管理的活动。
软件配置管理
软件配置管理是通过执行版本控制、变更控制的规程,并且使用合适的配置管理软件,
来保证所有产品配置项的完整性和可跟踪性。
来保证所有产品配置项的完整性和可跟踪性。
软件过程模型
软件过程模型是对软件过程的抽象描述
瀑布模型
将基本的开发活动看成是一系列界限分明的独立阶段,这是一种计划驱动的软件过程,有利于规范软件开发活动。
开发阶段严格按照线性方式进行,每一个阶段具有相关的里程碑和交付产品,且需要确认和验证。
• 以预测性为原则
• 以文档驱动开发过程
• 以过程控制为核心
• 以文档驱动开发过程
• 以过程控制为核心
原型化模型
原型是一个部分开发的产品,用于加强对系统的理解,有助于明确需求和选择可行的设计策略。
迭代式开发
将描述、开发和验证等不同活动交织在一起,在开发过程中建立一系列版本,将系统一部分一部分地逐步交付。
• 更快速地发布产品
• 追求产品创新
• 需求不确定性高
• 需要快速响应用户的变化
• 关注用户行为
• 追求产品创新
• 需求不确定性高
• 需要快速响应用户的变化
• 关注用户行为
- 增量模型:在每一个新的发布中逐步增加功能直到构造全部功能
- 迭代模型:一开始提交一个完整系统,在后续发布中补充完善各子系统功能
可转换模型
利用自动化的手段,通过一系列转换将需求规格说明转化为一个可交付使用的系统。
• 由于数学方法具有严密性和准确性,形式化方法所交付的系统具有较少的缺陷和较高的安全性
• 特别适合于那些对安全性、可靠性和保密性要求极高的软件系统,这些系统需要在投入运行前进行验证
• 特别适合于那些对安全性、可靠性和保密性要求极高的软件系统,这些系统需要在投入运行前进行验证
参考
https://next.xuetangx.com/learn/THU08091000367/THU08091000367/1516221/video/1385883
https://www.bilibili.com/video/BV1Q741157ve?p=2
0 条评论
下一页