第一章:
第二章:
第三章:
第四章:
第五章:
第六章:
第七章:
第八章:
第九章:
软件危机:开发软件所需的高成本和软件的低质量之间有尖锐的矛盾,其主要原因是软件本身特点、缺乏好的开发方法和手段、开发效率低。
软件工程:用系统科学的工程性方法解决软件开发时遇到的问题,也就是,将系统化的、严格约束的、可量化的方法应用于软件的开发、运行和维护。需要着重记忆的部分有:
这部分存在一些争议
错误(Error):在软件开发过程中的错误,停留在需求工作和编码过程中。
例如:误解了需求,敲错了代码。
缺陷(Fault):存在于软件(文档、数据、程序)之中的那些不希望或不可接受的偏差。由于错误而引起的,存在于某些功能实现处的问题,属于功能实现层面。
例如:对需求的误解导致的需求文档错误和与设计意图不相符的设计,敲错代码导致的错误的功能实现 (缺陷是错误的结果/表现)。
软件缺陷的主要特征:
- 软件未达到软件产品需求说明书指明的要求。
- 软件出现了软件产品需求说明书中指明不应出现的错误。
- 软件功能超出软件产品说明书指明的范围。
- 软件未达到软件产品说明书未指明但应达到的要求。
- 软件测试人员认为难以理解、不易使用、运行速度慢或最终用户认为不好。
软件故障:在一个计算机程序中出现的不正确的步骤、过程或数据定义常称为故障。是指软件运行时丧失了在规定的限度内执行所需功能的能力,执行输出错误结果,导致失效。
软件失效:是指软件运行时产生的一种不希望或不可接受的外部行为,偏离了用户需求。
软件错误、缺陷、故障和失效:
软件故障和失效的比较:
软件失效 | 软件故障 |
---|---|
面向用户 | 面向开发者 |
软件运行时偏离用户需求 | 程序执行输出错误结果 |
根据对用户应用的严重性等级分类 | 根据定位和排除故障的难度分类 |
软件质量可以从以下三个方面衡量:
产品质量的标准包括:
过程质量的标准涉及软件过程中发生的事件,他们会影响过程的质量,并最终影响软件的质量,量化的方法包括CMM等。
CMM(Capability Maturity Model,能力成熟度模型)是一种用于评估和改进软件工程组织过程质量的方法。CMM定义了五个成熟度级别,每个级别描述了组织在软件过程管理方面的不同能力水平:
- 初始级别(Level 1 - Initial):过程是无序的,项目的成功依赖于个别人员的能力和努力。
- 可重复级别(Level 2 - Repeatable):过程已经被定义,并且在项目中得到了重复使用。组织开始建立基本的项目管理控制。
- 定义级别(Level 3 - Defined):过程已经被标准化,并在整个组织范围内得到了统一的应用。组织能够根据标准过程执行项目。
- 管理级别(Level 4 - Managed):过程已经被量化和统计,并进行了过程管理和控制。组织能够通过度量和分析来预测项目的结果。
- 优化级别(Level 5 - Optimizing):过程已经通过持续的改进得到优化。组织能够根据量化的数据和经验教训来改进过程。
商业价值的标准包括:
现代软件工程大致包含如下阶段(共九个):
软件工程涉及的其他主要概念:
软件过程是软件开发活动中产生某种期望结果的一系列有序任务,涉及活动、约束和资源。他的重要性体现在:
软件生命周期是软件开发过程的别称。它包括的就是这九个阶段;而每个阶段本身又是一个过程,由一系列活动构成,每个活动又涉及输入、子活动、约束、资源、输出。
常用的软件开发模式有四种:1、瀑布开发模式;2、迭代式开发模式;3、螺旋开发模式;4、敏捷开发模式;
在瀑布模型中,软件开发的各项活动严格按照线性方式进行,当前活动接受上一项活动的工作结果,实施完成所需的工作内容。当前活动的工作结果需要进行验证,如验证通过,则该结果作为下一项活动的输入,继续进行下一项活动,否则返回修改。
其特点在于:
瀑布模型的优点:
瀑布模型的缺点:
瀑布开发模式各阶段及产生的文档:
阶段 | 产生的文档 |
---|---|
需求分析 | SRS(软件需求规格说明书) |
系统设计 | SAD(系统设计文档) |
程序设计 | 模块功能算法和数据描述文档 |
编码 | 源程序和注释 |
单元测试和集成测试 | 单元测试报告 |
系统测试 | 系统测试报告 |
验收测试 | 验收测试报告 |
运行与维护 | 维护报告 |
原型的定义:一种部分开发的产品,用来让开发者和用户共同研究,提出意见,为最终产品定型。其用途在于:
原型化是对瀑布开发模式的一定改进。
V模式基于瀑布模型,让迭代更明显。
瀑布模型强调文档与提交产物,V模型强调开发活动及正确性,允许各种重复活动。(增加了各种针对性的措施)
分阶段开发模型的定义:系统被设计成部分提交,用户每次都只能得到部分功能,而其他部分仍处于开发中。这种开发模型使得每个软件版本的周期变短。
根据发布形式的不同,分阶段开发模型有如下三种:
分阶段开发模型的特点(主打一个快):
螺旋模型的每一圈是一次迭代,每一个象限代表一重迭代中的某一部分任务。
注意看,每一个循环对应的内容存在差异。特别是开发与测试和计划两个象限。
螺旋开发模式的四个象限有各自的含义:
四重循环的含义:
UP模型即统一过程模型,是一种用例驱动的,以基础架构为中心的,迭代式、增量式的软件开发模型。
RUP模型是IBM提出的RUP模型是IBM提出的提供支持和包装的UP模型。
UP模型包含四个阶段:
UP模型的六个核心工序:
UP模型的三个支持工序:
进化式迭代开发是RUP的关键实践:
敏捷方法:一种较新型软件开发方法。不要求遵循传统的软件开发流程,强调快速开发和有效适应需求变化。
敏捷开发的四个基本原则:
敏捷方法强调:人与人之间的交互是复杂的,并且其效果从来都是难以预期的,但却是工作中最重要的方面。其目标在于:尽可能早的,持续的交付有价值的软件系统,以客户满意为最终目标。
敏捷方法的最佳实践包括XP、结对编程、测试驱动开发等。
极限编程(XP)是一种轻量级的软件开发方法,属于敏捷开发方法。它将复杂的开发过程分解为一个个相对比较简单的小周期,通过交流、反馈等方法,开发人员和客户可以非常清楚开发进度、变化、待解决的问题和潜在的困难等,并根据实际情况及时地调整开发过程。
XP的四个变量:成本、时间、质量和范围。
XP的四个基本原则:
派对编程/结对编程:两个程序员共同开发程序,且角色分工明确。一个负责编写程序,另一个负责复审与测试,并且两人定期交换角色。
项目进度:项目进度是对特定项目的软件开发周期的刻画。这包括对项目阶段、步骤、活动的分解,对各个离散活动交互关系的描述,对各个活动的完成时间及整个项目完成时间的初步估算。其中相关名词如下:
AOE网,必须会
妈的,自己学吧。
软件项目团队有两种基本结构:
主程序员负责制:主程序员负责所有决策,同时分配任务并监督成员。其余人向他汇报,并由主程序员进行最终决策并为此负责。副主程序员是二号人物,在必要时替代主程序员。资料员负责维护所有的项目文档,编译和链接代码,并对提交的所有模块进行初步测试。
优势:交流最小化,迅速做出决定,效率高。
劣势:主观性强,对主程序员要求高。
忘我制:去中心化,全员参与决策、共担责任。每个成员平等的承担责任,批评和表扬只针对产品,不针对个人。
队伍结构性的特点:队伍的结构性越强,越能按时完成复杂任务,但也更加循规蹈矩,能给出普通但功能完备的成品,适合规模大的团队解决稳定的任务;队伍的结构性越弱,创造性越强,越能对问题给出创造性的解决方案,但是不稳定,容易无法按时限完成任务,适合解决含有大量不确定因素的问题。
COCOMO模型针对项目开发的不同阶段来设置工作量的衡量标准,逐步细化,逐渐准确(就是为了方便给程序员算工资):
软件风险:软件生产过程中不希望看到的,有负面结果的事件。软件的风险有两方面:风险影响(即风险发生会造成的损失)和风险概率(即风险发生的概率)。
风险管理活动包括两方面:
降低风险的策略有三种:
避开风险:改变功能和性能需求,避开可能的风险。
例如:不用
C
改用Java
来避免内存泄漏。
转移风险:设法将风险转移到其他系统中,或者买个保险来补偿风险发生时造成的损失(毕竟风险不是100%发生)。
假设风险:总假设风险会发生,接受它,并使用资源来控制风险的后果。
需求是对来自用户的关于对软件系统的期望行为(做什么)的综合描述,涉及对象、状态、约束、功能等。
需求阶段作为一个工程,确定需求的过程如下:
获取需求时,如果有冲突,应该按照以下顺序划分优先级:
绝对要满足的需求。
例如:信用卡转账记录必须被持久化保存,并且可以列出,信用卡账单不保存是不可能的。
非常值得要但并非必要的需求。
例如:信用卡转账时要能实时发送给用户,这能极大的方便用户,但是非要说的话没有这个功能信用卡也能用。
可要可不要的需求。
例如:账单根据金额不同用不同的颜色显示。
需求文档分为两种:
比较蛋疼的一部分,首先需求分为功能性需求和非功能性(质量)需求:
功能性需求:描述系统内部功能或系统与外部环境的交互功能,涉及系统的输入应对、实体状态变化、结果输出、设计约束、过程约束等。
功能性需求的特征是:它针对的是解决问题的方案和可选方案的边界。
非功能性(质量)需求:描述软件方案必须具有的质量特征,比如响应时间、安全性、易用性等。
非功能性需求的特征是:它们不是用户想让系统完成的事,而是用户希望完成的有多好。
其次是需求会因为一些原因受到约束,也就是一些限制,可以分为两种约束:
设计约束:已经做出的设计决策或限制问题解决方案集合的设计决策。设计约束会让我们无法使用一些设计方案,设计约束是技术层面的。
设计约束包括:物理环境限制(如设备能力上限)、接口限制(如预定的输入输出格式)、用户限制(甲方有限制情况)。
过程约束:对于构建系统的技术和资源的限制,这个“技术”也包括软件开发模式等。
过程约束包括:资源限制(如人员、材料、资金)、文档限制(如文档量有要求)等,过程约束是外部要求。
四个比较蛋疼的概念,多品品(应该从下往上看比较好,先了解设计的定义):
软件体系结构:一种软件解决方案,用于解释如何将软件系统分解为单元,单元之间如何相互关联,单元的所有外部特性。
设计模式:一种针对单个或少量软件模块给出的一般性软件解决方案,这种设计决策层次低于软件体系结构。
设计公约:一系列设计决策和建议的集合,用于提高系统某一方面的设计。当一种设计公约发展成熟时,将会被封装成设计模式或体系结构风格,最后可能被内嵌封入程序语言结构。
对象就是一个例子。对象、模板等都是编程语言支持的设计和编程公约。
设计:将需求中的问题转换为软件解决方案的创造性过程。概念设计和技术设计是设计的两面 :
软件设计过程模型的五个阶段:
涉及用户界面时需要考虑这些问题:
其中,要素包括:
文化差异包括用户的信仰、价值观、道德规范和传统等因素,因此需要:
用户爱好问题可以为具有不同偏好的人选择备选界面。
模块独立性:模块之间彼此独立的程度。模块独立性取决于内聚和耦合程度,我们追求的是高内聚、低耦合。
耦合:两个软件部件之间的相互关联程度。根据耦合程度可以划分为紧密耦合、松散耦合、低耦合三种,具体来讲有六种:
非直接耦合:模块之间没有信息交换。
数据耦合:模块之间传递数据,但不限定数据结构。
例如:传递水费、电费。
特征耦合:模块之间传递的是数据结构(这代表两者传递信息是使用了相同的数据结构)。
例如:传递水电账单。
控制耦合:模块间传递的是控制量,控制量由一个模块传出,作为另一个模块完成功能的必要条件,控制另一个模块的活动。
例如:一个模块传出一个
flag
给别的模块,别的模块根据flag
执行不同操作。
公共耦合:不同模块访问公共的数据。
例如:大家一起访问全局变量。
内容耦合:一个模块直接修改另一个模块(直接调用另一个模块的私有数据,或者一个模块在另一个模块中)。
例如:A里直接有一个B对象。
内聚:模块内部各组成成分的关联程度。可以按层次划分为高内聚、低内聚。具体来讲有七种:
偶然性内聚:不相关的功能、过程、数据出现在同一个部件中。
例如:数据预处理,不同的模块在不同的步骤位置恰巧都有数据预处理,所以合并为一个模块。
逻辑性内聚:逻辑上相似或相关的功能或数据放置在同一个模块内。
例如:计算平均分和最高分都可以归纳为读数据、算、输出,那么读数据、输出就可以放进同一个模块里。虽然读和输出不能说有必然联系,但在系统中也可以理解为有逻辑相关性。逻辑性内聚是几个功能整体上在几个流程的相似位置出现,从而有了逻辑的相关性,而不是偶然性内聚中表现的,完全不同的流程中的随机位置恰好有相似的部分。
时间性内聚:模块中各部分要求在同一时间内完成。
例如:一个初始化模块中有为变量赋初始值, 打开文件等功能,这些功能不一定说谁是谁的前提,但是他们都必须在“初始化”这一段时间内完成。
过程性内聚:模块中的各部分有先后顺序。
例如:数据分析模块中有输入数据、检查数据、分析数据的过程,它们必须按顺序执行才行,前面不执行后面就不能执行。
通讯性内聚:模块中各个部分共享数据。
例如:一个传感器读取模块,里面的数据来自于各个传感器,彼此不一定相关,但是对模块内的各部分共享。
顺序性内聚:模块中的各个部分的输入与输出相连。
功能性内聚:模块中的各个部分只构成了一个单一功能。
设计复审:检查我们的软件设计、软件体系结构图是否满足了所有功能与质量需求。其重要性在于复审中批评和讨论是“忘我”的,能将开发人员更好地团结在一起,提倡并增强了成员之间的交流在评审过程中故障的改正还比较容易,成本还不高,在这时候发现故障和问题会使每一个人受益。复审可以分为两类:
面向对象的设计原则:
OO开发过程的五个步骤:
各种UML图:
一般性的编程原则应该从以下三个方面考虑:
首先是HCB,即头部注释版块,是总览性的信息,用于标识程序、描述数据结构、算法、控制结构。具体信息包括:
除此以外,还需要添加:
与此同时,编写内部文档时还要注意:
软件产生缺陷的原因:
为何要对软件缺陷进行分类:系统中不存在明显的故障时,我们就需要对程序进行测试,创造一些条件以期让代码不能像计划那样做出反应,看看有没有更多故障。而为了更容易发现这些故障,为故障进行分类时很重要的。
主要的缺陷类型(10种):
正交缺陷分类:使任意一个缺陷只属于一个类别的缺陷分类方案称为正交缺陷分类。
如果故障属于不止一个类,则失去了度量的意义。
测试的各个阶段如上图所示,具体包括:
单元测试:验证组件的功能。依据文档:程序代码与配套文档。
集成测试:验证系统组件是否能正确的协同工作。依据文档:系统体系结构文档SAD、程序设计规格说明。
功能测试:验证系统是否能执行需求规格说明中描述的功能。依据文档:软件需求规格说明书SRS。
性能测试:验证系统的软硬件表现和性能是否符合需求规格说明文档。这一步之后,软件系统应当能在客户的实际工作环境中成功执行,这时我们说产生了一个被确认的系统。依据文档:软件需求规格说明书SRS。
验收测试:验证系统是否满足了客户的需求定义(需求定义和需求规格说明是有区别的)。依据文档:客户需求定义。
安装测试:验证系统能否在用户使用的真实环境中安装并正常运行。依据文档:用户环境的说明。
系统测试:功能测试、性能测试、验收测试和安装测试统称为系统测试。
黑盒测试:人员在完全不了解程序内部的逻辑结构和内部特性的情况下,只依据程序的需求规格及设计说明,检查程序的功能是否符合它的功能说明。其原则是依据系统需求文档、系统设计文档、程序设计文档进行测试,正确的结果是系统完成了所有该做的,拒绝了一切不该做的。
白盒测试:人员拥有全套文档,以程序内部结构为基本依据,手动或自动进行测试。
黑盒测试的分类方法包括如下四种:
等价分类法中,等价类可以分为有效等价类和无效等价类两种,前者是正常输入,后者是不应当输出正确结果的无效输入。在划分等价类时,需要尽可能“密铺”式划分,并且尽可能不对测试的实体整体添加限制条件以提升可扩展性。
对有效等价类的用例,尽量用一个用例覆盖尽可能多的等价类,这是因为任意一个有效等价类的处理出现问题都会产生故障,被我们发现,我们可以以此减少测试次数;对无效等价类,必须为每一个无效等价类都设计一个专内验证该点的用例,这是因为所有无效等价类的表现都是不正常返回,不进行共用以避免多个错误一起发生导致漏过一些错误。用例可以进一步区分为如下三种:
弱一般等价类:测试每个一般等价类都至少出现在用例中一次即可。
基于单缺陷假设:失效很少因为两个或更多缺陷同时共同引发。
弱健壮等价类:考虑有效等价类之外,划分出的无效等价类,并且每个等价类在用例中至少出现一次即可;仍基于单缺陷假设。
强一般等价类:测试用例应当遍历等价类的所有笛卡尔积组合;基于多缺陷假设。
白盒测试的方法包括两种:
逻辑覆盖法是一组逻辑覆盖方法的统称,按照程序逻辑覆盖程度分为:
满足条件覆盖不一定满足分支覆盖,例如
a && b
,当用例包括a=false, b=true
和a=true, b=false
时就满足条件覆盖,但显然不满足分支覆盖。
路经测试法借助程序图设计测试用例,包括四种:
结点覆盖:经过所有结点,相当于逻辑覆盖中的语句覆盖。
边覆盖:覆盖所有边,相当于逻辑覆盖中的判定覆盖。
完全覆盖:同时满足结点覆盖和边覆盖,也即走过所有位置,这是测试简单程序的最低标准。
路径覆盖:程序图中每条路径都至少经过一次。
路经覆盖法与穷举测试有所不同,路径覆盖法并不关注循环次数,某个循环语句循环1次和n次对于路径覆盖法是一样的,但是穷举测试则认为它们是不同的。
单元测试:将每个程序构件与系统中其他构件隔离,对其单独进行测试。
在集成测试及以后的阶段,除去很小的程序,都应当使用黑盒原则进行测试。这一具体流程包括:
对于单元测试,我们往往可以直接参考模块的源代码,并且工作量可以承受,所以宜采用黑盒法白盒法结合运用——先使用黑盒法设计测试用例,然后使用白盒法进行补充,达到我们期望的覆盖标准。
下面举一个测试的例子,基本流程如下:
本例先用黑盒法设计测试用例,然后用白盒法进行检验与补充。
首先使用等价分类法和边值法、猜错法进行分析:
根据等价类设计测试用例:
用白盒法检验测试用例,可以发现前几个就能完成边覆盖,因此不需要补充更多测试用例:
如上,完成一个测试的设计。
集成测试验证系统组件是否能正确的协同工作,选择策略要兼顾系统特性和客户需求。
集成测试的方法有四种:
自底向上的集成测试:从模块结构图的最底层开始,由下而上按调用关系逐步添加新模块,组成子系统分别测试,直到全部组装完毕。典型特征是:添加的新模块调用的下层模块都必须被全部测试完毕、 使用驱动模块。
其优点在于:
- 容易生成测试用例(因为底层都是真实模块)。
- 适合面向对象方法(每次加入的是经过测试的对象,也符合消息的传递方式)。
- 当许多低级组件经常在各个地方被调用时,这种方法十分适用
缺点是许多存在于高层模块中的关键错误无法被及时发现。
自顶向下的集成测试:从顶层控制组件开始进行测试,然后逐步将调用的下级组件组合起来,再对更大的子系统测试,直到全部组装完毕;典型特征是:添加的新模块,调用它的上层模块必须被测试过、 使用桩模块。
其优点在于上层的问题常常是影响更大的,自顶向下更利于发现这些关键问题。
缺点:
- 生成测试用例更难。
- 可能需要很多桩。
莽撞/一次性测试:先测试每一个模块,之后将所有模块一并集成。
混合方式/三明治方式测试:从上到下将模块分为三层:上层、目标层、下层。上层自顶向下,下层自底向上,中层直接使用驱动模块+桩模块独立测试,最后集成三层,测试集中于目标层。
传统测试与OO测试的区别:
OO测试面临如下四个困难:
系统测试的过程如上图所示,具体步骤包括:
回归测试是用于新版本的一种测试,验证它与旧版本相比,是否仍以相同的方式执行着相同的功能。
功能测试的作用:以高检测率发现缺陷(因为一项功能测试只面向一小组组件,不容易导致多个缺陷彼此掩盖)。
功能测试的基本指导原则:
性能测试的作用:确保系统的可靠性、可用性和可维护性。
性能测试的主要分类包括如下13种:
测试需要考虑的三个性能:
可靠性:软件系统在给定的时间范围和条件下运行成功的概率。
可用性:软件系统在给定的时间点成功运行的概率。
可用性强调某一时刻系统正常,系统可能在相当长一段时间内都可用,保持了可靠性,但在不能使用(例如检修)的那一刻,它失去了可用性。
可维护性:是指在给定的使用条件(包括:预定的时间间隔、可用的维护程序、可用的维护资源之下进行维护)下,维护活动能被执行的概率。
验收测试由客户检查软件系统是否满足了他们的需求定义,主导者是客户,开发者只负责解答一些必要的问题。
有三种分类:
基准测试:先由用户准备测试用例,然后在实验环境中安装系统,最后由用户进行评估。
引导测试:先将系统安装在实验环境中,然后在假设系统正式安装的前提下,由测试者在测试系统上进行日常工作,而不是依赖于测试用例;测试和测试都属于引导测试。
测试:由开发者自己组织人员或委托专业团队来进行小规模测试;测试:由客户实际进行小规模测试
并行测试:当软件的一个旧版本正在使用,并且要测试一个新版本时使用;新旧版本并行运转,来自用户的操作会同时在新旧系统上执行,旧系统实际工作,新系统进行测试,使用户逐渐习惯新系统。
安装测试:验证系统能否在用户使用的真实环境中安装并正常运行,以发现并解决因开发环境和用户环境不同所引起的问题。