[软件工程 复习] 记录
文章目录第一章1.1 软件危机消除软件危机的途径1.2 软件工程1.3 软件工程知识体系第二章2.1 软件生命周期(SDLC,Systems Development Life Cycle)2.1.1 基本任务2.2 计划驱动的软件过程:Waterfall Model(瀑布模型,或称之为SDLC模型)2.3 快速原型(Rapid Prototype)2.4 增量模型(Increment Model)
文章目录
- 第一章
- 第二章
- 第三章
- 第四章
- 第五章 结构化实现
- 第六章 面向对象方法学导论
- 参考资料
第一章
1.1 软件危机
软件危机是指在计算机软件的开发和维护过程中所遇到的一系列严重问题。这些问题绝不仅仅是不能正常运行的软件才具有的。实际上,几乎所有软件都不同程度地存在这些问题。概括地说,软件危机包含下述两方面的问题:如何开发软件,以满足对软件日益增长的需求;如何维护数量不断膨胀的已有软件。
消除软件危机的途径
1.技术措施
使用更好的软件开发技术、开发工具、开发工具。
2.组织管理措施
(1)创造良好的组织、严密的管理与协调工作的机制软件开发不是某种个体劳动的神秘技巧,而应该是一种组织良好、管理严密、各类人员协同配合、共同完成的工程项目。
(2)摆脱软件危机的主要出路是,按工程化的原则和方法组织软件的开发工作。
(3)强调文档的重要性。
1.2 软件工程
概括来讲,软件工程是指导计算机软件开发和维护的工程学科。采用工程的概念、原理、技术和方法来开发与维护软件,吧经过时间考验而证明正确的管理技术和当前能够得到的最好的技术结合起来,经济地开发出高质量的软件并有效的维护它,这就是软件工程。
1993年IEEE(电气电子工程师学会)给出的一个更全面的定义。
软件工程是:1 把系统化的、规范的、可度量的途径用于软件开发、运行和维护的过程,也就是把工程化应用与软件中;2 研究 1 中提到的途径
1.3 软件工程知识体系
IEEE在2004年发布的《软件工程知识体系指南》中将软件工程知识体系划分为以下10个知识领域。
1 软件需求(software requirements)
软件需求表达了为解决某些真实世界问题而施加在软件产品上的要求和约束。软件需求的主要类型包括:产品与过程,功能性与非功能性,突出的属性。软件需求知识领域涉及软件需求的抽取、分析、规格说明和确认。软件工业界一致认同的就是如果这些工作完成得不好,软件工程项目就很容易失败。
2 软件设计(software design)
软件设计是一个过程和这个过程的结果,此过程对一个系统或组件定义架构(architecture,也叫体系结构)、组件、接口以及其他特征。在这项活动中分析软件需求以产生一个软件内部结构的描述,此描述将成为软件构建的基础。
3 软件构建(software construction)
软件构建指的是如何创建产生软件的详细步骤,这其中包括编码、验证、单元测试、集成测试和调试。
4 软件测试(software testing)
测试是一个标识产品的缺陷和问题的活动。测试的目的是为了评估和改进产品质量。
软件测试通过使用有限的测试用例来动态地验证程序是否能达到预期的行为。有限的测试用例是从通常情况下有无限可能性的执行领域中适当地选取出来的。
5 软件维护(software maintenance)
软件开发工作的结果就是交付一个满足用户需求的软件产品。软件产品一旦投入运行,产品的缺陷就会被逐渐地暴露出来,运行的环境会逐渐发生变化,新的用户需求也会不断地浮出水面。软件维护就是要针对这些问题而对软件产品进行相应地修改或演化,从而修正错误,改善性能或其他特征,以及使软件适应变化的环境。
6 软配置管理(software configuration management)
软件配置管理(Software Configuration Management,SCM)是一项跟踪和控制软件变更的活动。
7 软件工程管理(software engineering management)
软件工程管理是软件的开发和维护的管理活动,为了达到系统的、遵循规程的和可量化的目标,它包括计划、协调、度量、监控、控制和报表。
8 软件工程过程(software engineering process)
可以在两个层次上分析软件工程过程领域,第1个层次包括软件生命周期过程中技术的和管理的活动,它们是在软件获取、开发、维护和退出运行中完成的。第2个层次是元层次,涉及软件生命周期过程本身的定义、实现、评估、管理、变更和改进。
9 软件工程工具和方法(software engineering tools and methods)
软件开发工具是用于辅助软件生命周期过程的基于计算机的工具,工具可以将重复并明确定义的动作自动化,减少了软件工程师的认知负担,使软件工程师可以集中在开发过程的创造性方面。
软件工程方法的目标是使软件工程活动系统化并最终更可能成功。
10 软件质量(software quality)
对于Phil Crosby,质量就是“遵从用户需求”,Watts Humphrey认为质量就是
“达到适合使用的卓越层次”。IBM发明了术语“市场驱动的质量”,它基于达到全面的客户满意,关于组织质量的Baldrige准则使用了一个类似的短语“客户驱动的质量”,将客户满意作为主要的考虑。在ISO9001-00中,质量被定义为“一组内在特征满足需求的程度”。
第二章
软件过程
软件工程过程是为了获得高质量软件所需要完成的一系列任务的框架,它规定了完成各项任务的工作步骤。
本章讲述在软件生命周期全过程中应该完成的基本任务,并介绍各种常用的过程模型。
2.1 软件生命周期(SDLC,Systems Development Life Cycle)
1.软件定义:确定软件开发工程必须完成的总目标;确定工程的可行性;导出实现工程目标应该采用的策略及系统必须完成的功能;估计完成该项工程需要的资源和成本,并且制订工程进度表。这个时期的工作通常又称为系统分析,由系统分析员负责完成。软件定义时期通常进一步划分为3个阶段,即问题定义、可行性研究和需求分析。
2.软件开发:具体设计和实现在前一个时期定义的软件,它通常由下述4个阶段组成:概要设计、详细设计、编码和单元测试、综合测试。其中前两个阶段又称为系统设计,后两个阶段又称为系统实现。
3.运行维护:当软件在使用过程中发现错误时应该加以改正;当环境改变时应该修改软件以适应新的环境;当用户有新要求时应该及时改进软件以满足用户的新需要。通常对维护时期不再进一步划分阶段,但是每一次维护活动本质上都是一次压缩和简化了的定义和开发过程。
2.1.1 基本任务
问题定义
通过调研,系统分析员应该提出关于问题性质、工程目标和工程规模的书面报告,并且需要得到客户对这份报告的确认
可行性研究
确定上一个阶段的问题是否有行得通的解决办法
用最小的代价在尽可能短的时间内确定问题是否能够解决。
需求分析
解决“目标系统必须做什么”这个问题可行性研究的基本目的是用较小的成本在较短的时间内确定是否存在可行的解法,因此许多细节被忽略了。然而在最终的系统中却不能遗漏任何一个微小的细节
是用正式文档准确地记录对目标系统的需求,该文档通常称为规格说明(specification)。
概要设计
概括地回答“怎样实现目标系统?”概要设计又称为初步设计、逻辑设计、高层设计或总体设计。
设计几种可能的方案
推荐最佳方案
制定详细计划
模块化
详细设计
将抽象概括的方式具体化
设计出程序的详细规格说明
详细设计也称为模块设计、物理设计或低层设计。在这个阶段将详细地设计每个模块,确定实现模块功能所需要的算法和数据结构。
编码和单元测试
这个阶段的关键任务是写出正确的,容易理解、容易维护的程序模块
程序员应该根据目标系统的性质和实际环境,选取一种适当的高级程序设计语言(必要时用汇编语言),把详细设计的结果翻译成用选定的语言书写的程序,并且仔细测试编写出的每一个模块。
综合测试
这个阶段的关键任务是通过各种类型的测试(及相应的调试)使软件达到预定的要求。最基本的测试是集成测试和验收测试。必要时还可以再通过现场测试或平行运行等方法对目标系统进一步测试检验。
集成测试是根据设计的软件结构,把经过单元测试检验的模块按某种选定的策略装配起来,在装配过程中对程序进行必要的测试。
验收测试则是按照规格说明书的规定(通常在需求分析阶段确定),由用户(或在用户积极参加下)对目标系统进行验收。
软件维护
通过各种必要的维护活动使系统持久地满足用户的需要。
改正性维护:也就是诊断和改正在使用过程中发现的软件错误
适应性维护:即修改软件以适应环境的变化
完善性维护:即根据用户的要求改进或扩充软件使它更完善
预防性维护:即修改软件为将来的维护活动预先做准备。
实际上每一项维护活动都应该经过提出维护要求(或报告问题),分析维护要求,提出维护方案,审批维护方案,确定维护计划,修改软件设计,修改程序,测试程序,复查验收等一系列步骤,因此,实质上是经历了一次压缩和简化了的软件定义和开发的全过程。每一项维护活动都应该准确地记录下来,作为正式的文档资料加以保存。
2.2 计划驱动的软件过程:Waterfall Model(瀑布模型,或称之为SDLC模型)
https://blog.csdn.net/az9996/article/details/88064598
1.阶段间具有顺序性和依赖性
①必须等前一阶段的工作完成之后,才能开始后一阶段的工作;②前一阶段的输出文档就是后一阶段的输入文档。
传统的瀑布模型
实际的瀑布模型(加入迭代过程的瀑布模型)
2.3 快速原型(Rapid Prototype)
快速原型(rapid prototype)是快速建立起来的可以在计算机上运行的程序,它所能完成的功能往往是最终产品能完成的功能的一个子集。如图所示(图中实线箭头表示开发过程,虚线箭头表示维护过程)
快速原型模型是不带反馈环的,软件产品的开发基本上是按线性顺序进行的。
原型系统已经通过与用户交互而得到验证,据此产生的规格说明文档正确地描述了用户需求。
开发人员通过建立原型系统已经学到了许多东西,在设计和编码阶段发生错误的可能性也比较小。
快速原型的本质是“快速”。开发人员应该尽可能快地建造出原型系统,以加速软件开发过程,节约软件开发成本。原型的用途是获知用户的真正需求,一旦需求确定了,原型将被抛弃。
当快速原型的某个部分是利用软件工具由计算机自动生成的时候,可以把这部分用到最终的软件产品中。
2.4 增量模型(Increment Model)
增量模型也称为渐增模型,使用增量模型开发软件时,把软件产品作为一系列的增量构件来设计、编码、集成和测试。每个构件由多个相互作用的模块构成,并且能够完成特定的功能。
增量模型分批地逐步向用户提交产品,每次提交一个满足用户需求子集的可运行的产品。整个软件产品被分解成许多个增量构件,开发人员一个构件接一个构件地向用户提交产品。每次用户都得到一个满足部分需求的可运行的产品,直到最后一次得到满足全部需求的完整产品。
难点:把每个新的增量构件集成到现有软件体系结构中时,必须不破坏原来已经开发出的产品。此外,必须把软件的体系结构设计得便于按这种方式进行扩充,向现有产品中加入新构件的过程必须简单、方便。
从某种意义上说,增量模型本身是自相矛盾的。它一方面要求开发人员把软件看做一个整体,另一方面又要求开发人员把软件看做构件序列,每个构件本质上都独立于另一个构件。除非开发人员有足够的技术能力协调好这一明显的矛盾,否则用增量模型开发出的产品可能并不令人满意。
2.5 螺旋模型(spiral model)
使用原型及其他方法来尽量降低风险。理解这种模型的一个简便方法,是把它看做在每个阶段之前都增加了风险分析过程的快速原型模型,图中带箭头的点画线的长度代表当前累计的开发费用,螺线旋过的角度值代表开发进度。
对可选方案和约束条件的强调有利于已有软件的重用,也有助于把软件质量作为软件开发的一个重要目标。减少了过多测试(浪费资金)或测试不足(产品故障多)所带来的风险。维护只是模型的另一个周期,在维护和开发之间并没有本质区别。
螺旋模型主要适用于内部开发的大规模软件项目。如果进行风险分析的费用接近整个项目的经费预算,则风险分析是不可行的。事实上,项目越大,风险也越大,因此,进行风险分析的必要性也越大。
螺旋模型的主要优势在于,它是风险驱动的,但是,这也可能是它的一个弱点
2.6 喷泉模型(Fountain model)
迭代是软件开发过程中普遍存在的一种内在属性。经验表明,软件过程各个阶段之间的迭代或一个阶段内各个工作步骤之间的迭代,在面向对象范型中比在结构化范型中更常见。如图所示的喷泉模型是典型的面向对象生命周期模型。“喷泉”这个词体现了面向对象软件开发过程迭代和无缝的特性。
为避免使用喷泉模型开发软件时开发过程过分无序,应该把一个线性过程(例如,快速原型模型或螺旋模型中的中心垂线)作为总目标。但是,同时也应该记住,面向对象范型本身要求经常对开发活动进行迭代或求精。
2.7 Ration统一过程
Rational统一过程(rational unified process,RUP)是由Rational软件公司(已被IBM并购)推出的一个软件开发过程框架。所谓软件开发过程框架是指团队可以根据具体的项目组或软件开发企业的不同需求,能够定义、配置、定制和实施一致的软件开发过程。
核心元素
1.用于成功开发软件的一组基本观念和原则。
2.一套关于可重用方法内容和过程构建的框架。
3.基础的方法和过程定义语言。
最佳实践
1.迭代式开发:迭代式开发允许在每次迭代过程中需求发生变化,这种开发方法通过一系列细化来加深对问题的理解,因此能更容易地容纳需求的变更。也可以把软件开发过程看做是一个风险管理过程,迭代式开发通过采用可验证的方法来减少风险。
2.管理需求:在开发软件的过程中,客户需求将不断地发生变化,因此,确定系统的需求是一个连续的过程。RUP描述了如何提取、组织系统的功能性需求和约束条件并把它们文档化。经验表明,使用用例和脚本是捕获功能性需求的有效方法,RUP采用用例分析来捕获需求,并由它们驱动设计和实现。
3.使用基于组件的架构:所谓组件就是功能清晰的模块或子系统。系统可以由已经存在的、由第三方开发商提供的组件构成,因此组件使软件重用成为可能。RUP提供了使用现有的或新开发的组件定义架构的系统化方法,从而有助于降低软件开发的复杂性,提高软件重用率。
4.可视化建模:RUP与可视化的统一建模语言(Unified Modeling Language,UML)紧密地联系在一起,在开发过程中建立起软件系统的可视化模型,可以帮助人们提高管理软件复杂性的能力。
5.验证软件质量:某些软件不受用户欢迎的一个重要原因,是其质量低下。在软件投入运行后再去查找和修改出现的问题,比在开发的早期阶段就进行这项工作需要花费更多的人力和时间。在RUP中,软件质量评估不再是一种事后的行为或由单独小组进行的孤立活动,而是内建在贯穿于整个开发过程的、由全体成员参与的所有活动中。
6.控制软件变更:在变更是不可避免的环境中,必须具有管理变更的能力,才能确保每个修改都是可接受的而且能被跟踪的。RUP描述了如何控制、跟踪和监控修改,以确保迭代开发的成功。
RUP的十大要素
(1)前景:制定前景
有一个清晰的前景是开发一个满足项目干系人(stakeholder)需求的产品的关键。前景(vision)给更详细的技术需求提供了一个高层的、有时候是合同式的基础。
前景的内容将回答以下问题:
◆关键术语是什么?(词汇表)
◆我们要尝试解决什么问题?(问题声明◆谁是项目干系人?谁是用户?他们的需要是什么?
◆产品的特性是什么?
◆功能性需求是什么?(用例)
◆非功能性需求是什么?
◆设计约束是什么?
(2)计划:按计划管理
在RUP中,软件开发计划(software development plan,SDP)综合了管理项目所需的各种信息,也许会包括一些在先启阶段开发的单独的内容。SDP必须在整个项目中被维护和更新。
(3)风险:降低风险并跟踪相关问题
RUP的要点之一是在项目早期就标识并处理最大的风险。项目组标识的每一个风险都应该有一个相应的缓解或解决计划。风险列表应该既作为项目活动的计划工具,又作为组织迭代的基础。
(4)业务案例:检验业务案例
业务案例主要用于为实现项目前景而制定经济计划。一旦制定之后,业务案例就用来对项目提供的投资收益率(ROI)进行精确的评估。
业务案例的描述不应深挖问题的细节,而应就为什么需要该产品树立一个有说服力的论点。
(5)架构:设计组件架构
在RUP中,软件系统的架构是指一个系统关键部件的组织或结构,组件之间通过接口交互,而组件是由一些更小的组件和接口组成的。架构由软件架构文档通过多个视图表示,每个视图都描述了某一组项目干系人所关心的系统的某个方面
(6)原型:增量地构建和测试产品
RUP是为了尽早排除问题和解决风险和问题而构建、测试和评估产品的可执行版本的一种迭代方法。递增地构造和测试系统的组件,这是实施和测试规程及原则通过迭代证明价值的“要素”。
(7)评估:定期评估结果
顾名思义,RUP的迭代评估审查了迭代的结果。评估得出了迭代满足需求规范的程度,同时还包括学到的教训和实施的过程改进。根据项目的规模、风险以及迭代的特点,评估可以是对演示及其结果的一条简单的记录,也可能是一个完整的、正式的测试评审记录。
(8)变更请求:管理并控制变更
RUP的配置和变更管理流程的要点是当变化发生时管理和控制项目的规模,并且贯穿整个生命周期。在RUP中,变更请求通常用于记录和跟踪缺陷和增强功能的要求,或者对产品提出的任何其他类型的变更请求。
(9)用户支持:部署可用的产品在
RUP中,部署流程的要点是包装和交付产品,同时交付有助于最终用户学习、使用和维护产品的所有必要的材料。项目组至少要给用户提供一个用户指南(也许是通过联机帮助的方式提供),可能还有一个安装指南和版本发布说明。
(10)过程:采用适合项目的过程
选择适合正开发的产品类型的流程是非常必要的。即使在选定一个流程后,也不能盲目遵循这个流程——必须应用常理和经验来配置流程和工具,以满足组织和项目的需要。
2.8 敏捷过程与极限编程
敏捷宣言
https://blog.csdn.net/az9996/article/details/88073926
原则
1.最重要的是通过尽早和持续地交付有价值的软件以满足客户需要。
2.即使在开发后期也欢迎需求的变化。敏捷过程驾驭变化带给客户竞争优势。
3.经常交付可以使用的软件,间隔可以从几星期到几个月,时间尺度越短越好。
4.业务人员和开发人员应该在整个项目过程中每天都在一起工作。
5.使用积极的开发人员进行项目,给他们提供所需环境和支持,并信任他们能够完成任务。
6.在开发小组中最有效率和效果的信息传达方式是面对面的交谈。
7.可以使用的软件是度量进度的主要标准8.敏捷过程提倡的是持续开发过程。投资人、开发人员和用户应该维持一个长期稳定的步调。
9.持续地追求卓越的技术与良好的设计会增加敏捷性。
10.简单(尽可能减少工作量)是最重要的。
11.最好的架构、需求和设计都来自于自组织的团队。
12.团队要定期总结如何提高效率,然后相应地调整自己的行为。
极限编程
极限编程(extreme programming)是敏捷过程中最负盛名的一个,其名称中“极限”二字的含义是指把好的开发实践运用到极致。目前,极限编程已经成为一个典型的开发方法,广泛应用于需求模糊且经常改变的场合。
有效实践
◆客户作为开发团队的成员
◆使用用户素材
◆短交付周期
◆验收测试
◆结对编程
◆测试驱动开发
◆集体所有
◆持续集成
◆可持续的开发速度
◆开放的工作空间
◆及时调整计划
◆简单的设计
◆重构
◆使用隐喻
极限编程的整体开发过程
首先,项目组针对客户代表提出的“用户故事”
然后,项目组在隐喻和用户故事的基础上,根据客户设定的优先级制订交付计划
最后,开始多个迭代过程,在迭代期内产生的新用户故事不在本次迭代内解决,以保证本次开发过程不受干扰。
极限编程的迭代过程
能力成熟度模型(capability maturity model,CMM)
能力成熟度模型(capability maturity model,CMM)并不是一个软件生命周期模型,而是改进软件过程的一种策略,它与实际使用的过程模型无关。1986年美国卡内基一梅隆大学软件工程研究所首次提出能力成熟度模型(CMM),不过在当时它被称为过程成熟度模型。
问题是由管理软件过程的方法不当引起的,所以新软件技术的运用并不会自动提高生产率和软件质量。能力成熟度模型有助于软件开发组织建立一个有规律的、成熟的软件过程。改进后的过程将开发出质量更好的软件,使更多的软件项目免受时间和费用超支之苦。
能力成熟度等级
1.初始级
软件过程的特征是无序的,有时甚至是混乱的。几乎没有什么过程是经过定义的,项目能否成功完全取决于个人能力。
处于这个最低成熟度等级的组织,基本上没有健全的软件工程管理制度。每件事情都以特殊的方法来做。
2.可重复级
建立了基本的项目管理过程,以追踪成本、进度和功能性。必要的过程规范已经建立起来了,使得可以重复以前类似项目所取得的成功。
3.已定义级
用于管理和工程活动的软件过程已经文档化和标准化,并且已经集成到整个组织的软件过程中。所有项目都使用文档化的、组织批准的过程来开发和维护软件。
这一级包含了第2级的所有特征。
4.已管理级
已收集了软件过程和产品质量的详细度量数据,使用这些详细的度量数据,能够定量地理解和控制软件过程和产品。这一级包含了第3级的所有特征。
5.优化级
通过定量的反馈能够实现持续的过程改进,这些反馈是从过程及对新想法和技术的测试中获得的。这一级包含了第4级的所有特征。
第三章
需求分析是发现、求精、建模、规格说明和复审的过程。为了发现用户的真正需求,首先应该从宏观角度调查、分析用户所面临的问题。也就是说,需求分析的第1步是尽可能准确地了解用户当前的情况和需要解决的问题。
软件原型
快速建立软件原型是最准确、最有效、最强大的需求分析技术。快速原型就是快速建立起来的旨在演示目标系统主要功能的程序。构建原型的要点是,它应该实现用户看得见的功能(如屏幕显示或打印报表),省略目标系统的“隐含”功能(如修改文件)。
快速地构建和修改原型的方法和工具
(1)第四代技术(4GT)第四代技术包括众多数据库查询和报表语言、程序和应用系统生成器以及其他非常高级的非过程语言。因为第四代技术使得软件工程师能够快速地生成可执行的代码,因此,它们是理想的快速原型工具。
(2)可重用的软件构件使用一组已有的软件构件(或称为组件)来装配原型。软件构件可以是数据结构(或数据库),或软件体系结构构件(即程序),或过程构件(即模块)。必须把软件构件设计成能在不知其内部工作细节的条件下重用。应该注意,现有的软件产品可以被用做“新的或改进的”产品的原型,这也是软件原型重用的一种形式。
(3)形式化规格说明和原型环境①使得分析员能够交互地创建基于语言的规格说明;②调用自动工具把基于语言的规格说明翻译成可执行的代码;③使得用户能够使用可执行的原型代码去精化形式化的需求。
数据流图
[软件工程] 数据流(DFD)图绘制
https://blog.csdn.net/az9996/article/details/89257215
数据字典
[软件工程] 数据字典
https://blog.csdn.net/az9996/article/details/89258225
第四章
SA、SD方法
结构化开发方法是由E.Yourdon 和 L.L.Constantine 提出的,即所谓的SASD 方 法, 也可称为面向功能的软件开发方法或面向数据流的软件开发方法。
SA(Structured Approach),结构化方法是软件工程中的一种方法,结构化分析和结构化设计可以分析商业的需求,再转换为规格文件,最后再产生电脑软件、硬件配置及相关的手册及程序。
SD(Structured Design),结构化设计是一种面向数据流的设计方法,目的在于确定软件的结构。结构化分析 是一种面向功能或面向数据流的需求分析方法,采用自顶向下、逐层分解的方法,建立系统的处理流程。
分
析
模
型
转
变
成
软
件
设
计
分析模型转变成软件设计
分析模型转变成软件设计
软件设计的概念和原理
模块化
模块是由边界元素限定的相邻的程序元素(如数据说明,可执行的语句)的序列,而且有一个总体标识符来代表它。
模块化就是把程序划分成可独立命名且独立访问的模块,每个模块完成一个子功能,把这些模块集成起来构成一个整体,可以完成指定的功能满足用户的需求
模块化定义的5条标准
(1)模块可分解性
如果一种设计方法提供了把问题分解为子问题的系统化机制,它就能降低整个问题的复杂性,从而可以实现一种有效的模块化解决方案。
(2)模块可组装性
如果一种设计方法能把现有的(可重用的)设计构件组装成新系统,它就能提供一种并非一切都从头开始做的模块化解决方案。
(3)模块可理解性
如果可以把一个模块作为一种独立单元(无须参考其他模块)来理解,那么,这样的模块是易于构造和易于修改的。
(4)模块连续性
如果对系统需求的微小修改只导致对个别模块、而不是对整个系统的修改,则修改所引起的副作用将最小。
(5)模块保护性
如果在一个模块内出现异常情况时,它的影响局限在该模块内部,则由错误引起的副作用将最小。
采用模块化原理可以使软件结构清晰,不仅容易设计也容易阅读和理解。因为程序错误通常局限在有关的模块及它们之间的接口中,所以模块化使软件容易测试和调试,因而有助于提高软件的可靠性;因为变动往往只涉及少数几个模块,所以模块化能够提高软件的可修改性;模块化也有助于软件开发工程的组织管理,一个复杂的大型程序可以由许多程序员分工编写不同的模块,并且可以进一步分配技术熟练的程序员编写复杂的模块。
抽象
人类在认识复杂现象的过程中使用的最强有力的思维工具是抽象。人们在实践中认识到,在现实世界中一定事物、状态或过程之间总存在着某些相似的方面(共性)。把这些相似的方面集中和概括起来,暂时忽略它们之间的差异,这就是抽象。
由于人类思维能力的限制,如果每次面临的因素太多,是不可能做出精确思维的。处理复杂系统的唯一有效的方法是用层次的方式构造和分析它。一个复杂的动态系统首先可以用一些高级的抽象概念构造和理解,这些高级概念又可以用一些较低级的概念构造和理解,如此进行下去,直至最低层次的具体元素。
软件工程过程的每一步都是对软件解法的抽象层次的一次精化。
逐步求精和模块化的概念,与抽象是紧密相关的。
软件结构顶层的模块,控制了系统的主要功能并且影响全局;在软件结构底层的模块,完成对数据的一个具体处理,用自顶向下由抽象到具体的方式分配控制,简化了软件的设计和实现,提高了软件的可理解性和可测试性,并且使软件更容易维护。
逐步求精
逐步求精是人类解决复杂问题时采用的基本技术,也是许多软件工程技术(如规格说明技术、设计和实现技术、测试和集成技术)的基础。可以把逐步求精定义为:“为了能集中精力解决主要问题而尽量推迟对问题细节的考虑。”
可以把逐步求精看做是一项把一个时期内必须解决的种种问题按优先级排序的技术。逐步求精确保每个问题都将被解决,而且每个问题都在适当的时候解决。
逐步求精最初是由Niklaus Wirth提出的一种自顶向下的设计策略。按照这种设计策略,程序的体系结构是通过逐步精化过程细节的层次而开发出来的。
求精实际上是细化过程。
抽象与求精是一对互补的概念。抽象使得设计者能够说明过程和数据,同时却忽略低层细节。
信息隐藏
信息隐藏原理指出:应该这样设计和确定模块,使得一个模块内包含的信息(过程和数据)对于不需要这些信息的模块来说,是不能访问的。
实际上,应该隐藏的不是有关模块的一切信息,而是模块的实现细节,因此,有人主张把这条原理称为“细节隐藏”。但是,由于历史原因,人们已经习惯于把这条原理称为“信息隐藏”。
隐藏”意味着有效的模块化可以通过定义一组独立的模块来实现。这些模块彼此之间只交换那些为了完成软件功能而必须交换的信息。“抽象”有助于定义组成软件的过程实体,“隐藏”则定义并施加了对模块内部过程细节和模块使用的局部数据结构的访问限制
模块独立
模块独立的概念是模块化、抽象、逐步求精、信息隐藏等概念的直接结果,也是完成有效的模块设计的基本标准。
开发具有独立功能而且和其他模块之间没有过多的相互作用的模块,就可以做到模块独立。
模块的独立性很重要的原因主要有两条:①有效的模块化(即具有独立的模块)的软件比较容易开发出来,这是由于能够分割功能而且接口可以简化,当许多人分工合作开发同一个软件时,这个优点尤其重要;
②独立的模块比较容易测试和维护,这是因为相对说来,修改设计和程序需要的工作量比较小,错误传播范围小,需要扩充功能时能够“插入”模块。总之,模块独立是好设计的关键,而设计又是决定软件质量的关键环节。
模块的独立程度可以由两个定性标准来度量,这两个标准分别称为内聚和耦合。耦合衡量不同模块彼此间互相依赖(连接)的紧密程度;内聚衡量一个模块内部各个元素彼此结合的紧密程度。
耦合
耦合(Coupling)是对一个软件结构内不同模块之间互连程度的度量。耦合强弱取决于模块间接口的复杂程度,进入或访问一个模块的点,以及通过接口的数据。
在软件设计中应该追求尽可能松散耦合的系统。在这样的系统中可以研究、测试或维护任何一个模块,而不需要对系统的其他模块有很多了解。
如果两个模块彼此间通过参数交换信息,而且交换的信息仅仅是数据,那么这种耦合称为数据耦合。如果传递的信息中有控制信息(尽管有时这种控制信息以数据的形式出现),则这种耦合称为控制耦合。
数据耦合是低耦合。系统中至少必须存在这种耦合,因为只有当某些模块的输出数据作为另一些模块的输入数据时,系统才能完成有价值的功能。控制耦合是中等程度的耦合,它增加了系统的复杂程度。控制耦合往往是多余的,在把模块适当分解之后通常可以用数据耦合代替它。
公共环境耦合的复杂程度随耦合的模块个数而变化,当耦合的模块个数增加时复杂程度显著增加。如果只有两个模块有公共环境,那么这种耦合有下面两种可能。
①一个模块往公共环境送数据,另一个模块从公共环境取数据。这是数据耦合的一种形式,是比较松散的耦合。
②两个模块都既往公共环境送数据又从里面取数据,这种耦合比较紧密,介于数据耦合和控制耦合之间。
如果两个模块共享的数据很多,都通过参数传递可能很不方便,这时可以利用公共环境耦合。最高程度的耦合是内容耦合。如果出现下列情况之一,两个模块间就发生了内容耦合:
◆一个模块访问另一个模块的内部数据;
◆一个模块不通过正常入口而转到另一个模块的内部;
◆两个模块有一部分程序代码重叠(只可能出现在汇编程序中);
◆一个模块有多个入口(这意味着一个模块有几种功能)。
内聚
内聚(Cohesion)标志一个模块内各个元素彼此结合的紧密程度,它是信息隐藏和局部化概念的自然扩展。简单地说,理想内聚的模块只做一件事情。
内聚和耦合是密切相关的,模块内的高内聚往往意味着模块间的松耦合。
低内聚有如下几类:如果一个模块完成一组任务,这些任务彼此间即使有关系,关系也是很松散的,就叫做偶然内聚。如果一个模块完成的任务在逻辑上属于相同或相似的一类(如一个模块产生各种类型的全部输出),则称为逻辑内聚。如果一个模块包含的任务必须在同一段时间内执行(如模块完成各种初始化工作),则称为时间内聚。
偶然内聚的模块中,各种元素之间没有实质性。
联系逻辑内聚的模块中,不同功能混在一起,合用部分程序代码。
时间关系在一定程度上反映了程序的某些实质,所以时间内聚比逻辑内聚好一些:如果一个模块内的处理元素是相关的,而且必须以特定次序执行,则称为过程内聚;如果模块中所有元素都使用同一个输入数据和(或)产生同一个输出数据,则称为通信内聚。
高内聚也有两类:如果一个模块内的处理元素和同一个功能密切相关,而且这些处理必须顺序执行(通常一个处理元素的输出数据作为下一个处理元素的输入数据),则称为顺序内聚。如果模块内所有处理元素属于一个整体,完成一个单一的功能,则称为功能内聚。
功能内聚是最高程度的内聚。
耦合和内聚的概念是Constantine、Yourdon、Myers和Stevens等人提出来的。按照他们的观点,如果给上述7种内聚的优劣评分,将得到如下结果:
功能内聚10分 时间内聚3分
顺序内聚9分 逻辑内聚1分通信内聚7分偶然内聚0分
过程内聚5分
表示软件结构的图形工具
层次图和HIPO图
通常使用**层次图(hierarchy chart)描绘软件的层次结构。在层次图中一个矩形框代表一个模块,框间的连线表示调用关系(位于上方的矩形框所代表的模块调用位于下方的矩形框所代表的模块)。如图所示是层次图的一个例子,最顶层的矩形框代表正文加工系统的主控模块,它调用下层模块以完成正文加工的全部功能;第二层的每个模块控制完成正文加工的一个主要功能,如“编辑”模块通过调用它的下属模块,可以完成6种编辑功能中的任何一种
。在自顶向下逐步求精设计软件的过程中,使用层次图很方便。
HIPO图是美国IBM公司发明的“层次图加输入/处理/输出图”(Hierarchy Plus Input Process Output)**的英文缩写。为了使HIPO图具有可追踪性,在H图(即层次图)里除了顶层的方框之外,每个方框都加了编号。
和H图中的每个方框相对应,应该有一张IPO图描绘这个方框代表的模块的处理过程。
IPO图使用的基本符号既少又简单,因此很容易学会使用这种图形工具。它的基本形式是在左边的框中列出有关的输入数据,在中间的框内列出主要的处理,在右边的框内列出产生的输出数据。处理框中列出处理的次序暗示了执行的顺序,但是用这些基本符号还不足以精确描述执行处理的详细情况。在IPO图中还用类似向量符号的粗大箭头清楚地指出数据通信的情况。如图所示为一个主文件更新的例子,通过这个例子不难了解IPO图的用法。
结构图
Yourdon提出的结构图是进行软件结构设计的另一个有力工具。结构图和层次图类似,也是描绘软件结构的图形工具,图中一个方框代表一个模块,框内注明模块的名字或主要功能;方框之间的箭头(或直线)表示模块的调用关系。因为按照惯例总是图中位于上方的方框代表的模块调用下方的模块,即使不用箭头也不会产生二义性,为了简单起见,可以只用直线而不用箭头表示模块间的调用关系。
层次图和结构图并不严格表示模块的调用次序。
层次图和结构图并不指明什么时候调用下层模块。
通常用层次图作为描绘软件结构的文档。结构图作为文档并不很合适,因为图上包含的信息太多有时反而降低了清晰程度。利用IPO图或数据字典中的信息得到模块调用时传递的信息,从而由层次图导出结构图的过程,却可以作为检查设计正确性和评价模块独立性的好方法。
过程设计
过程设计应该在数据设计、体系结构设计和接口设计完成之后进行,它是详细设计阶段应该完成的主要任务。
过程设计的任务还不是具体地编写程序,而是要设计出程序的“蓝图”,以后程序员将根据这个蓝图写出实际的程序代码。
过程设计工具
程序流程图
程序流程图又称为程序框图,它是历史最悠久、使用最广泛的描述过程设计的方法,然而它也是用得最混乱的一种方法。
程序流程图的主要缺点
①程序流程图本质上不是逐步求精的好工具,它诱使程序员过早地考虑程序的控制流程,而不去考虑程序的全局结构。
②程序流程图中用箭头代表控制流,因此程序员不受任何约束,可以完全不顾结构程序设计的精神,随意转移控制。
③程序流程图不易表示数据结构。
盒图(N-S图)
特点:
①功能域(即一个特定控制结构的作用域)明确,可从盒图上一眼就看出来。②不可能任意转移控制。
③很容易确定局部和全程数据的作用域。
④很容易表现嵌套关系,也可以表示模块的层次结构。
PAD图
PAD是问题分析图(Problem Analysis Diagram)的英文缩写,它用二维树形结构的图来表示程序的控制流,将这种图翻译成程序代码比较容易。
PAD图主要优点
①使用表示结构化控制结构的PAD符号所设计出来的程序必然是结构化程序。②PAD图所描绘的程序结构十分清晰。③PAD图中竖线的总条数就是程序的层次数。④用PAD图表现程序逻辑,易读、易懂、易记。⑤容易将PAD图转换成高级语言源程序。⑥既可用于表示程序逻辑,也可用于描绘数据结构。⑦PAD图的符号支持自顶向下、逐步求精方法的使用。
判定表
当算法中包含多重嵌套的条件选择时,用程序流程图、盒图、PAD图或后面即将介绍的过程设计语言(PDL)都不易清楚地描述。然而判定表却能够清晰地表示复杂的条件组合与应做的动作之间的对应关系。
一张判定表由4部分组成,左上部列出所有条件,左下部是所有可能做的动作,右上部是表示各种条件组合的一个矩阵,右下部是和每种条件组合相对应的动作。判定表右半部的每一列实质上是一条规则,规定了与特定的条件组合相对应的动作。
判定树
判定树是判定表的变种,也能清晰地表示复杂的条件组合与应做的动作之间的对应关系。判定树的优点在于,它的形式简单到不需任何说明,一眼就可以看出其含义,因此易于掌握和使用。多年来判定树一直受到人们的重视,是一种比较常用的系统分析和设计的工具。
过程设计语言
过程设计语言(Program Design Language,PDL)也称为伪码,是一个笼统的名称,现在有许多种不同的过程设计语言在使用。它是用正文形式表示数据和处理过程的设计工具。
PDL具有严格的关键字外部语法,用于定义控制结构和数据结构;PDL表示实际操作和条件的内部语法通常又是灵活自由的,以便可以适应各种工程项目的需要。
PDL的特点
①关键字的固定语法,它提供了结构化控制结构、数据说明和模块化的特点。②自然语言的自由语法,它描述处理特点。③数据说明的手段。④模块定义和调用的技术,应该提供各种接口描述模式。
PDL的优点
①可以作为注释直接插在源程序中间。②可以使用普通的正文编辑程序或文字处理系统,很方便地完成PDL的书写和编辑工作。③已经有自动处理程序存在,而且可以自动由PDL生成程序代码。
面向数据结构的设计方法
面向数据结构的设计方法的最终目标是得出对程序处理过程的描述。这种设计方法并不明显地使用软件结构的概念,模块是设计过程的副产品,对于模块独立原理也没有给予应有的重视,因此,这种方法最适合于在详细设计阶段使用,即在完成了软件结构设计之后,可以使用面向数据结构的方法来设计每个模块的处理过程。
Jackson图
.1.顺序结构
顺序结构的数据由一个或多个数据元素组成,每个元素按确定次序出现一次。图4.31所示为顺序结构的Jackson图的一个例子。图中,A由B、C、D3个元素顺序组成(每个元素只出现一次,出现的次序依次是B、C和D)。
2.选择结构
选择结构的数据包含两个或多个数据元素,每次使用这个数据时按一定条件从这些数据元素中选择一个。图4.32所示为从3个条件中选一个结构的Jackson图。图中,根据条件A是B或C或D中的某一个(注意:在B、C和D的右上角有小圆圈做标记)。
.3.重复结构
重复结构的数据,根据使用时的条件由一个数据元素出现零次或多次构成。图4.33所示为重复结构的Jackson图。图中,A由B出现次(N20)组成(注意:在B的右上角有星号标记)。
Jackson图有下述优点:
◆便于表示层次结构,而且是对结构进行自顶向下分解的有力工具;
◆形象直观可读性好;
◆既能表示数据结构也能表示程序结构(因为结构程序设计也只使用上述3种基本结构)。
改进Jackson图
改进的Jackson图解决了下述问题
用这种图形工具表示选择或重复结构时,选择条件或循环结束条件不能直接在图上表示出来,影响了图的表达能力,也不易直接把图翻译成程序。
框间连线为斜线,不易在行式打印机上输出。
Jackson方法
.①分析并确定输入数据和输出数据的逻辑结构,并用Jackson图描绘这些数据结构。
.②找出输入数据结构和输出数据结构中有对应关系的数据单元。
.③用下述3条规则从描绘数据结构的Jackson图导出描绘程序结构的Jackson图。
◆为每对有对应关系的数据单元,按照它们在数据结构图中的层次在程序结构图的相应层次画一个处理框
◆根据输入数据结构中剩余的每个数据单元所处的层次,在程序结构图的相应层次分别为它们画上对应的处理框。
◆根据输出数据结构中剩余的每个数据单元所处的层次,在程序结构图的相应层次分别为它们画上对应的处理框。
④列出所有操作和条件(包括分支条件和循环结束条件),并且把它们分配到程序结构图的适当位置。
⑤用伪码表示程序。
示例
如果要求两个正整数的最小公倍数,请用程序流程图、N-S图和PAD图分别表示出求解该问题的算法。
程序流程图:
PAD图:
第五章 结构化实现
通常把编码和测试统称为实现。
所谓编码就是把软件设计翻译成计算机可以理解的形式一用某种程序设计语言书写的程序。
软件测试仍然是保证软件质量的关键步骤,它是对软件规格说明、设计和编码的最后复审。
通常在编写出每个模块之后就对它做必要的测试(称为单元测试),模块的编写者和测试者是同一个人,编码和单元测试属于软件生命周期的同一个阶段。在这个阶段结束之后,对软件系统还应该进行各种综合测试,这是软件生命周期中的另一个独立的阶段,通常由专门的测试人员承担这项工作。
选择程序设计语言
程序设计语言是人和计算机通信的基本工具,它的特点不可避免地会影响人思维和解决问题的方式,会影响人和计算机通信的方式和质量,也会影响其他人阅读和理解程序的难易程度。
使用汇编语言编码需要把软件设计翻译成机器操作的序列,由于这两种表示方法很不相同,因此汇编程序设计既困难又容易出差错。一般说来,高级语言的源程序语句和汇编代码指令之间有一句对多句的对应关系。
统计资料表明,程序员在相同时间内可以写出的高级语言语句数和汇编语言指令数大体相同;同时,高级语言一般都允许用户给程序变量和子程序赋予含义鲜明的名字,通过名字很容易把程序对象和它们所代表的实体联系起来;此外,高级语言使用的符号和概念更符合人的习惯,因此,用高级语言写的程序容易阅读,容易测试,容易调试,容易维护。
总的说来,高级语言明显优于汇编语言,因此,除了在很特殊的应用领域或者大型系统中执行时间非常关键的一小部分代码需要用汇编语言书写之外,其他程序应该一律用高级语言书写。
为了使程序容易测试和维护以减少生命周期的总成本,选用的高级语言应该有理想的模块化机制,以及可读性好的控制结构和数据结构;为了便于调试和提高软件可靠性,语言特点应该使编译程序能够尽可能多地发现程序中的错误;为了降低软件开发和维护的成本,选用的语言应该有良好的独立编译机制。
选择高级语言的标准
(1)系统用户的要求
(2)可以使用的编译程序
(3)可以得到的软件工具
(4)工程规模
(5)程序员的知识
(6)软件可移植性要求
(7)软件的应用领域
编码风格
.源程序代码的逻辑简明清晰、易读易懂是好程序的一个重要标准。为了做到这一点,应该遵循下述规则:
.1.程序内部的文档
所谓程序内部的文档包括恰当的标识符、适当的注解、程序的视觉组织等。选取含义鲜明的名字,使它能正确地提示程序对象所代表的实体,这对于帮助阅读者理解程序是很重要的。
2.数据说明
数据说明的次序应该标准化;当多个变量名在一个语句中说明时,应该按字母顺序排列这些变量;用注解说明用程序设计语言实现复杂数据结构的方法和特点。
.3.语句构造
◆不要为了节省空间而把多个语句写在同一行;
◆尽量避免复杂的条件测试;
◆尽量减少对“非”条件的测试;
◆避免大量使用循环嵌套和条件嵌套;
◆利用括号使逻辑表达式或算术表达式的运算次序清晰直观。
4.输入/输出
◆对所有输入数据都进行检验;
◆检查输入项重要组合的合法性;
◆保持输入格式简单;
◆使用数据结束标记,不要要求用户指定数据的数目;◆明确提示交互式输入的请求,详细说明可用的选择或边界数值;
◆当程序设计语言对格式有严格要求时,应保持输入格式一致;
◆设计良好的输出报表;
◆给所有输出数据加标志。
.5.效率
.(1)程序运行时间
◆写程序之前先简化算术的和逻辑的表达式;
◆仔细研究嵌套的循环,以确定是否有语句可以从内层往外移;
◆尽量避免使用多维数组;
◆尽量避免使用指针和复杂的表;
◆使用执行时间短的算术运算;
◆不要混合使用不同的数据类型;
◆尽量使用整数运算和布尔表达式。
.(2)存储器效率
。在大型计算机中必须考虑操作系统页式调度的特点,一般说来,使用能保持功能域的结构化控制结构,是提高效率的好方法;在微处理机中如果要求使用最少的存储单元,则应选用有紧缩存储器特性的编译程序,在非常必要时可以使用汇编语言。
.(3)输入/输出的效率
。硬件之间的通信效率是很复杂的问题,从写程序的角度看,有些简单的原则可以提高输入/输出的效率:
◆所有输入/输出都应该有缓冲,以减少用于通信的额外开销;
◆对二级存储器(如磁盘)应选用最简单的访问方法;◆二级存储器的输入/输出应该以信息组为单位进行;
◆如果“超高效的”输入/输出很难被人理解,则不应采用这种方法。
软件测试基础
表面看来,软件测试的目的与软件工程所有其他阶段的目的都相反。软件工程的其他阶段都是“建设性”的:软件工程师力图从抽象的概念出发,逐步设计出具体的软件系统,直到用一种适当的程序设计语言写出可以执行的程序代码。而在测试阶段测试人员努力设计出一系列测试方案,目的却是为了“破坏”已经建造好的软件系统一竭力证明程序中有错误,不能按照预定要求正确工作。
测试阶段的根本目标是尽可能多地发现并排除软件中潜藏的错误,最终把一个高质量的软件系统交给用户使用。
测试目标
.测试的规则
◆测试是为了发现程序中的错误而执行程序的过程;
◆好的测试方案是极可能发现迄今为止尚未发现的错误的测试方案;
◆成功的测试是发现了至今为止尚未发现的错误的测试
黑盒测试和白盒测试
黑盒测试:已经知道了产品应该具有的功能,可以通过测试来检验是否每个功能都能正常使用;
白盒测试:知道产品内部工作过程,可以通过测试来检验产品内部动作是否按照规格说明书的规定正常进行。
对于软件测试而言,黑盒测试法是把程序看成一个黑盒子,完全不考虑程序的内部结构和处理过程。
白盒测试法的前提是可以把程序看成装在一个透明的白盒子里,也就是完全了解程序的结构和处理过程。
单元测试
单元测试又称模块测试,是针对软件设计的最小单位程序模块(函数、类等)进行正确性检验的测试工作。
单元测试采用黑盒+白盒混合方式,采用黑盒测试为主为先,白盒测试为辅为后的策略。
1)用黑盒进行模块接口测试
2)用黑盒进行模块边界条件的测试
3)用白盒进行模块局部数据结构和算法的测试4)用白盒进行模块中独立路径的测试
5)模块中各条错误处理路径的测试
测试准则
①所有的测试都应该能追溯到用户需求。
②应该在测试开始之前的相当长时间,就制定出测试计划。
③把Pareto原理应用于软件测试。Pareto原理:测试发现的错误中的80%很可能是由程序中20%的模块造成的。
④测试应该从“小规模”开始,并逐步进行“大规模”测试。
⑤穷举测试是不可能的。
⑥为了达到最佳的测试效果,应该由独立的第三方来从事测试工作。
流图
在设计测试方案时,往往需要仔细分析程序的控制流。为了突出表示程序的控制流,可以使用流图(也称为程序图)。
流图仅仅描绘程序的控制流程,在流图中用圆表示节点,一个圆代表一条或多条语句,一个处理框序列和一个菱形判定框,可以映射成流图中的一个节点,箭头线称为边,它和程序流程图中的箭头线类似,代表控制流。在流图中一条边必须终止于一个节点,即使这个节点并不代表任何语句(实际上相当于一个空语句)。由边和节点围成的面积称为区域,当计算区域数时应该包括图外部未被围起来的那个区域。
逻辑覆盖
逻辑覆盖是设计白盒测试方案的一种技术。
不同的测试数据发现程序错误的能力差别很大,为了提高测试效率降低测试成本,应该选用高效的测试数据。
有选择地执行程序中某些最有代表性的通路是对穷尽测试的唯一可行的替代办法。
所谓逻辑覆盖是对一系列测试过程的总称,这组测试过程逐渐进行越来越完整的通路测试。
语句覆盖
为了暴露程序中的错误,至少每个语句应该执行一次。语句覆盖的含义是,选择足够多的测试数据,使被测程序中每个语句至少执行一次。
语句覆盖对程序的逻辑覆盖很少,是很弱的逻辑覆盖标准。
判定覆盖
判定覆盖又叫做分支覆盖,它的含义是,不仅每个语句必须至少执行一次,而且每个判定的每种可能的结果都应该至少执行一次,也就是每个判定的每个分支都至少执行一次。
判定覆盖比语句覆盖强,但是对程序逻辑的覆盖程度仍然不高。
条件覆盖
条件覆盖的含义是,不仅每个语句至少执行一次,而且使判定表达式中的每个条件都取到各种可能的结果条件覆盖通常比判定覆盖强,因为它使判定表达式中每个条件都取到了两个不同的结果,而判定覆盖却只关心整个判定表达式的值。
判定/条件覆盖
既然判定覆盖不一定包含条件覆盖,条件覆盖也不一定包含判定覆盖,自然会提出一种能同时满足这两种覆盖标准的逻辑覆盖,这就是判定/条件覆盖。它的含义是,选取足够多的测试数据,使得判定表达式中的每个条件都取到各种可能的值,而且每个判定表达式也都取到各种可能的结果。
条件组合覆盖
条件组合覆盖是更强的逻辑覆盖标准,它要求选取足够多的测试数据,使得每个判定表达式中条件的各种可能组合都至少出现一次。
示例
现有一段判定三角形类型的程序,可以根据输入的三角形的三边长来判定可构成的三角形是否为等腰三角形。请用等价类划分法来为此段代码设计测试用例。
(1)实现语句覆盖的测试方案如下:
覆盖THEN部分的语句,即使得条件(A>0)AND(B>0)和条件(C>A)OR(D<B)均为真
输入:A=1,B=1,C=0,D=1预期输出:X=2,Y=22)
覆盖ELSE部分的语句,即使得条件(A>0)AND(B>0)和条件(C>A)OR(D<B)均为假
输入:A=0,B=-1,C=0,D=1预期输出:X=1,Y=1
(2)实现路径覆盖的测试方案如下:
覆盖两个条件均为假的路径输入:A=-1,B=-2,C=-3,D=3预期输出:X=1,Y=02)
覆盖第一个条件为假第二个条件为真的路径输入:A=-1,B=-2,C=1,D=-3预期输出:X=1,Y=43)
覆盖第一个条件为真第二个条件为假的路径输入:A=1,B=1,C=0,D=2预期输出:X=2,Y=2
覆盖两个条件均为真的路径输入:A=-1,B=-2,C=2,D=1预期输出:X=3,Y=1
第六章 面向对象方法学导论
对象模型
对象模型表示静态的、结构化的系统的“数据”性质。在建立对象模型时,我们的目标是从客观世界中提炼出对具体应用有价值的概念。
使用统一建模语言(UML)所提供的类图来建立对象模型。在UML中术语“类”的实际含义是,“一个类及属于这个类的对象”。
类图描述类及类与类之间的静态关系。类图是一种静态模型,它是创建其他UML图的基础。
类图不仅定义软件系统中的类,描述类与类之间的关系,它还表示类的内部结构(类的属性和操作)。类图描述的是一种静态关系,它是从静态角度表示系统的。
1.定义类
UML中类的图形符号为长方形,用两条横线把长方形分成上、中、下3个区域(下面两个区域可省略),3个区域分别放类的名字、属性和操作
类名是一类对象的名字。命名是否恰当对系统的可理解性影响相当大,类命名的准则如下:
①使用标准术语;②使用具有确切含义的名词;③必要时用名词短语作名字。
2.定义类的属性
.选取类的属性时应考虑的原则:
①类的属性应能描述并区分该类的每个对象;②只有系统需要使用的那些特征才抽取出来作为类的属性;③系统建模的目的也影响属性的选取。
UML描述属性的语法格式如下
可见性属性名:
类
型
名
=
初
值
性
质
串
类型名=初值{性质串}
类型名=初值性质串
其中,属性名和类型名必须有,其他部分根据需要可有可无。
属性的可见性(即可访问性)通常分为3种:公有的(public)、私有的(private)和保护的(protected),分别用加号(+)、减号(一)和井号(#)表示。
.3.定义类的操作
.选取类的操作时应该遵守下述准则:
①操作围绕对类的属性数据所需要做的处理来设置,不设置与这些数据无关的操作;②只有系统需要使用的那些操作才抽取出来作为类的操作;③选取操作时应该充分考虑用户的需求。
.UML描述操作的语法格式如下
可
见
性
操
作
名
(
参
数
表
)
:
返
回
值
类
型
性
质
串
可见性 操作名(参数表):返回值类型{性质串}
可见性操作名(参数表):返回值类型性质串
其中,可见性和操作名是不可缺少的。
表示关系的符号
.1.关联
关联表示两个类的对象之间存在某种语义上的联系,如作家使用计算机,我们就认为在作家和计算机之间存在某种语义连接,因此,在类图中应该在作家类和计算机类之间建立关联关系。
(1)普通关联
普通关联是最常见的关联关系,只要在类与类之间存在连接关系就可以用普通关联表示。
通常,关联是双向的,可在一个方向上为关联起一个名字,在另一个方向上起另一个名字(也可不起名字)
为避免混淆,在名字前面(或后面)加一个表示关联方向的黑三角。
在表示关联的直线两端可以写上重数(multiplicity),它表示该类有多少个对象与对方的一个对象连接。重数的表示方法通常有:
0..1
表
示
0
到
1
个
对
象
0..1表示0到1个对象
0..1表示0到1个对象
0..
∗
或
∗
表
示
0
到
多
个
对
象
0..*或*表示0到多个对象
0..∗或∗表示0到多个对象
1
+
或
1..
∗
表
示
1
到
多
个
对
象
1+或1..*表示1到多个对象
1+或1..∗表示1到多个对象
1..15
表
示
1
到
15
个
对
象
1..15表示1到15个对象
1..15表示1到15个对象
3
表
示
3
个
对
象
3表示3个对象
3表示3个对象
(2)关联类
为了说明关联的性质可能需要一些附加信息,可以引入一个关联类来记录这些信息。关联中的每个连接与关联类的一个对象相联系,关联类通过一条虚线与关联连接。
(3)关联的角色
在任何关联中都会涉及参与此关联的对象所扮演的角色(即起的作用),在某些情况下显式标明角色名有助于别人理解类图。
(4)聚集
聚集也称为聚合,是关联的特例。聚集表示类与类之间的关系是整体与部分的关系。在陈述需求时使用的“
包含”、“组成”、“分为…….部分”等字句,往往意味着存在聚集关系。除了一般聚集之外,还有两种特殊的聚集关系,分别是共享聚集和组合聚集。
◆共享聚集
如果在聚集关系中处于部分方的对象可同时参与多个处于整体方对象的构成,则该聚集称为共享聚集。
◆组合聚集
如果部分类完全隶属于整体类,部分与整体共存,整体不存在了部分也会随之消失(或失去存在价值了)
,则该聚集称为组合聚集(简称为组成)。
泛化
UML中的泛化关系就是通常所说的继承关系,它是通用元素和具体元素之间的一种分类关系。
UML定义泛化关系的3条要求:
①具体元素应与通用元素完全一致,通用元素具有的属性、操作和关联,具体元素也都隐含地具有;②具体元素还应包含通用元素所没有的额外信息;③允许使用通用元素实例的地方,也应能够使用具体元素的实例。
(1)继承
需要特别说明的是,没有具体对象的类称为抽象类。抽象类通常作为父类,用于描述其他类(子类)的公共属性和行为。
(2)多重继承
多重继承指的是一个子类可以同时多次继承同一个上层基类,如图6.14中的水陆两用类继承了两次交通工具类。与多重继承相反的是不相交继承,即一个子类不能多次继承同一个基类
依赖和细化
.(1)依赖关系
依赖关系描述两个模型元素(类、用例等)之间的语义连接关系,其中一个模型元素是独立的,另一个模型元素不是独立的,它依赖于独立的模型元素,如果独立的模型元素改变了,将影响依赖于它的模型元素。
(2)细化关系
虽然在软件开发的不同阶段都使用类图,但是这些类图表示了不同层次的抽象。类图可以分为3个层次:概念层、说明层和实现层
◆概念层:在需求分析阶段使用概念层类图描述应用领域中的概念。实现它们的类可以从这些概念中得出,但是两者间并没有直接的映射关系。
◆说明层:在设计阶段使用说明层类图描述软件的接口部分(类与类之间的接口),而不是描述软件的实现部分。
◆实现层:在实现阶段使用实现层类图描述软件系统中类的实现。只有在实现层才真正有类的概念,并描述了软件的实现部分。
动态模型
动态模型表示瞬时的、行为化的系统的“控制”性质,它规定了对象模型中的对象的合法变化序列。
一旦建立起对象模型之后,就需要考察对象的动态行为。对一个对象来说,生命周期由许多阶段组成,在每个特定阶段中,都有适合该对象的一组运行规律和行为规则,用以规范该对象的行为。生命周期中的阶段也就是对象的状态。所谓状态,是对对象属性值的一种抽象。
状态有持续性,它占用一段时间间隔。
通常,用UL提供的状态图来描绘对象的状态、触发状态转换的事件以及对象的行为(对事件的响应)。
功能模型
功能模型表示变化的系统的“功能”性质,它指明了系统应该“做什么”,因此更直接地反映了用户对目标系统的需求。
通常,功能模型由一组数据流图组成。与对象模型和动态模型比较起来,数据流图并没有增加新的信息。
UML提供的用例图也是进行需求分析和建立功能模型的强有力工具。一幅用例图包含的模型元素有系统、行为者、用例以及用例之间的关系。在UML中把用用例图建立起来的系统功能模型称为用例模型,其描述的是外部行为者所理解的系统功能。
3种模型之间的关系
.3种模型之间的关系:
①针对每个类建立的动态模型,描述了类实例的生命周期或运行周期;②状态转换驱使行为发生,这些行为在数据流图中被映射成处理,在用例图中被映射成用例,它们同时与类图中的服务相对应;③功能模型中的用例对应于复杂对象提供的服务,简单的用例对应于更基本的对象提供的服务;④数据流图中的数据存储,以及数据的源点/终点,通常是对象模型中的对象;⑤数据流图中的数据流,往往是对象模型中对象的属性值,也可能是整个对象;⑥功能模型中的用例可能产生动态模型中的事件。⑦对象模型描述了数据流图中的数据流、数据存储以及数据源点/终点的结构。
UML
静态图(static diagram) | 类图不仅定义系统中的类,表示类与类之间的关系(如关联、依赖、泛化、细化等关系), 也表示类的内部结构(类的属性和操作)。类图描述的是一种静态关系,在系统的整个生命 期内都是有效的。 |
---|---|
用例图(Use Case) | 描述系统参与者与领域问题的功能 |
类图(Class) | 描述系统的逻辑结构,类、接口及它们的协作关系 |
对象图是类图的实例,它使用几乎与类图完全相同的图示符号。两者之间的差别在于, 对象图表示的是类的多个对象实例,而不是实际的类。由于对象有生命周期,因此对象图只能在系统的某个时间段内存在。 一般说来,对象图没有类图重要,它主要用来帮助对类图的理解, 也可用在协作图中,表示一组对象之间的动态协作关系。 | |
对象图(Object) | 描述类的实例在某时刻的关系立于 |
包图(Package) | 描述类的复用组织一分组 |
实现图 | 这类图提供关于系统实现方面的信息 |
---|---|
构件图(Component) | 描述代码构建的物理结构及各个构件之间的依赖关系。 构件可能是源代码、二进制文件或可执行文件。 使用构件图有助于分析和理解构件之间的相互影响。 |
部署图(Deployment diagram) | 描述系统运行环境的配置情况 部署图用来定义系统中软件和硬件的物理体系结构。 通常,部署图中显示实际的计算机和设备(用节点表示),以及各个节点之间的连接关系, 也可以显示连接的类型及构件之间的依赖关系。在节点内部显示可执行的构件和对象, 以清晰地表示出哪个软件单元运行在哪个节点上。 |
交互图(interactive diagram) | 这类图描述对象间的交互关系 |
---|---|
顺序图(Sequence) | 描述某些对象共同合作完成某项功能而按时间顺序进行的消息传递, 描述对象之间的交互过程。 |
协作图(Collaboration) | 描述某些对象共同合作完成某项功能的依赖关系 |
行为图 | 这类图描述系统的动态行为和组成系统的对象间的交互关系 |
---|---|
活动图(Activity) | 描述某个用例按事件流转所经历的的活动,即业务流程 |
状态图(State chart) | 描述某个业务流程按事件流转所经历的状态,即状态机 |
示例
一、面向对象的基本概念
1.对象与面向对象
对象(Object):即表示客观世界中的某个具体的事物。面向对象(Object Oriented):是人类的活动,是人类认知、观察客观事物的方法论。
2.面向对象的抽象与分类
3.类的封装与对象的整体性
4.关联性与交互性
客观事物都不是孤立存在的,万物之间相互依存、相互交流。关联性表达客观事物的社会性、共存性、组织性,是静态的结构描述。消息机制是对象的交互性,表示对象生存环境的依赖性。
5.继承性
对事物的分类本身就体现继承性。软件开发利用继承性可对Object更好地分类,软件结构更严谨,代码的复用性更强。
6.多态性
对象在不同的条件下,同样的行为会表现不同的效果,这就是Object的多态(polymorphism)。
面向对象编程语言提供抽象类、接口、重载等技术支持多态的实现。
参考资料
https://max.book118.com/html/2015/0321/13587488.shtm
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)