软件工程是指导计算机软件开发和维护的一门工程学科。采用工程的概念、原理、技术和方法来开发和维护软件,把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来,以经济地开发出高质量的软件并有效地维护它,这就是软件工程
本质特性
关注于大型程序的构造
中心课题是控制复杂性
将复杂问题分解成可理解的问题,并使各部分之间保持简单的通信关系,此法不能降低复杂性但可使其变为可管理的
软件经常变化
需求一直在变化
开发软件的效率非常重要
和谐地合作是开发软件的关键
必须有效的支持它的用户
意味着需要仔细研究用户以确定适当的功能需求、可用性要求等,应写出用户手册和培训材料,注意建立使用新系统的环境(培训用户,使其习惯于新的工作流程)
在软件工程领域中通常由具有一种文化背景的人替具有另一种文化背景的人创造产品
软件开发人员通常并不是其他领域专家但需要为其他领域开发软件,就需要事先了解那些领域的流程
基本原理
用分阶段的生命周期计划严格管理
坚持进行阶段评审
不能等到编码阶段结束后在进行质量保证工作,理由如下:
- 错误出现往往是由于设计造成的(设计错误占比63%,编码错误占比37%)
- 错误发现与改正得越晚,改正所需付出的代价越高
实行严格的产品控制
基准配置管理(基线配置、变动控制):为保持软件各个配置成分的一致性而进行的管理
基线配置:他们是经过阶段评审后的软件配置成分(各个阶段产生的文档或程序代码)
变动控制:一切有关修改软件的建议,特别是涉及对基准配置的修改建议都必须经过审批才可修改
采用现代程序设计技术
结果应能清楚地审查
根据软件开发项目的总目标及完成期限,规定开发组织的责任和产品标准,从而使所得到的结果能够清楚地审查
开发小组的人员应该少而精
承认不断改进软件工程实践的必要性
软件工程方法学
软件工程包括技术和管理两方面的内容
管理:通过计划、组织和控制等一系列活动,合理地配置和使用各种资源,以达到既定目标的过程
三要素:工具、方法、过程
方法:完成软件开发的各项任务的技术方法,回答“怎样做”的问题
工具:运用方法而提供的自动的或半自动的软件工程支撑环境 过程:为了获得高质量软件所需要完成的一系列任务的框架,它规定了完成各项任务的工作步骤
传统方法学:也称为生命周期方法学或结构化范型
采用结构化技术(结构化分析、设计、实现)
面向对象方法学——强调主动地多次反复迭代
面向对象方法:把数据和行为看成同等重要,它是一种以数据为主线,把数据和对数据的操作紧密地结合起来的方法。
四个要点:对象、类、继承、消息
软件生命周期
三个时期(软件定义、软件开发、软件维护),八个阶段(问题研究、可行性研究、需求分析、概要设计、详细设计、编码和单元测试、综合测试、软件发布运行维护)
软件过程
软件过程是为了获得高质量软件所需要完成的一系列任务的框架,它规定了完成各项任务的工作步骤
需求分析与验证、规格说明与验证、设计与验证、编码与验证、综合测试、维护
瀑布模型
按软件开发流程进行顺序开发,适用于目的需求明确的情况
特点
阶段间具有顺序性和依赖性
- 必须等前一段工作完成后才能开始后一段工作
- 前一段的输出文档就是后一段的输入文档,所以需要保证每个阶段的文档正确才能输出最终正确的结果
推迟实现的观点
急于求成而忽略先前的分析会导致总工作量的增加
质量保证的观点
文档驱动型,文档作为每个阶段开始和验收的依据
优点
缺点
文档驱动型脱离实际,只是停留在纸面上,在实践中可能不能满足客户真实需要
快速原型模型
在用户不能给出完整、准确的需求说明或开发者不能确定算法的有效性、操作系统的适应性或人机交互的形式等多种情况下,可以根据用户的一组基本需求,快速建造一个模型,评估,然后再进一步精化,调整模型,适用于需求不明确的情况
1. 快速原型
1. 快速建立起来的可以在计算机上运行的程序,他所能完成的功能往往是最终产品能完成的功能的子集
2. 一般在得到需求分析之后就不再使用
2. 特点
1. 不带反馈环
增量模型
把软件产品作为一系列的增量构件来设计、编码、集成和测试,每个构件由多个相互作用的模块构成,并且能够完成特定的功能(类似于版本管理,一个个版本更新并最终得到版本)
1. 优点
1. 短期内可以提供可完成部分工作的初代产品给客户,客户可以早期熟悉软件而不必等到软件全部开发完
2. 用户有较为充裕的时间学习和适应新产品
3. 可以更早的知道客户需求的变化,对软件进行修改
4. 对软件公司来说可以降低开发成本
2. 风险大
1. 可能提高开发速度,但需要密切地监控整个开发过程,否则将冒构件无法集成到一起的风险,使整个工程毁于一旦
3. 注意事项
1. 增量构件规模适中
2. 系统设计需要构架为开放性的,防止后期增量无法集成
4. 适用对象
1. 经常改变的开发过程
2. 人手不足的软件公司
螺旋模型
使用原型及其他方法来尽量降低风险
1. 简化版本
1. 快速原型+瀑布模型+风险分析(在每个阶段前进行风险分析,每个阶段后进行评估)
2. 优点
1. 风险驱动
2. 减少测试过多或不足所带来的风险
3. 对可选方案和约束条件的强调用力与已有软件的重用
4. 维护知识模型的一个周期
3. 缺点
1. 需要具有相当丰富的风险评估经验的人员参与开发,增加成本
2. 过多的迭代次数会增加开发成本,延迟提交时间
4. 适用对象
1. 庞大复杂高风险系统
2. 大型软件开发
喷泉模型*
典型的面向对象生命周期模型
Rational统一过程*
RUP是一种迭代的,以架构为中心的,用例驱动的模型
最佳实践
迭代式开发
允许软件需求变化
管理需求
使用基于构件的体系结构
子系统模块
可视化建模
画UML图
验证软件质量
控制软件变更
敏捷过程与极限编程*
敏捷软件开发宣言(敏捷价值观)
弱化文档的重要性,强调客户需求的是一个可以使用的软件
极限编程(XP)
敏捷过程中最负盛名的一个
适用于需求模糊且经常改变的场合
适用于有限时间有限开发人员的环境
重构:不改变系统行为的前提下,重新调整和优化系统内部结构,以提高代码的可维护性,降低复杂性,消除冗余
集体所有:每个小组成员都拥有修改代码的权利,每个成员都对全部代码质量负责
持续集成:极限编程主张在一天内多次集成系统,随着需求的更变,应该不断的进行回归测试
现场客户:至少有一名客户代表在项目的整个周期中与开发人员一起紧密地配合工作
系统隐喻:整个系统联系在一起的全局视图,它描述系统如何运作,以及用何种方式把新功能加入到系统中
可持续的开发速度:每周工作不超过40h,连续加班不超过两周
微软过程
小结
从工程管理的角度,可以将软件设计分为概要设计阶段和详细设计阶段
把复杂问题分解成许多容易解决的小问题
现实世界中的一些事物、状态或过程之间总存在着某些相似的方面(共性(。把这些相似的方面集中和概括起来,暂时忽略它们之间的差异
一般抽象过程
软件工程抽象过程
逐步求精
为了能集中精力解决主要问题而尽量推迟对问题细节的考虑。逐步求精是人类解决复杂问题时采用的基本方法,也是许多软件工程技术的基础。
Miller法则:一个人在任何时候都只能把注意力集中在(7±2)个知识块上
信息隐藏和局部化
信息隐藏
一个模块内包含的信息(过程和数据)对于不需要这些信息的模块来说应是不能访问的
局部化
把一些关系关系密切的软件元素物理的放的彼此靠近,局部化有利于实现信息隐藏
作用
模块独立
模块独立的概念是模块化、抽象、信息隐藏和局部化概念的直接结果
每个模块完成一个相对独立的特定子功能,并且和其他模块的关系很简单
模块独立程度的两个定性标准度量
耦合
是对一个软件结构内不同模块之间连接程度的考量。耦合度强弱取决于模块间接口的复杂程度,进入或访问一个模块的点,以及通过接口的数据
非直接耦合/完全独立
数据耦合:两个模块彼此间通过参数交换信息,而且交换的信息仅仅是数据
控制耦合:两个模块彼此间传递的信息中有控制信息
特征耦合:把整个数据结构作为参数传递而被调用的模块只需要使用其中一部分数据元素时,就出现了特征耦合
公共环境耦合:两个或以上模块通过一个公共数据环境相互作用时,他们之间的耦合称为公共环境耦合
公共环境可以是全程变量、共享的通信区、内存的公共覆盖区、任何存储介质上的文件、物理设备等
公共环境耦合的复杂程度随耦合的模块个数而变化,当耦合的模块个数增加时复杂程度显著增加。如果只有两个模块有公共环境,那么这种耦合有下面两种可能:
一个模块往公共环境送数据,另一个模块从公共环境取数据。这是数据耦合的一种形式,是比较松散的耦合
两个模块都既往公共环境送数据又从里面取数据,这种耦合比较紧密,介于数据耦合和控制耦合之间
如果两个模块共享的数据很多,都通过参数传递可能很不方便,这时可以利用公共环境耦合
内容耦合:最高程度的耦合
- 一个模块访问另一个模块的内部数据
- 一个模块不通过正常入口转到另一个模块的内部
- 两个模块有一部分程序代码重叠
- 一个模块有多个出入口
以上情况出现之一,这两个模块就发生了内容耦合
应该坚决避免使用内容耦合
内聚(重要性高于耦合)
标志着一个模块内各个元素彼此结合的紧密程度,他是信息隐藏和布局化概念的自然扩展,理想的内聚模块只做一件事情
启发规则
小结
详细设计阶段的根本目标:确定应该怎样具体的实现所要求的系统。经过这个阶段的设计工作,应该得出对目标系统的精准描述,从而在编码阶段可以把这个描述直接翻译成用某种程序设计语言的代码
设计问题
系统响应时间
用户帮助设施
大多数现代软件都提供练级帮助设施,这使得用户无需离开用户界面就能解决自己的问题
集成的帮助设施
设计在软件里面,对用户操作敏感,用户可从与刚刚完成的操作有关的主体中选择一个请求帮助
附加的帮助设施
在系统建成后再添加到软件中的,它实际上是一种查询能力有限的联机用户手册
集成的优于附加的帮助设施
出错信息处理
命令交互
设计过程
是一个迭代的过程,通常先创建设计模型,再用原型实现这个设计模型,并由用户试用和评估,根据用户意见进行修改
人机界面设计指南
界面分类
一般交互界面
一般交互指南涉及信息显示,数据输入和系统整体控制
保持一致性。
使用一致的格式提供有意义的反馈(视觉、听觉)
在执行有较大破坏性的动作之前要求用户确认。
允许取消绝大多数操作减少两次操作之间必须记忆的信息量
提高对话、移动和思考的效率
允许犯错误
按功能对动作分类,并据此设计屏幕布局提供对用户工作敏感的帮助设施
用简单动词或动词短语作为命令名
信息显示界面
数据输入界面
又称为程序框图,历史悠久使用最为广泛的描述过程设计的方法,但也是最混乱的一种方法
一种不允许违背结构程序设计精神的图形工具
包含多重嵌套的条件选择,用判定表能够清晰地表示复杂的条件组合与应做的动作之间的相应关系
判定表需要考虑到所有可能出现的组合,不然就容易隐藏bug
也称伪码
优点:
1. 可以作为注释直接插在源程序中间,保持文档和程序的一致性,提高文档质量
缺点:
不如图形工具形象直观,描述复杂的条件组合与动作间的对应关系时不如判定表清晰简单
顺序结构
选择结构
在顺序结构的标记右上角有一个°标记
重复结构
在顺序结构的标记右上角有一个*标记
Jackson图的缺点是,这种工具表示选择或重复结构时,选择条件或者循环结束条件不能直接在图上表示出来,影响了图的表达能力,并且连线为斜线,在行式打印机上不易输出
五个步骤:
分析并确定输入数据和输出数据的逻辑结构,并用Jackson图庙会这些数据结构
找出输入数据结构和输出数据结构中有对应关系的数据单元
对应关系:指有直接的因果关系,在程序中可以同时处理的数据单元。但对于重复出现的数据单元,重复的次序和次数必须都相同才有可能有对应关系
导出描绘程序结构的Jackson图,此图应该综合输入数据结构和输出数据结构的层次关系而导出来
列出所有操作和条件(包括分支条件和循环结束条件),并且把它们分配到程序结构图的适当位置
用伪代码表示程序
根据程序中运算符和操作数的总数来度量程序的复杂程度
N=N1+N2,N1为程序中运算符出现的总次数,N2为操作数出现的总次数,N为程序长度
预测程序长度的公式
H = n 1 l o g 2 n 1 + n 2 l o g 2 n 2 H=n_1log_2n_1+n_2log_2n_2 H=n1log2n1+n2log2n2
预测程序中包含错误的个数的公式
E = N l o g 2 ( n 1 + n 2 ) / 3000 E=Nlog_2(n_1+n_2)/3000 E=Nlog2(n1+n2)/3000
划代 | 语言 | 特点 | 级别 |
---|---|---|---|
1GL | 机器语言 | 程序不直观,编程出错率高 运行效率高,使用CPU硬件 | 低级 |
2GL | 汇编语言 | 比机器语言直观,减少了出错率 与机器码一样长 使用于嵌入式开发,驱动程序 | |
3GL | 面向过程语言, C | 语法简洁,速度快,使用嵌入式,驱动程序,系统软件,服务器程序 | 高级 |
4GL | 面向对象语言, JAVA**,**C#, C++ | Java:虚拟机,跨平台,垃圾回收机制,使用Web****应用程序,信息管理系统 **C#:微软平台,**垃圾回收机制 **C++:**自己管理内存,速度快,C/S结构程序,服务器程序 |
注释分为序言性注释和功能性注释
注释基本原则
(1) 注释应该增加代码的清晰度。代码注释的目的是要使代码更易于被其他开发人员等理解。
(2) 避免使用装饰性内容。
(3) 保持注释的简洁。
(4) 注释信息不仅要包括代码的功能,还应给出原因。
(5) 不要为注释而注释。
(6) 除变量定义等较短语句的注释可用行尾注释外,其他注释当避免使用行尾注释。
文件注释
类、接口注释
方法注释
其他注释
Java注释
基本命名规则
(1) 名字应能反映它所代表的实际东西,应有一定实际意义。例如,表示次数的量用Times,表示总量的用Total,表示平均值的用Average,表示和的量用Sum等
(2) 使用可以准确说明变量/字段/类/接口/包等的完整的英文描述符。例如,采用类似 firstName,listAllUsers 或 CorporateCustomer 这样的名字,严禁使用汉语拼音及不相关单词命名。
(3) 采用该领域的术语。如果用户称他们的“客户” (clients) 为“顾客” (customers),那么就采用术语 Customer 来命名这个类,而不用 Client。(4) 采用大小写混合,提高名字的可读性。一般应该采用小写字母,但是类和接口的名字的首字母,以及任何中间单词的首字母应该大写。
(5) 尽量少用缩写,但如果一定要使用,当使用公共缩写和习惯缩写等,如实现(implement)可缩写成impl,经理(manager)可缩写成mgr等,严禁滥用缩写。
(6) 避免使用长名字(最好不超过 25 个字母)。
(7) 避免使用相似或者仅在大小写上有区别的名字。
类、接口
常量
变量和参数
部件/组件
方法
语句构造力求简单直接,不要为了片面追求效率而使语句复杂化
软件测试阶段,测试人员将努力设计出一系列测试方案,目的是破坏已经建造好的软件系统——竭力证明程序中有错误,不能按照预定要求正确工作。发现问题不是最终目的,是为了解决问题。
Pareto原理:程序中80%的错误一般是由20%的软件模块造成的
黑盒测试
已经知道产品应该具有的功能,通过测试来检验是否每个功能都能正常使用
白盒测试
已经知道产品的内部工作过程,通过测试来检验产品内部动作是否按照规格说明书的规定正常进行
模块测试
子系统测试
系统测试
验收测试
平行运行
比较重大的软件产品在验收后往往不立即投入生成性运行,而是要经过一段时间的平行运行时间的考验,即新旧系统同时运行,以便比较新旧系统的处理结构
着重对五个模块进行测试:
由审查小组正式进行人工测试源程序称为代码审查
为每个单元测试开发驱动软件或存根软件,通常驱动程序就是一个“主程序”,接收测试数据并传送给测试模块,在印出有关结果。存根程序代替被测试的模块所调用的模块,因此可被称为“虚拟子程序”。
是测试和组装软件的系统化技术,测试可能发生的接口问题
模块组装成程序有两种方法:
当使用渐增方式吧模块结合到程序中时,有自顶向下和自底向上两种集成策略
自顶向下集成
从主控制模块开始,沿着程序的控制层次向下移动,逐渐把各个模块结合起来。在把附属于(及最终附属于)主控制模块的那些模块组装到程序结构中去,或者使用深度或宽度优先的策略
具体过程
从2开始不断重复以上过程,知道构造起完整的软件结构为止。
优点:
问题
容易遇到逻辑上的问题,最常见的是因为存根程序替代了低层次的模块,使得软件结构中没有重要数据自下往上流
解决:
自底向上集成
从原子模块(最低层模块)开始组装和测试。因为时从底部向上结合模块,总能得到所需的下层模块处理功能,所以不需要存根程序
不同集成测试策略的比较
自顶向下
自底向上
与自顶向下刚好相反
实际使用时一般使用两种方法的混合
回归测试
指重新执行依据做过的测试的某个子集,以保证上述这些变化没有带来非预期的副作用
也称为验收测试,目标是验证软件的有效性
验证:指保证软件正确地实现了某个特定要求的一系列活动
确认:保证软件确实满足了用户需求而进行的一系列活动
软件有效性:如果软件的功能和性能如同用户所合理期待的那样,软件就是有效的
确认测试的范围
软件配置复查
Alpha和Beta测试
适用于面向许多客户开发的情况
语句覆盖
判定覆盖(分支覆盖)
条件覆盖
判定/条件覆盖
能同时满足判定和条件覆盖标准的逻辑覆盖
条件组合覆盖
点覆盖
与语句覆盖标准一致
边覆盖
通常与判定覆盖一致
路径覆盖
基本路径测试
步骤
由程序流程图导出程序控制流图
计算流图的环路复杂度
确定程序的独立路径
独立路径又称为基本路径,至少包含一条在其它独立路径中从未有过的边的路径
独立路径条数是确保程序中,每个可执行语句至少执行一次所必需的测试用例数目的上界
独立路径条数=环路复杂度V(G)
确定测试路径的集合
针对测试路径设计测试用例
条件测试
循环测试
基本思想
把所有可能的输入数据(包括有效和无效),划分成若干数据类(等价类),然后从每个数据类中选取少数有代表性的数据作为测试用例
这种方法完全不考虑程序的内部结构,指依据程序的规格说明来设计测试用例
原则