02333-软件工程
江苏省高等教育自学考试【02333-软件工程】学习笔记
| 课程代号 | 课程名称 | 教材代号 | 教材名称 | 作者 | 出版社 | 版次 |
|---|---|---|---|---|---|---|
| 02333 | 软件工程 | 023331 | 软件工程 | 瞿中、宋琦等 | 人民邮电出版社 | 2016 年 |
参考书籍:软件工程.epub
考试大纲:02333 软件工程(高纲4068).pdf
⭐软件工程的定义、特点、基本原理
软件的定义
软件,包括程序、相关数据及其说明文档,在计算机系统中与硬件相互依存。其中程序是按照事先设计的功能和性能要求执行的指令序列;数据是程序能正常操纵信息的数据结构;说明文档包含与程序开发、维护和使用过程中有关的各种图文数据。
⭐软件工程的定义
软件工程是一门研究如何用系统化、规范化、数量化等工程原则和方法进行软件开发和维护的学科,包括软件开发技术和软件项目管理。
软件的特点
首先,软件是一种抽象的逻辑实体;其次,软件是一种通过智力活动,把知识与技术转化为信息的一种产品,它是在研制、开发中被创造出来的;最后,在软件的运行和使用期间,没有硬件那样的机器磨损、老化问题,但是软件也存在退化问题,需要维护。
⭐软件工程的基本原理
- 用分阶段的生存周期计划严格管理。
- 坚持进行阶段评审。
- 实行严格的产品控制。
- 采纳现代程序设计技术。
- 能清楚地审查结果。
- 开发小组的人员应少而精。
- 承认不断改进软件工程实践的必要性。
软件的分类:系统软件,支撑软件,应用软件
系统软件是与计算机硬件紧密配合,使计算机的硬件与相关软件及数据进行协调、高效工作的系统,如操作系统、数据库管理系统、设备驱动程序以及通信处理程序等。
软件危机的定义和原因
软件危机的定义
软件危机指软件产品的质量低得通常不能接受,并且不能满足交付日期和预算限制。
软件危机产生的原因
- 忽视软件开发前期的需求分析。
- 开发过程没有统一的、规范的方法论的指导,文件资料不齐全,忽视人与人的交流。
- 忽视测试阶段的工作,提交用户的软件质量差。
- 忽视软件的维护。
- 缺少规范而盲目编写程序。
软件工程的生命周期
软件从开始计划起,到废弃不用止,称为软件生存周期。
通常将软件生存周期分为计划、开发、运行、维护 4 个时期。
软件的生存周期:
- 制订计划
- 需求分析和定义。需求分析包括需求的获取、分析、规格说明、变更、验证、管理等一系列需求工程。软件人员与用户共同讨论决定,哪些需求是可以满足的,并且加以确切描述。然后编写出软件需求说明书或系统功能说明书,以及初步的系统用户手册,提交管理机构。
- 软件设计
- 程序编写
- 软件测试
- 运行与维护。软件产品开发完成投入使用后可能运行若干年。在运行过程中可能因为各方面原因需要进行修改,如硬件变更、操作系统换代、平台移植等问题都可能需要对软件进行维护。
软件开发过程模型
瀑布模型
瀑布模型将软件开发过程划分为需求定义与分析、软件设计、软件实现、软件测试和运行维护等一系列基本活动,并且规定这些活动自上而下、相互衔接的固定次序。该模型支持结构化的设计方法,但它是一种理想的线性开发模式,缺乏灵活性,无法解决软件需求不明确或不准确的问题。
瀑布模型的优点:
- 严格规范软件开发过程,克服了非结构化的编码和修改过程的缺点。
- 强调文档的作用,要求每个阶段都要仔细验证。
瀑布模型的缺点:
- 各个阶段的划分完全固定,阶段之间产生大量的文档,极大地增加了工作量。
- 由于开发模型是线性的,用户只有等到整个过程的后期才能见到开发成果,中间提出的变更要求很难响应。
- 早期的错误可能要等到开发后期的测试阶段才能发现,进而带来严重的后果。
快速原型模型
快速原型模型需要迅速建造一个可以运行的软件原型,以便理解和澄清问题,使开发人员与用户达成共识,最终在确定用户需求基础上开发用户满意的软件产品。
快速原型模型的优点:克服了瀑布模型的缺点,减少由于软件需求不明确带来的开发风险,可以快速构建一个软件原型,用于进行用户需求分析,帮助用户更好地描述系统要实现的功能。
快速原型模型的缺点:
- 所选用的开发技术和工具不一定符合主流的发展。
- 快速建立起来的系统结构加上连续的修改可能会导致产品质量低下。
螺旋模型
螺旋模型将瀑布模型和快速原型模型结合起来,它将软件过程划分为若干个开发回线,每一个回线表示开发过程的一个阶段,如此反复形成了螺旋上升的过程。螺旋模型适合于大型软件的开发,它吸收了软件工程“演化”的概念。
螺旋模型的优点:
- 以风险驱动开发过程,强调可选方案和约束条件从而支持软件的重用。
- 关注于早期错误的消除,将软件质量作为特殊目标融入产品开发之中。
螺旋模型的缺点:
- 要求许多客户接受和相信风险分析并做出相关反应是不容易的,往往适应于内部的大规模软件开发。
- 需要软件开发人员具备风险分析和评估的经验,否则将会带来更大的风险。
增量模型
增量模型是一种非整体开发的模型。在增量模型中,软件被作为一系列的增量构件来设计、实现、集成和测试,从而适应用户逐步细化需求的形成过程。该模型有较大的灵活性,适合于软件需求不明确、设计方案有一定风险的软件项目。
增量模型的优点:
- 较好地适应需求的变化,用户可以不断看到所开发软件的可运行中间版本。
- 重要功能被首先交付,从而使其得到最多的测试。
增量模型的缺点:
- 各个构件是逐渐并入已有的软件体系结构中,要求软件具备开放式的体系结构。
- 容易退化为边做边改的方式,从而使软件过程的控制失去整体性。
喷泉模型
喷泉模型是一种以用户需求为动力,以对象作为驱动的模型,适合于面向对象的开发方法。
喷泉模型的优点:
- 具有更多的增量和迭代性质,生存周期的各个阶段可以相互重叠和多次反复。
- 在项目的整个生存周期中还可以嵌入子生存周期。
- 采用面向对象方法实现的这种在概念上和表示方法上的一致性,保证了开发活动间的无缝过渡。
喷泉模型的缺点:面向对象范例要求经常对开发活动进行迭代,这就有可能造成在使用喷泉模型的开发过程中过于无序。
统一过程模型
Rational统一过程(Rational Unified Process,RUP),RUP把一个项目分为 4 个不同的阶段。
- 初始阶段。包括用户沟通和计划活动两个方面,强调定义和细化用例,并将其作为主要模型。
- 细化阶段。包括用户沟通和建模活动,重点是创建分析和设计模型,强调类的定义和体系结构的表示。
- 构造阶段。将设计转化为实现,并进行集成和测试。
- 交付阶段。将本次迭代的可用产品移交给用户。
统一过程模型的优点:
- 任何功能开发后就进入测试过程,及早进行验证。
- 早期风险识别,采取预防措施。
统一过程模型的缺点:
- 需求必须在开始之前完全弄清楚,是否有可能在架构上出现错误。
- 必须有严格的过程管理,以免使过程退化为原始的“试—错—改”模式。
- 如果不加控制地让用户过早接触没有测试完全、版本不稳定的产品,可能为用户和开发团队都带来负面的影响。
极限编程过程
极限编程(Extreme Programming,XP)是敏捷软件开发方法的代表。
极限编程过程模型的优点:
- 重视客户的参与、团队合作和沟通。
- 制订计划前做出合理预测,让编程人员参与软件功能的管理。
- 重视质量、设计简单、高频率的重新设计和重构、高频率及全面的测试。
- 递增开发和连续的过程评估。
- 对过去的工作持续不断地检查。
极限编程过程模型的缺点:
- 以代码为中心,忽略了设计。
- 缺乏设计文档,局限于小规模项目。
- 对已完成工作的检查步骤缺乏清晰的结构,质量保证依赖于测试并缺乏质量规划。
- 没有提供数据的收集和使用的指导。
- 开发过程不详细,全新的管理手法带来的认同度问题。
- 缺乏过渡时的必要支持。
敏捷过程
敏捷过程由 4 个声明组成。
- 个体和交互胜过过程和工具。
- 可以工作的软件胜过面面俱到的文档。
- 客户合作胜过合同谈判。
- 响应变化胜过遵循计划。
个人理解的总结(非教材):
| 模型名称 | 典型用途(优点) | 一句话理解 |
|---|---|---|
| 瀑布模型 | 需求明确的项目。 很难,代价大。开发小组非常熟悉项目的需求。 | 一步接一步,顺序执行 |
| 快速原型 | 需求不清楚。试的阶段可以随便改。 | 先做假的试,满意了再做真的 |
| 螺旋模型 | 高风险大项目。每圈都可以调整方向。 客户不能确定系统需求的项目。 | 每转一圈加一层,同时检查风险。每圈结束看到新版本。 |
| 增量模型 | 想尽快用上核心功能。分批交付、逐步完善、每块都是成品。每块加完可以改之前的。 | 先搭骨架,再一块一块加功能 |
| 喷泉模型 | 面向对象项目。非常自由 | 各阶段重叠、同时进行、随意跳转 |
软件开发方法,软件开发工具
软件开发方法
- 结构化方法
- 面向数据结构的开发方法
- 面向对象的方法
- 视觉化开发方法
结构化方法
1978年,爱德华·尤顿和拉里·康斯坦丁提出了结构化方法。
结构化方法的基本要点是自顶向下、逐步求精、模块化设计。
面向数据结构的开发方法
面向数据结构开发方法的最终目标是得出对程序处理的描述。这种方法的总指导思想是自顶而下、逐步求精、单入口、单出口,基本原则是抽象和功能分解。因此,这种方法最适合于详细设计阶段使用,在完成软件结构设计之后,可以使用面向数据结构的方法来设计每个模块的处理过程。
视觉化开发方法
可视化开发工具应提供两大类服务。一类是生成图形用户接口及相关的消息响应函数;另一类服务是为各种具体的子应用的各个常规执行步骤提供规范窗口,它包括对话框、菜单、列表框、组合框、按钮和编辑框等,以供用户挑选,开发工具还应为所有的选择(事件)提供消息响应函数。
软件工程的最新发展动向
- 软件工程合理的开发治理
- 软件工程全球化协作发展
- 软件工程模块化
- 软件工程开放式计算
⭐可行性研究的任务、内容和步骤
⭐可行性研究的任务
可行性研究是在明确了问题定义的基础上,对软件项目从技术、经济等各个方面进行研究与分析,得出项目是否具有可行性结论的过程。
可行性研究的任务是用用最小的代价、在尽可能短的时间内确定问题是否能够解决。
但必须注意的是,可行性研究的根本目的并不是解决问题,而是确定问题是否值得去解决,也就是判断系统原定的目标和规模是否能实现,软件使用所带来的效益是否值得用户去投资开发。因此,可行性研究实质上是要进行一次压缩和简化系统分析、设计的过程,是在较高层次上以较抽象的方式进行的系统分析和设计
最后可将可行性研究报告提交给项目管理部门,由项目管理人员对可行性研究报告进行评审。
⭐可行性研究的基本内容
经济可行性、技术可行性、法律可行性
在技术可行性研究过程中,系统分析员应采集软件系统涉及的各种信息(包括系统性能、可靠性、可维护性和可生产性方面),分析实现系统功能和性能所需要的各种设备、技术、方法和过程,并且需要分析软件开发在技术方面可能面临的风险,以及技术问题对开发成本的影响等。
⭐可行性研究的步骤
- 系统规模和目标的复查
- 认真研究现有系统
- 导出新系统的高层逻辑模型
- 重新定义问题
- 导出和评价供选择的方案
- 推荐方案和行动方针
- 草拟开发计划
- 提交文档
需求规格说明书的写法
⭐问题定义
问题定义阶段在说明软件项目的最基本情况下形成问题定义报告。在此阶段,开发者与用户一起,讨论待开发软件项目的类型(应用软件还是系统软件、通用软件还是专用软件)、将要开发软件项目的性质(主要是区分软件是新开发软件还是原有软件系统的升级)、待开发软件项目的目标(软件主要的使用功能)、待开发软件的大致规模以及开发软件项目的负责人等问题,并且用简洁、明确的语言将上述内容写进问题报告,最后双方对报告签字认可。
问题定义阶段的持续时间一般很短,形成的报告文本也相对比较简单。问题定义报告主要有如下内容。
- 待开发项目名称。
- 软件项目的使用单位和部门。
- 软件项目的开发单位。
- 软件项目的用途和目标。
- 软件项目的类型和规模。
- 软件项目开发的开始时间以及大致交付使用的时间。
- 软件项目开发可能投入的经费。
- 软件项目的使用单位与开发单位双方名称全称及其盖章。
- 软件项目的使用单位与开发单位双方的负责人签字。
- 问题定义报告的形成时间。
系统流程图的符号
系统流程图的符号:

系统流程图的画法
系统流程图的图形元素比较简单,也较容易理解。一个图形符号代表一种物理部件,这些部件可以是程序、文件、数据库、表格、人工过程等。
在系统流程图的绘制过程中,要注意以下 3 个方面:
- 物理部件的名称应写在图形内,用以说明该部件的含义。
- 系统流程图中不应该出现信息加工控制的符号。
- 用以表示信息流的箭头符号,无须标注名称。
面对复杂的系统,一个比较好的方法是分层次描绘。首先用一张高层次的系统流程图描绘系统总体概括,表明系统的关键功能,然后分别把每个关键功能扩展到适当的详细程度,画在单独的一页纸上。
软件计划的制定
- 确定软件计划
- 复审软件计划
- 开发方案的选择
确定软件计划
确定软件计划就是要用书面文件的形式,把开发过程中所涉及的每个问题,如各项工作的负责人员、成本、进度及所需要的软硬件条件等做出合理地估算。
- 资源需求分析:开发系统、目标硬件系统
- 软件开发进度安排
- 制订项目开发计划
制订软件开发进度计划时应该考虑如下问题:
- 开发进度与开发人员数量的关系 。与其他科学活动不一样,软件开发的进度不可能靠不断增加人数来保证。因为人员的增加就意味着增加了开发人员之间信息交流的复杂性。
- 开发进度与人员配备
- 软件进度计划
项目开发计划主要内容:项目概述、实施计划、人员配置、交付期限。
复审软件计划
管理方面:
- 计划描述的系统是否符合用户的需要。
- 计划中对系统相关资源的描述是否合理有效。
- 开发成本与开发进度要求是否合理。
技术方面:
- 系统的功能复杂性是否与开发风险、成本、进度相一致。
- 是否为后续的开发提供足够的依据和空间。
- 规格说明中关于系统性能、可维护性等要求是否恰当。
开发方案的选择
通常系统工程师将一个复杂系统分解为若干个相对简单的子系统。然后再精确地定义子系统,以及给出各子系统之间的关系。每种方案对各种因素,如成本、时间、人员、技术、设备等都有一定的要求。而每一种方案开发出来的系统在功能和性能方面都会存在很大的差异。系统开发各阶段所用成本分配方案的不同也会对系统的功能和性能产生相当大的影响。
成本估算方法和效益分析方法
成本估算
- 基于代码行的成本估算方法
- 基于任务分解的成本估算方法
- 经验统计估算模型
基于代码行的成本估算方法
- 源代码行
- 工作量
- 软件生产率 。每行代码的平均成本取决于软件复杂程度和开发人员的工资水平,如果用软件生产率相乘,则得预期开发期,进行功能或者任务分解,则可以估计开发进度。
基于任务分解的成本估算方法
典型办法是根据生存周期得到的瀑布模型,对开发工作进行任务分解,分别估算每个任务的成本,然后累加得到总成本。每个任务的成本估算通常只估算工作量(通常以PM为单位),如果软件系统庞大,可以分子系统独立开发,则应该对每个子系统按照开发阶段分别估算。
经验统计估算模型
- 参数方程
- 动态多变量参数模型
- COCOMO模型。构造性成本模型(Constructive Cost Model,COCOMO)是一种结构成本组合模型。该模型将软件开发方式分为有机方式、嵌入方式和半分离方式3种
成本/效益分析的方法
- 货币的时间价值
- 纯收入
- 投资回收期
- 投资回收率
需求分析的概念、目标和任务
需求分析的概念
需求分析是在可行性研究的基础上进行的更细致的分析工作,是软件定义时期的最后一次对软件目标及范围的求精和细化。通过可行性研究和分析,充分了解用户对软件系统的要求,把用户要求表达出来,解决“软件系统必须做什么”的问题。
需求分析的层次:业务需求、用户需求、功能需求。
需求分析的目标
- 理清数据流或数据结构。
- 通过标识接口细节,深入描述功能,确定设计约束和软件有效性要求。
- 构造一个完全、精致的目标系统逻辑模型。
需求分析的任务
需求分析的基本任务是准确回答“系统必须做什么”的问题。
需求分析的具体任务如下:
- 确定对系统的综合要求。
- 分析系统的数据要求。
- 导出目标系统的详细逻辑模型。
- 修订系统开发计划。
- 编写软件需求规格说明书,并提交审查。
需求陈述
需求陈述应该阐明“做什么”,而不是“怎样做”。
系统分析员必须把需求与实现策略区分开,后者是一类伪需求,分析员至少应该认识到它们不是问题域的本质性质。
需求分析的原则、过程和方法
需求分析的原则
- 分析人员要使用符合用户语言习惯的表达,尽量多地了解用户的业务及目标,以期获得用户所需要的功能和质量的系统。
- 分析人员必须编写软件需求报告,要求得到需求工作结果的解释说明。
- 开发人员要尊重用户的意见,要对需求及产品实施提出建议和解决方案,同时分析人员也要尊重开发人员的需求可行性及成本评估。
- 用各种方法特别是容易理解和交流的图形来准确而详细地说明需求,描述产品使用特性,清楚地说明并完善需求。为提高生产效率,须划分需求的优先级。
- 允许重用已有的软件组件,需求变更要立即联系,特别是要求对变更的部分提供真实可靠的评估,遵照开发小组处理需求变更的过程。
- 及时做出决定。
- 评审需求文档和原型。
需求分析的过程
软件需求分析的工作过程是依据在软件计划阶段确定的软件作用范围,进 一步对目标对象和环境做细致深入的调查,了解现实的各种可能解法,加以分析评价,做出抉择,配置各个软件元素,建立一个目标系统的逻辑模型并写出软件规格说明。需求分析过程实际上是一个调查研究、分析综合过程,是一个抽象思维、逻辑推理过程。它要求分析者能够从冲突和混淆的原始资料中吸收恰当的事实,从复杂的大量的事实中抽象出一组概念, 并把它们组织成一个逻辑整体。因此,需求分析是一项复杂的综合性技术,需求分析过程是一种高水平的创造性劳动。
需求评审规则
完整性、正确性、可行性、必要性、划分优先级、无二义性、可验证性
传统软件建模方法
- 分析建模
- 数据模型
- 功能模型
- 行为模型
- 数据字典
分析建模
分析建立的模型必须达到下述的 3 个基本目标:
- 描述用户需求
- 为软件设计工作奠定基础
- 定义一组需求,一旦开发出软件产品之后,就可以以这组需求为标准来验收。
数据模型
最常用的概念数据模型是实体—联系方法。也就是通常说的用E-R图来描述。E-R模型中有 3 种要素:实体、属性、联系。实体-矩形、属性-椭圆形、关系-菱形。
- 实体:实体即是对软件必须理解的复合信息的抽象。
- 属性:属性定义了数据对象的性质。
- 联系:客观世界中的事物彼此间往往是有联系的。
- 符号:实体关系图(Entity-Relationship Diagram,ERD)作为数据建模的基础,描述数据对象及其关系。
联系可分为以下 3 种类型:
- 一对一联系(1:1)
- 一对多联系(1:N)
- 多对多联系(M:N)
功能模型
数据流图是描述系统逻辑模型的图形工具,数据流图是一个逻辑模型而不是物理模型,是描述系统功能的模型。
处理一般表示一个功能或一组功能,静态数据用数据存储表示,常用来描述数据库在数据流图中的表示,可以用来表示一个文件(一个文件可看作许多数据的组合)或者多个文件的组合,总之数据存储用来表示静态的一些数据的集合体。
数据字典
数据字典是对数据流图中数据的描述,由数据流、文件、数据项(指不再分解的数据单位)、加工 4 个类型条目组成。
⭐DFD图的画法
总体设计工具、总体设计说明书的编写规范
总体设计中的工具:系统流程图、HIPO图
软件体系结构的概念、现状和描述方法。
软件体系结构
软件体系结构指软件的整体结构和这种结构所提供的系统在概念上的整体性的方式。
软件体系结构的现状
- 客户机/服务器体系结构
- 分布式对象体系结构
- 三层C/S软件体系结构
软件体系结构的描述方法
软件体系结构描述语言(Architecture Description Language,ADL)
ADL的 3 个基本元素如下:
- 构件
- 连接件
- 体系结构配置
体系结构配置
- 系统的层次结构与块状结构,块状结构把系统垂直地分解成若干个相对独立的低耦合的子系统,一个子系统相当于一块,每块提供一种类型的服务,所以称为块状组织形式。层次结构把软件系统组织成一个层次形式的结构,上层在下层的基础上建立,下层为上层提供必要的服务。第一层,最高层是应用系统层,可包括多个应用系统。每一个应用系统向用户提供一组服务,系统之间可通过接口实现交互操作。 第二层,次高层是构件系统层,同理,也可以包括多个构件系统。 第三层,中间层为构件系统提供实用软件,这些实用软件通常不依赖平台。 第四层,系统软件层,例如,一般操作系统、网络操作系统、硬件接口等。 第五层,硬件系统层,也称为硬件平台。
- 基于构件系统的分层体系及引用关系,为了确保分层系统的管理,规定在一个系统内,高层可以重用低层的构件,低层不能重用高层的构件。
- 创建者、支持者与重用者,为了实施软件的重用,软件开发单位往往要考虑 3 个子系统,即创建者子系统、支持者子系统与重用者子系统。
总体设计的任务、过程
总体设计的任务
总体设计也称为概要设计,其主要的任务是根据用户需求分析阶段得到的目标系统的物理模型,确定一个合理的软件系统的体系结构。
总体设计的过程
- 设想供选择的方案
- 选取合理的方案
- 推荐最佳方案
- 功能分解。对数据流图进一步细化,进行功能分解。分析员结合算法描述仔细分析数据流图中的每个处理,如果一个处理的功能过分复杂,必须把它的功能适当地分解成一系列比较简单的功能。
- 设计软件结构
- 数据库的设计
- 制订测试计划
- 书写文档
- 审查和复审
正式的记录总体设计结果的文档通常包括以下 5 种,
- 系统说明
- 用户手册
- 测试计划
- 详细的实现计划
- 数据库设计结果
⭐模块设计、结构设计原理
模块设计:模块化、抽象、信息隐蔽、模块独立性、模块的耦合、模块的内聚
模块化
模块是数据说明、可执行语句等程序对象的集合,包含 4 种属性。(1)输入/输出(2)逻辑功能(3)运行程序(4)内部数据
理想模块(黑箱模块)的特点如下:
- 每个理想模块只解决一个问题。
- 每个理想模块的功能都应该明确,使人容易理解。
- 理想模块之间的连接关系简单,具有独立性。
- 由理想模块构成的系统,易于理解、编程、测试、修改和维护。
抽象
抽象是一种思维方法,这种方法在认识事物时,忽略事物的细节,通过事物本质的共同特性来认识事物。
信息隐蔽
信息隐蔽是指在设计和确定模块时,使得一个模块内包含的信息(过程或数据),对于不需要这些信息的其他模块来说是不能访问的,或者说是“不可见”的。
模块的耦合
耦合是对一个软件结构内各个模块之间互连程度的度量。
| 名称 | 说明 | 记忆口诀 | 好坏 |
|---|---|---|---|
| 非直接耦合 | 如果两个模块中的每一个都能独立地工作而不需要另一个模块的存在,那么它们彼此完全独立,这表明模块间无任何连接,耦合程度最低。 | 谁也不理谁。通过中间人。 | ⭐ 最好 |
| 数据耦合 | 如果两个模块彼此间通过参数交换信息,而且交换的信息仅仅是数据,那么这种耦合称为数据耦合。 | 只传必要数据(最佳实践)。传简单参数。 | ⭐⭐ 很好 |
| 标记耦合 | 如果一组模块通过参数表传递记录信息,即这组模块共享了这个记录,这就是标记耦合。 | 你明明只需要一把剪刀,我却把整个工具箱递给你。 | ⭐⭐⭐ 一般 |
| 控制耦合 | 如果传递的信息中有控制信息,则这种耦合称为控制耦合。 | 传开关,指挥别人。传控制标志。 | ⭐⭐⭐⭐ 较差 |
| 外部耦合 | 一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该变量的信息,则称之为外部耦合。 | 共用外部设备/协议 | ⭐⭐⭐⭐⭐ 差 |
| 公共环境耦合 | 当两个或多个模块通过一个公共数据环境相互作用时,它们之间的耦合称为公共环境耦合。 | 共用全局变量 | ⭐⭐⭐⭐⭐⭐ 很差 |
| 内容耦合 | 如果一个模块和另一个模块的内部属性(即运行程序和内部数据)有关,则称为内容耦合。 | 钻进别人肚子里搞破坏(最差) | ⭐⭐⭐⭐⭐⭐⭐ 最差 |
耦合性从低到高(模块独立性从强到弱):非直接耦合、数据耦合、标记耦合、控制耦合、外部耦合、公共环境耦合、内容耦合
应该采取的原则是尽量使用数据耦合,少用控制耦合,限制公共环境耦合的范围,完全不用内容耦合。
模块的内聚
| 名称 | 说明 | 记忆口诀 | 好坏 |
|---|---|---|---|
| 功能内聚 | 如果一个模块内部的各组成部分的处理动作全都为执行同一个功能而存在,并且只执行一个功能,则称为功能内聚。 | 只做一件事,且这件事完整 | ⭐ 最好 |
| 信息内聚 | 信息内聚模块具有多种功能,能完成多种任务。各个功能都在同一数据结构上操作,每一项功能只有一个唯一的入口点。 | 共享数据(各干各的,但用同一份数据) | ⭐⭐ 好 |
| 通信内聚 | 如果模块中所有元素都使用同一个输入数据和(或)产生同一个输出数据,则称为通信内聚。 | 共享数据源,但不传递。 模块A包含对学生信息表的添加、删除、修改三种操作 | ⭐⭐⭐ 中 |
| 过程内聚 | 如果一个模块内部的各个组成部分的处理动作各不相同,彼此也没有联系,但它们都受同一个控制流支配,并由这个控制流决定它们的执行次序,则为过程内聚。 | 按顺序执行,但数据不一定相关 | ⭐⭐⭐⭐ 较差 |
| 时间内聚 | 如果一个模块内的各组成部分的处理动作和时间相关,则称为时间内聚。 | 同一时间执行(如初始化) | ⭐⭐⭐⭐⭐ 差 |
| 逻辑内聚 | 如果一个模块内部各组成部分的处理动作在逻辑上相似,但功能都彼此不同或无关,则称为逻辑内聚。 | 通过参数选择执行哪一段 | ⭐⭐⭐⭐⭐⭐ 很差 |
| 偶然内聚 | 如果一个模块完成一组任务,各个任务之间没有实质性联系,即使这些任务彼此间有关系,其关系也是很松散的,就叫作偶然内聚。 | 毫无关系,硬凑在一起。集中于单个模块的功能和结构检验。 | ⭐⭐⭐⭐⭐⭐⭐ 最差 |
内聚性从高到低(模块独立性从强到弱):功能内聚、信息内聚、通信内聚、过程内聚、时间内聚、逻辑内聚、偶然内聚
记忆口诀:“功信通过时逻偶”(功能、信息、通信、过程、时间、逻辑、偶然)
结构设计
软件结构也可以描述为管道和过滤器、面向对象、隐式请求、层次化、过程控制等形式。
⭐设计准则
总体设计准则
- 多样化设计
- 设计对于分析模型应该是可跟踪的 。因为设计模型中的一个软件元素可能会涉及多个需求,也可能一个需求由多个软件元素实现。为了使得设计出的软件满足需求,要求设计模型一定要具有可回溯性。
- 设计不应该从头做起 。软件系统是使用一系列设计模式构造的,很多模式可能在以前就遇到过,这些模式通常被称为可复用设计构件。应该尽可能使用已有的设计构件,减少设计的工作量,并且也可以保证设计的质量。
- 软件设计应该尽可能缩短软件和现实世界的距离
- 设计应该表现出一致性和规范性
- 设计的易修改性
- 容错性设计
- 设计的粒度要适当
- 在设计时就要开始评估软件的质量
- 要复审设计,减少设计引入的错误
ohter
影响软件设计的因素:
- 共同设计
- 用户界面 。设计用户界面的目标就是帮助用户方便、快捷地获得需要的信息。用户界面设计是设计中的关键,用户界面包含各种各样的技术——超文本、声音、二维画面、视频和虚拟世界。为了设计出舒适、高效的界面,设计者必须考虑两个主要问题:文化和喜好。
- 文化问题
- 并发性 。处理并发系统必须有更复杂的设计,并发系统中一个最大的问题就是要保证共享数据的一致性。处理这类系统通常采用同步和互斥技术,同步是协调多个并发活动的技术,互斥是保证共享数据的一致性的技术。
⭐面向数据流的设计方法
- 变换流
- 事务流
- 变换分析
- 事务分析
- 软件模块结构的改进
变换流
信息沿输入通路进入系统,同时由外部形式变换成内部形式,进入系统的信息通过变换中心,经过加工处理后再沿输出通路变换成外部形式离开软件系统。当数据流具有这些特征时,这种信息流被称为 变换流。
事务流
数据沿输入通路到达一个处理T,这个处理根据输入数据的类型在若干个动作序列中选出一个来执行。这种“以事务为中心的”的数据流,称为“事务流”。
变换分析
- 划分边界,区分系统的输入部分、变换中心和输出部分。
- 完成第一级分解,设计系统的上层模块。
- 完成第二级分解,设计输入部分、变换中心和输出部分的中、下层模块。
事务分析
事务分析设计方法也是从分析数据流图出发,通过自顶向下的逐步分解来建立系统软件结构。
- 划分边界,明确数据流图中的接收路径、事务中心和加工路径。
- 建立事务型结构的上层模块。
- 分解、细化接收路径和加工路径,得到事务型结构的下层模块。
模块结构设计方法
设计软件模块的结构就是要把软件模块组成良好的层次系统,描述各模块之间的关系。顶层模块调用它的下层模块以实现程序的完整功能,每个下层模块再调用更下层的模块,最下层的模块完成最具体的功能。
软件设计方法主要有面向数据流的设计方法和面向数据结构的设计方法,在总体设计阶段,主要采用面向数据流的结构化设计方法,通过把不够详细的数据流图进一步细化至适当层次,从而映射出软件结构,用层次图或软件结构图来描述,可以直接从数据流图映射出软件结构。
数据存储设计方法
常用的数据存储管理有以下 3 种方式:
- 数据文件
- 关系数据库
- 面向对象数据库
数据文件
数据文件是由操作系统提供的存储形式,应用系统将数据按字节顺序存储,并定义如何以及何时检索数据。显然,文件形式给应用系统带来更多的灵活性,但是应用系统需要自己处理并发访问和数据恢复等问题。
关系数据库
在关系数据库中,数据是以表的形式存储在预先定义好的称为Schema的类型中。表的每一列表示一个属性,每一行将一个数据项表示成一个属性值的元组。关系数据库是一种成熟的技术,使用费用较高而且会产生性能上的瓶颈。
面向对象数据库
与关系数据库不同的是,面向对象数据库将对象和关系作为数据一起存储。它提供了继承和抽象数据类型,但其查询要比关系数据库慢。
⭐MVC框架的模式和实现
MVC模式
模型—视图—控制器(Model-View-Controller,MVC),是一种用来使用户界面层和系统的其他部分分离的结构化模式。
MVC的实现
- 分析应用问题,对系统进行分离。
- 设计和实现每个视图。
- 设计和实现每个控制器。
- 使用可安装和卸载的控制器。
分层结构和微内核结构
为什么软件要分层
| 为什么软件要分层 | 说明 |
|---|---|
| 层次提升 | 软件分层后,上一层的软件使用下一层提供的服务。在此基础之上为更上一层提供更为方便的服务。提高开发效率和软件质量。 |
| 隐藏细节 | 通过一个适当的接口把细节隐藏在背后,利于提高效率。 |
| 标准互换 | 每层的服务是基于接口服务的。只要在服务接口不变的情况下,每层的具体代码及其相应的算法等是可以替换的。 |
分层的原则
- 实现和接口分离原则
- 单向性原则
- 服务接口的粒度提升原则
微内核结构
微内核的基本思想是内核中仅存放那些最基本的核心操作系统功能,其他服务和应用则建立在微内核之外,在用户模式下运行。
微内核结构的优点:
- 一致性接口
- 可扩充性
- 适用性
- 可移植性 。随着各种各样的硬件平台的出现,可移植性称为操作系统的一个有吸引力的特性。在微内核结构中,所有与特定CPU有关的代码均在内核中,因而把系统移植到一个新CPU上所做的修改较小。
- 可靠性
- 支持分布式系统
- 支持面向对象的操作系统
详细设计的任务
- 算法设计
- 数据结构设计
- 对需求分析、概要设计确定的概念性的数据类型进行确切的定义。
- 物理设计
- 其他设计:
- 代码设计。为了提高数据的输入、分类、存储及检索等操作的效率,以及节约内存空间,对数据库中的某些数据项的值要进行代码设计。
- 输入/输出格式设计
- 人机对话设计
- 编写详细设计说明书
- 详细设计说明书有下列主要内容:
- 引言:包括编写目的、背景、定义、参考资料。
- 程序系统的组织结构
- 程序 1(标识符)设计说明。包括功能、性能、输入、输出、算法、流程逻辑、接口。
- 程序 2(标识符)设计说明
- 程序N(标识符)设计说明
- 评审
详细设计的原则
- 模块的逻辑描述一要清晰易读,二要正确可靠
- 采用结构化设计方法,改善控制结构,降低程序复杂程度,提高程序的可读性、可测试性和可维护性
- 选择适当的描述工具来描述模块的算法
结构化程序设计的基本方法与原则
- 程序语言中尽量少用GOTO语句,以确保程序的独立性。
- 用单入口单出口的控制结构,确保程序的静态结构与动态执行情况相一致,保证程序容易理解。
- 程序的结构一般采用顺序、选择、循环三种结构来构成,确保结构简单。
- 用自顶向下逐步求精的方法做程序设计
结构化程序设计的缺点是存储容量和运行时间增加10%~20%,优点是可读性和可维护性好。
⭐详细设计的方法、工具
详细设计的方法
处理过程设计中采用的典型方法是结构化程序设计(StructuredProgramming,SP)方法
结构化程序详细设计方法有以下 3 个基本要点:
- 采用自顶向下、逐步求精的程序设计方法
- 使用三种基本控制结构构造程序
- 主程序员的组织形式
详细设计的工具
- 程序流程图
- 盒图
- PAD图
- 判定表
- 判定树
- PDL语言
程序流程图
程序流程图又称为程序框图,它是历史最悠久、使用最广泛的一种描述程序逻辑结构的工具,程序流程图常用符号及基本控制结构来描述。
盒图
盒图(Nassi-Shneiderman图)也称为N-S图,是由艾萨克·纳西(IsaacNassi,1949—)和本·施奈德曼(Ben Shneiderman,1947—)按照结构化的程序设计要求提出的一种图形算法描述工具。与程序流程图相比,N-S图的最大特点在于没有带箭头的流程线,并以基本结构作为图形的基本符号,所以用它描述的算法必定是结构化的。用N-S图表示算法,思路清晰,结构良好,容易设计,也容易阅读,可以十分放心地进行结构化程序设计,从而有效地提高了详细设计的质量和效率。但是,当需要对设计进行修改时,盒图的修改工作量太大。
PAD图
问题分析图(Problem Analysis Diagram,简称PAD)是自 1973 年日本日立公司提出以来,已得到一定程度的推广,它用二维树形结构的图来表示程序的控制流。
判定表
一张判定表由 4 个部分组成。左上部列出所有条件,左下部是所有可能做的动作,右上部是表示各种条件组合的一个矩阵,右下部是和每种条件组合相对应的动作。判定表的每一列实质上是一条规则,规定了与特定的条件组合相对应的动作。
判定树
判定树实质上是判定表的一种变形,它们只有形式上的差别,本质上是一样的。判定树的优点是形式简单、比较直观、易于掌握和使用。主要缺点是容易遗漏判断条件,这个缺点可以通过用判定表验证克服。判定表的缺点是简洁性差于判定树,重复多。另外,在组合条件很复杂的情况下,判定树的节点会有较多的分支,从而使判定树的直观性和易读性有所下降。
PDL语言
过程设计语言(Process Design Language,PDL),也称为伪码,它是一种用正文形式表示数据和处理过程的工具,用严格的关键字和外部语法来定义控制结构和数据结构。
PDL的特点:
- 描述处理过程的说明性语言没有严格的语法。
- 具有模块定义和调用机制,开发人员应根据系统编程所用的语种,用PDL表示有关程序结构。
- 具有数据说明机制,包括简单的与复杂的数据说明。
- 所有关键字都有固定语法,以便提供结构化控制结构、数据说明和模块的特征。
PDL的程序结构: 顺序结构、 选择结构、 重复结构、 出口结构、 扩充结构。
详细设计说明与复审
详细设计说明
详细设计阶段的文档是详细设计说明书,是程序运行过程的描述。
详细设计说明书的内容主要包括以下两个方面:
- 表示软件结构的图表。
- 对逐个模块的程序描述,包括算法和逻辑流程。
详细设计复审
设计复审是指对设计文档的复审
复审的指导原则
- 详细设计复审一般不邀请用户和其他领域的代表。
- 复审应持积极的态度,接受他人提出的建议或批评,坦然面对设计显露出来的不足。全体参加者都应为设计文档的修正创造和谐气氛,防止产生质询或辩论等场面。
- 复审中提出的问题应详细记录,但不要求当场解决。
- 复审结束前做出本次复审能否通过的结论。
复审的主要内容
详细设计复审的重点应该放在各个模块的具体设计上
复审的方式
复审分为正式与非正式两种方式,非正式复审的特点是参加人数少,且均为软件人员,带有同行讨论的性质,因而方便灵活,十分适合于详细设计复审。有一种称为“走查”的非正式复审,进行时由一名设计人员逐行宣读设计资料,由到会的同行跟随他指出的次序一行行地往下审查。当发现有问题或错误时做好记录,然后根据多数参与者的意见,决定通过该设计资料或退回原设计人进行纠正。
Jackson程序设计方法、Warnier程序设计方法
JSP(Jackson Structured Programming)方法定义了一组以数据结构为指导的映射过程,它是根据输入、输出的数据结构,按一定的规则映射成软件结构的过程描述,即程序结构。JSP方法有别于软件的体系结构,因此该方法适用于详细设计阶段。
Jackson程序设计方法
在充分理解问题的输入、输出数据的基础上,找出输入、输出数据的层次结构对应关系,根据数据结构的层次关系映射为软件控制层次结构,然后给出问题详尽、准确的对外求解描述。
Jackson方法面向数据结构设计提供了自己的工具—Jackson结构图。给出了 3 种基本结构的表示,即顺序结构、选择结构和重复结构。
Warnier程序设计方法
Warnier方法与Jackson方法十分相似,它们都从分析数据结构出发,经过映射得出程序结构,最终导出程序的过程性描述。但它们之间仍存在许多差别,总体上看,Warnier方法的中间转换步骤比Jackson方法更细致,过程也更加严格。
Warnier定义了下列 3 类指令:输入和输出准备、分支和分支准备、计算。
基于组件的设计方法
面向对象方法的出现使软件方法学迈进了一大步,但是,它还没有解决高层次上复用、分布式异构互操作等难点。基于组件的软件设计方法学在软件方法学上为解决这个难题提供了机会,它把应用逻辑和实现分离,提供标准接口和框架,使软件开发变成组件的组合。
用组件描述技术和规范,如UML、JavaBean、EJB、Servlet等描述组件;
在基于组件的软件方法方面,研究内容包括基于组件的软件开发方法、模型和过程,重点研究组件库技术、组件组合、组件的测试和质量保证、基于COTS的开发等理论和技术。
基于组件的软件设计方法学的主要特点:把应用逻辑和实现分离,提供标准接口和框架,使软件开发变成组件的组合。以接口为中心、面向行为、基于体系结构设计。对组件要有明确的定义,用组件描述技术和规范。开发应用系统要按组件来裁剪、划分组织与分配角色。使用支持检验组件特性和生成文档的工具,确保组件规范的实现和质量测试。
近年来对基于组件的软件开发方法的研究已经取得了不少成果,在国内外许多大规模分布式应用系统中得到应用和实践。在组件和组件库的标准化方面,在美国军方和政府资助的项目中,已经建立了若干组件库系统,如CARDS、ASSET、DSRS等。
⭐用户界面设计、字符界面设计、菜单设计、对话框设计、多窗口界面设计
⭐用户界面设计原则⭐
用户界面设计
- 可使用性
- 灵活性
- 复杂性和可靠性
可使用性
用户界面设计最重要的目标是可使用性,它包括以下 5 方面:
- 界面简单
- 术语标准化和一致化
- 拥有完善的帮助功能
- 系统响应快和系统成本低
- 容错能力
灵活性
- 算法可隐可观
- 界面方式可由用户动态制定和修改,如此便可以有较高的维护性
- 按照用户的希望和需要,系统提供了不同详细程度的系统响应信息
- 界面标准化
复杂性和可靠性
复杂性是指用户界面的规模和组织的复杂程度。在完成预定功能的前提下,用户界面越简单越好。应当把系统的功能按相关性质和重要程度进行逻辑划分,组织成树形结构,同一分支上包含着相关的命令。无故障使用的间隔时间越长,该用户界面的可靠性就越高。用户界面应能保证用户正确、可靠地使用系统,保证相关程序和数据的安全性。
用户界面设计存在的问题
用户界面设计涉及的范围很广,除了人的因素,还有工程心理学、认知工程学和认知科学等领域的问题。因此需要人机工程专家和计算机专家合作来进行设计开发。用户界面的开发有别于一般软件,无固定结构,其目的是与用户的真正需求相适应。但是用户的意图有时并不容易明确表达出来,唯有通过探索或进一步咨询的方法来完成。部分软件人员没有从用户的角度去考虑界面设计,我行我素,没有重视界面的美观和方便。设计人员习惯单一的抽象思维,希望能自主控制软件的运行,忽略了人机交互设计。有些开发人员则是依赖于设计界面的健壮性,害怕用户的干预导致程序运行的瘫痪。软件人员和用户在知识结构上存在差异。程序员不乐于学习用户工作领域的专业知识,忽视软件的专业性,导致所生成的软件不适合用户的习惯。
字符界面设计
命令语言的设计原则如下:
- 一致性。命令名称、变量顺序等的一致性很重要,可以保证最短的任务时间、最少的求助请求以及最少的差错。
- 选择有意义的独特的命令名
- 避免不必要的复杂性
- 使用缩写要一致
- 命令语法结构一致
- 允许对一个命令串进行重现和修改
- 采用提示帮助临时用户
- 考虑用命令菜单帮助临时用户
菜单设计
根据菜单在屏幕上的出现方式和位置,菜单又可以分为固定菜单和活动菜单。固定菜单是在屏幕的固定位置显示菜单项列表。固定菜单的位置通常在屏幕的上方、下方或者两侧。活动菜单是在需要选择时才出现的菜单。在当前光标所在的任一位置均可出现和消失的菜单称为“弹出式”(Pop-Up)菜单。在固定菜单项被选中后展开的该项下层菜单选择项列表,通常称为“下拉式”(Pull-Down)菜单。活动菜单不占用显示工作空间,可以根据用户当前所处的操作状态和要求动态出现,因此在图形用户界面中广泛使用。活动菜单可在消失后恢复原来显示的内容。实现多级活动菜单显示的技术较复杂,通常要求支持窗口重叠技术。
对话框设计
在应用系统设计中通常考虑两种对话方式
| 对话框设计 | 说明 |
|---|---|
| 必须回答方式 | 当对话框弹出后,用户必须回答有关信息或者撤销当前会话,否则对话框不会消失,系统也不执行其他操作。 |
| 无须回答方式 | 这类对话框通常仅为用户提供当前操作或者系统环境的参考信息。不需要用户回答信息,用户可以不理睬它,继续原来的工作。 |
多窗口界面设计
在多任务系统中,每个窗口可以看作一个独立的逻辑屏幕(虚拟屏幕)
程序设计语言的发展、分类和选择标准
程序设计语言的发展及分类
- 第一代语言——机器语言
- 第二代语言——汇编语言
- 第三代语言——高级程序设计语言
- 第四代语言(4GL)
- 第五代语言
FORTRAN:第一个高级程序设计语言,20 世纪50 年代由IBM公司发明,主要用于科学计算,现在仍在使用。
高级程序设计语言特点
高级语言使用的概念和符号与人们通常使用的概念和符号比较接近,它的一个语句往往对应若干条机器指令。
分类
按应用特点分类,高级语言可以分为基础语言、通用的结构化程序设计语言、面向对象设计语言和专用语言 4 类。
按语言内在特点分类,高级语言可分为系统实现语言、静态高级语言、块结构高级语言和动态高级语言 4 类。
程序设计语言的选择标准
- 理想标准
- 实用标准
理想标准
- 所选择的高级语言应该有理想的模块化机制,以及可读性好的控制结构和数据结构,以使程序容易测试和维护,同时减少软件生存周期的总成本。
- 所选择的高级语言应该使编译程序能够尽可能多地发现程序中的错误,以便于调试和提高软件的可靠性。
- 所选择的高级语言应该有良好的独立编译机制,以降低软件开发和维护的成本。
实用标准
- 从软件的应用领域角度考虑,各种语言都有自己的适用领域,具有各自的特点和相对最为适合的应用领域。 在实时系统中或很特殊的复杂算法、代码优化要求高的领域,可选用汇编语言、Ada语言或C语言
- 系统用户的要求
- 软件运行环境。软件运行的软件、硬件环境也影响着语言的选择。良好的编程环境不但能有效地提高软件生产率,同时能减少错误,有效提高软件质量。
- 可得到的软件工具
- 如果某种语言有支持程序开发的软件工具可以利用,则目标系统的实现和验证都变得比较容易。
- 工程规模
- 软件可移植性要求
- 程序员的知识
软件测试的文档说明
⭐程序设计风格
程序设计风格一般表现在 4 个方面:源程序文档化、数据说明的方法、表达式和语句结构、输入和输出方法。
源程序文档化
- 标识符
- 注释
- 源程序的布局
注释
序言性注释通常在每个模块的开始,它给出程序的整体说明,对于理解程序具有引导作用,其主要内容如下:
- 说明每个模块的用途、功能。
- 说明模块的接口:调用形式、参数描述及从属模块的清单。
- 数据描述:重要数据的名称、用途、限制、约束及其他信息。
- 开发历史:设计者、审阅者姓名及日期,修改说明及日期。
功能性注释插在源程序当中,它着重说明其后的语句或程序段的处理功能以及数据的状态,书写功能性注释,要注意以下 5 点:
- 用于描述一段程序,而不是每一个语句。
- 用缩进和空行,使程序与注释容易区别。
- 注释要正确。
- 有合适的、有助于记忆的标识符和恰当的注释,就能得到比较好的源程序内部的文档。
- 有关设计的说明,也可以作为注释,嵌入源程序体内。
源程序的布局
源程序的布局即源程序的正文编排格式。层次清楚对于改善程序的可读性有重要作用。常用方法如下:
- 注释部分和程序部分之间、完成不同功能的程序段之间都可以用空行显式地隔开。
- 在注释部分周围加上边框。
- 用分层缩进的写法显示嵌套结构层次。
- 每行只写一条语句。
- 书写表达式时适当使用空格或圆括号作为隔离符。
数据说明
- 数据说明的次序应规范。
- 当用一个语句说明多个变量名时,应当对这些变量按字母顺序排列。
- 如果设计了一个复杂数据结构,应使用注释说明这个数据结构在实现时的特点。
表达式和语句结构
- 首先应考虑程序的清晰性和可读性。
- 尽可能使用库函数。
- 注意GOTO语句的使用。
输入和输出方法
- 对所有输入数据进行检验,从而识别错误输入,以保证每个数据的有效性。
- 检查输入项的各种重要组合的合理性,必要时报告输入状态信息。
- 使输入的步骤和操作尽可能简单,并保持简单的输入格式。
- 输入数据时,应允许使用自由格式输入。
- 应允许默认值。
- 输入一批数据时,最好使用输入结束标志,而不要由用户指定输入数据的数目。
- 在以交互式方式进行输入时,要在屏幕上使用提示符明确提示交互输入请求,指明可使用选择项的种类和取值范围。同时,在数据输入的过程中和输入结束时,也应在屏幕上给出状态信息。
- 当程序语言对输入格式有严格要求时,应保持输入格式与输入语句要求的一致性。
- 给所有的输出加注解,并设计输出报表格式。
程序代码效率、存储器效率和输入、输出效率
正由于编码阶段在很大程度上影响着软件的效率,因此在进行编码时必须充分考虑程序生成后的效率。软件效率的高低是一个相对的概念,它与程序的简单性直接相关,不能因过分追求高效率而忽视了程序设计中的其他要求。
程序效率
程序效率是指程序的执行速度和程序占用的存储空间,即主要涉及处理时间和存储器容量两个方面。
下面是 3 条基本准则:
- 效率是性能方面的需求,应当在需求分析阶段给出效率方面的要求。软件效率应以需求为准,不应以人力所及为准。
- 提高效率依靠好的设计,而不仅仅是利用程序技巧。
- 程序的效率与程序的简单性相关。
代码效率
当把详细设计翻译为代码时,一般可以使用以下准则:
- 编码之前应先简化算术和逻辑的表达式。
- 仔细研究嵌套的循环,以确定是否有语句可以从内层往外移。
- 尽量避免使用多维数组。
- 尽量避免使用指针和复杂的列表。
- 使用执行时间短的算术运算。
- 在表达式中尽量避免出现不同的数据类型。
- 尽量不用整数表达式和布尔表达式。
存储器效率
在微型计算机系统中,内存的限制仍是一个现实问题。因此要选择可生成较短目标代码且存储压缩性能优良的编译程序,必要时采用汇编语言编程。
输入/输出效率
- 所有输入/输出都应有缓冲,以避免过多的通信次数。
- 对于辅存(如磁盘)应选用简单有效的访问方法。
- 与辅存有关的输入/输出应该以块为单位进行。
- 与终端和打印机有关的输入/输出,应当考虑设备的特性,以提高输入/输出的质量和速度。
- 有的输入/输出方式尽管很高效,但如果难以被人们理解,也不应当采用。
冗余程序设计和防错程序设计
提高软件质量和可靠性的技术大致可分为两类,一类是避开错误技术,即在开发的过程中不让差错潜入软件的技术;另一类是容错技术,即对某些无法避开的差错,使其影响减至最小的技术。
冗余程序设计
冗余是改善系统可靠性的一种重要技术。在软件系统中,采用冗余技术是指要解决一个问题必须设计出两个不同的程序,包括采用不同的算法和设计,而且编程人员也应该不同。如果两个程序的执行结果都在预定的“计算误差”之内,则可任取其中一个结果或者取两者的平均值作为正确答案。若两个程序的执行结果不一致,则可使用“错误检测系统”加以纠正。如果在同时解同一问题时采用三种或三种以上不同方法进行程序设计,则其运行结果的正确答案可采纳多数一致的那个答案,这种技术称为“多数逻辑”或“多数表决”。
防错程序设计
- 主动式防错程序设计
- 被动式防错程序设计
主动式防错程序设计
周期性地对整个程序或数据库进行搜查或在空闲时搜查异常情况。主动式程序设计既可在处理输入信息期间使用,也可在系统空闲时间或等待下一个输入时使用。
- 内存检查
- 标志检查
- 反向检查
- 状态检查
- 连接检查
- 时间检查
- 其他检查
被动式防错程序设计
必须等到某个输入之后才能进行检查,也就是达到检查点时,才能对程序的某些部分进行检查。
- 来自外部设备的输入数据,包括范围、属性是否正确
- 由其他程序所提供的数据是否正确
- 数据库中的数据,包括数组、文件、结构、记录是否正确
- 操作员的输入,包括输入的性质、顺序是否正确
- 栈的深度是否正确
- 数组界限是否正确
- 表达式中是否出现零分母情况
- 正在运行的程序版本是否是所期望的(包括最后系统重新组合的日期)
- 通过其他程序或外部设备的输出数据是否正确
结构化程序设计方法
结构化程序设计的概念最早由Edsger Wybe Dijkstra提出
结构化程序设计的原则
- 使用语言中的顺序、选择、重复等有限的基本控制结构表示程序逻辑。
- 选用的控制结构只允许有一个入口和一个出口。
- 程序语句组成容易识别的块,每块只有一个入口和出口。
- 复杂结构应该用基本控制结构进行组合嵌套来实现。
- 语言中没有的控制结构,可用一段等价的程序段模拟。
- 严格控制GOTO语句,仅在下列情形下才可使用
- 用一个非结构化的程序设计语言来实现一个结构化的构造。
- 在可以改善而不是损害程序可读性的情况下。结构化程序设计也有它的缺点,就是目标程序所需要的存储容量和运行时间都有一些增加。
自顶向下、逐步细化的设计方法
自顶向下、逐步求精方法的优点如下:
- 自顶向下、逐步求精方法符合人们解决复杂问题的普遍规律,可提高软件开发的成功率和生产率。
- 用先全局后局部、先整体后细节、先抽象后具体的逐步求精的过程开发出来的程序具有清晰的层次结构,因此程序容易阅读和理解。
- 程序自顶向下,逐步细化,分解成一个树形结构,在同一层模块上做的细化工作相互独立。在任何一步发生错误,一般只影响它下层的模块,同一层其他模块不受影响。在以后的测试中,也可以先独立地一个一个模块地测试,最后再集成测试。
- 程序清晰和模块化,使得在修改和重新设计一个软件时,可复用的代码量最大。
- 每一步工作仅在上层模块的基础上做不多的设计扩展,便于检查。
- 有利于设计的分工和组织工作。
主程序员的组织形式
主程序员的组织形式指开发程序的人员应以一个主程序员(负责全部技术活动)、一个后备程序员(协调、支持主程序员)和一个程序管理员(负责事务性工作,如收集、记录数据,文档资料管理等)三人为核心,再加上一些专家(如通信专家、数据库专家)、其他技术人员组成小组。这种组织形式突出了主程序员的领导,设计责任集中在少数人身上,有利于提高软件质量,并且能有效地提高软件生产率。
代码行度量方法、McCabe度量法、Halstead度量法
⭐代码行度量法
统计一个程序的源代码行数,并以源代码行数作为程序复杂性的度量。
局限性:源代码行数与出错率是线性相关的。随着程序的增大,出错率以非线性方式增长。所以,代码行度量法只是一个简单的、估计粗糙的方法。
McCabe度量法
McCabe度量法是由托马斯·麦克凯(Thomas McCabe)提出的一种基于程序控制流的复杂性度量方法。McCabe定义的程序复杂性度量值又称环路复杂度,它基于一个程序模块的程序图中环路的个数,因此计算它先要画出程序图。
Halstead度量法
Halstead度量法可以科学确定计算机软件开发中的一些定量规律,它采用以下一组基本的度量值,这些度量值通常在程序产生之后得出,或者在设计完成之后估算出。
- 程序长度,即预测的Halstead长度。
- 实际的Halstead长度。
- 程序的词汇表。
- 程序量
- 程序员工作量
- 程序的潜在错误。
软件测试的意义、目的和原则
软件测试就是在软件投入运行前,对软件需求规格说明、设计规格说明和编码的最终复审,是软件质量保证的关键过程。
测试用例是为特定的目的而开发的一组测试输入、执行条件和预期结果。
软件测试的意义
确认一个系统的品质或性能是否符合用户提出的要求的标准。软件测试是为了发现错误而执行程序的过程
软件测试的目的:确保软件的质量
- 确认软件的质量,一方面是确认软件做了所期望的事情,另一方面是确认软件以正确的方式来做了这个事件。
- 提供信息,比如提供给开发人员或项目经理的反馈信息,为风险评估所准备的信息。
- 保证整个软件开发过程是高质量的。
软件测试的原则
| 软件测试的原则 | 说明 |
|---|---|
| 用户角度 | 通过软件测试能充分暴露软件中存在的问题和缺陷,从而考虑是否可以接受该产品。 |
| 开发者的角度 | 表明软件产品不存在错误,已经正确地实现了用户的需求,确立人们对软件质量的信心。 |
软件静态测试和动态测试方法
静态测试
静态测试是指无须执行被测代码,而是借助专用的软件测试工具评审软件文档或程序,度量程序静态复杂度,检查软件是否符合编程标准,借以发现编写的程序的不足之处,减少错误出现的概率。
动态测试
动态测试是使被测代码在相对真实的环境下运行,从多角度观察程序运行时能体现的功能、逻辑、行为、结构等,以发现其中的错误现象。
动态测试方法分为黑盒法和白盒法,黑盒测试是基于功能的测试,只关心软件的功能,而不考虑其内部结构,也叫功能测试; 白盒测试只关心软件内部逻辑结构,测试覆盖率,是由逻辑驱动的测试。为了较快得到测试效果,通常先进行功能测试,达到所有功能后,为确定软件的可靠性进行必要的覆盖测试。
软件黑盒测试方法和白盒测试方法
⭐黑盒测试
黑盒测试也称功能测试或数据驱动测试,它是在已知产品所应具有的功能上,通过测试来检测每个功能是否都能正常使用,在测试时,把程序看作一个不能打开的黑盒子,在完全不考虑程序内部结构和内部特性的情况下,测试者在程序接口进行测试,它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收输入数据而产生正确的输出信息,并且保持外部信息(如数据库或文件)的完整性。
黑盒测试的测试用例设计方法如下:
- 划分等价类
- 确定测试用例
- 边界值分析
- 错误推测
- 因果图
- 综合策略
【例题】某个比赛报名系统的程序输入条件为:每个参赛者可以报名1至3个赛项。试用黑盒测试法完成测试,按边界值分析法,无效的设计测试用例是(D)。
- A. 赛项数=1
- B. 赛项数=3
- C. 赛项数=4
- D. 赛项数=5 【解析:5不是边界值。】
⭐白盒测试
白盒测试也称结构测试或逻辑驱动测试,它是知道产品的内部工作过程,通过测试来检测产品内部工作是否按照规格说明书的规定正常进行,按照程序内部的结构测试程序,检验程序中的每条通路是否都能按预定要求正确工作,而不顾它的功能,白盒测试的主要方法有逻辑驱动、基本路径测试等,主要用于软件验证。
白盒测试是穷举路径测试。
7 种常用的覆盖技术:
- 语句覆盖:为了提高发现错误的可能性,在测试时应该执行到程序中的每一个语句。语句覆盖是指设计足够的测试用例,使被测试程序中每个语句至少执行一次。
- 判定覆盖:判定覆盖指设计足够的测试用例,使得被测程序中每个判定表达式至少获得一次“真”值和“假”值,从而使程序的每一个分支至少都通过一次,因此判定覆盖也称为分支覆盖。
- 条件覆盖:条件覆盖是指设计足够的测试用例,使得判定表达式中每个条件的各种可能的值至少出现一次。
- 判定/条件覆盖:该覆盖标准指设计足够的测试用例,使得判定表达式的每个条件的所有可能的值至少出现一次,并使每个判定表达式所有可能的结果也至少出现一次。
- 多条件覆盖:多条件覆盖也称条件组合覆盖,设计足够的测试用例,使得每个判定中条件的各种可能组合都至少出现一次。
- 路径覆盖:路径覆盖是指设计足够的测试用例,覆盖被测程序中所有可能的路径。
- 修正条件判定覆盖:需要足够的测试用例来确定各个条件能够影响到包含判定的结果。
| 覆盖技术 | 个人理解 |
|---|---|
| 语句覆盖 | 最低的测试度量。你的测试用例就像个“检查员”,要求把程序代码里的每一行(每一句)都至少跑过一次,就算完成任务了。至于这条路上的各种岔路、各种特殊情况有没有测到?不管,反正每一行我都“踩”过一脚就行。 |
| 判定覆盖 | 每个 if/while 的真和假分支至少跑一次。 |
| 条件覆盖 | 程序里每个判定(比如 if (A > 0 && B > 0))中,都包含多个原子条件(A>0、B>0)。条件覆盖要求每个原子条件都能取到一次“真”和一次“假”,不管整个判断的最终结果是真是假。 |
| 判定/条件覆盖 | 它要求你在设计测试用例时,同时满足两个条件:① 每个判定(if、while等)的真/假分支至少各走一次(判定覆盖的要求);② 每个判定内部的每个原子条件(A>0、B>0等)的真/假取值也至少各出现一次(条件覆盖的要求)。两条要求同时达成,才算过关。 |
| 多条件覆盖 | 程序里每个判定(比如 if (A > 0 && B > 0))包含多个原子条件(A>0、B>0)。多条件覆盖要求这些原子条件的真假值的所有可能组合,至少都出现一次。 |
| 路径覆盖 | 覆盖率最强。你需要设计测试用例,把程序控制流图中从入口到出口的每一条可能的执行路径(包括循环的各种情况)都至少走一遍。 |
| 修正条件判定覆盖 | 它要求:① 每个判定(if)的真/假分支至少走一次;② 每个原子条件(A>0)的真/假值至少出现一次;③ 最关键的是:每个原子条件要独立地影响整个判定的结果——即单独改变该条件,其他条件不变,判定结果会随之改变。 |
单元测试、集成测试、确认测试、系统测试和验收测试
软件测试的步骤
- 测试过程按 5 个步骤进行,即单元测试、集成测试、确认测试、系统测试和验收测试。
- 开始是单元测试,集中对源代码实现的每一个程序单元进行测试,检查各个程序模块是否正确地实现了规定的功能。
- 集成测试把已测试过的模块组装起来,主要对与设计相关的软件体系结构的构造进行测试。
- 确认测试则是要检查已实现的软件是否满足了需求规格说明中确定了的各种需求,以及软件配置是否完全正确。
- 系统测试把已经经过确认的软件纳入实际运行环境中,与其他系统成分组合在一起进行测试。
单元测试
单元测试的对象是软件设计的最小单位模块。单元测试的依据是详细设计的描述,单元测试应对模块内所有重要的控制路径设计测试用例,以便发现模块内部的错误。单元测试多采用白盒测试技术,系统内多个模块可以并行地进行测试
单元测试任务:
- 模块接口测试:模块接口测试是单元测试的基础
- 模块局部数据结构测试
- 模块边界条件测试
- 模块中所有独立执行通路测试
- 模块的各条错误处理通路测试
集成测试
集成测试(也叫组装测试,联合测试)是单元测试的逻辑扩展。最简单的形式是两个已经测试过的单元组合成一个组件,并且测试它们之间的接口。从这一层意义上讲,组件是指多个单元的集成聚合。
集成测试是单元测试的逻辑扩展,集成测试是在单元测试的基础上,在将所有的软件单元按照概要设计规格说明的要求组装成模块、子系统或系统的过程中,测试各部分工作是否达到或实现相应技术指标及要求的活动。即在集成测试之前,单元测试应该已经完成,集成测试中所使用的对象应该是已经经过单元测试的软件单元。集成测试是单元测试的逻辑扩展。
集成测试的实施方案:
- 自底向上集成测试
- 核心系统先行集成测试
- 高频集成测试
确认测试
确认测试又称有效性测试。任务是验证软件的功能和性能及其他特性是否与用户的要求一致。
验收测试
验收测试是软件开发结束后,软件产品投入实际应用以前进行的最后一次质量检验活动。
软件调试过程和调试途径
调试过程
调试(也称为纠错)作为成功测试的后果出现,调试是在测试发现错误之后排除错误的过程。调试的目标都是寻找软件错误的原因并改正错误。
调试是软件开发过程中最艰巨的脑力劳动。调试工作如此困难,可能心理方面的原因多于技术方面的原因,但是,软件错误的下述特征也是相当重要的原因:
- 症状和产生症状的原因可能在程序中相距甚远,即症状可能出现在程序的一个部分,而实际的原因可能在与之相距很远的另一部分。紧耦合的程序结果更加剧了这种情况。
- 当改正了另一个错误之后,症状可能暂时消失了。
- 症状可能实际上并不是由错误引起的。
- 症状可能是由不易跟踪的人为错误引起的。
- 症状可能是由定时问题而不是由处理问题引起的。
- 可能很难重新产生完全一样的输入条件。
- 症状可能时有时无,这种情况在硬件和软件紧密地耦合在一起的嵌入式系统中特别常见。
- 症状可能是由分布在许多任务中的原因引起的,这些任务运行在不同的处理机上
调试途径
- 蛮干法
- 回溯法
- 原因排除法
蛮干法可能是寻找软件错误原因的最低效的方法。仅当所有其他方法都失败的情况下,才应该使用这种方法。
在动手改正错误之前,软件工程师应该仔细考虑下述 3 个问题:
- 是否同样的错误也在程序其他地方存在?在许多情况下,一个程序错误是由错误的逻辑思维模式造成的,而这种逻辑思维模式也可能用在别的地方。仔细分析这种逻辑模式,有可能发现其他错误。
- 将要进行的修改可能会引入的“下一个错误”是什么?在改正错误之前应该仔细研究源程序,以评估逻辑和数据结构的耦合程度。如果所要做的修改位于程序的高耦合段中,则修改时必须特别小心谨慎。
- 为防止今后出现类似的错误,应该做什么?如果不仅修改了软件产品,还改进了开发软件产品的软件过程,则不仅排除了现有程序中的错误,还避免了今后在程序中可能出现的错误。
软件测试用例设计方法和错误曲线
软件测试工具和软件自动测试
自动软件测试的优点
- 对程序的回归测试更方便,这是自动化测试最主要的任务,特别是在程序修改比较频繁时,效果是非常明显的。
- 可以运行更多、更烦琐的测试。
- 可以执行一些手工测试困难或不可能进行的测试。
- 更好地利用资源。
- 测试具有一致性和可重复性。
- 测试的复用性。
- 可以让产品更快面向市场。
- 增加软件信任度。
⭐自动测试的相关问题
- 不现实的期望。
- 缺乏测试实践经验。
- 期望自动测试工具能取代手工测试。
- 期望自动测试发现新故障。
- 安全性错觉。
- 测试自动化不能提高有效性。自动化测试并不会比手工运行相同的测试更加有效,自动化只能提高测试的效率,即运行测试的开销和时间。
- 自动测试的维护性。
- 测试自动化可能会制约软件开发。
- 组织问题。
- 工具本身没有想象力。
测试工具分类
黑盒测试(功能测试)工具:功能测试工具、性能测试工具
白盒测试工具一般是针对被测源程序进行的测试,测试中发现的故障可以定位在代码级,根据测试工具的原理不同,又可分为静态测试工具和动态测试工具。
白盒测试工具:静态测试工具、动态测试工具
静态测试工具:静态测试是指在不执行程序的情况下,对软件特性进行分析。静态分析主要集中在需求文档、设计文档以及程序结构上,可以进行类型分析、接口分析、输入/输出规格说明等。
动态测试工具:动态测试工具直接执行被测程序以提供测试支持。它所支持的测试范围十分广泛,包括功能确认与接口测试、覆盖率分析、性能分析、内存分析等。
软件维护的定义、分类和特点
软件维护的定义
软件系统交付之后对其实施更改的过程叫作软件维护。
软件维护的分类
软件需要进行维护的原因很多,归结起来主要有以下 3 种: (1)故障 (2)环境变化 (3)用户和维护人员的要求
由这些原因引进的软件维护活动分为 4 类,每类维护活动的任务各不相同:
- 改正性维护:在软件运行中发生异常或故障时进行的,任务就是诊断和修改软件,以识别和纠正软件中存在的错误。
- 适应性维护:对软件进行适当的修改,以便运行的软件能与变化了的环境相适应。
- 完善性维护:通过修改软件,扩充软件的功能,提高原有软件的性能,满足用户日益增长的需求。
- 预防性维护:提高软件的可维护性、可靠性,对软件进行一些适当的改动,为今后进行的软件维护活动,进一步改进软件打下良好的基础。
完善性维护占了维护阶段工作量的一半以上,由此可见,大多数维护工作是用来对软件的更改或加强,而不是纠错。
预防性维护:对于很早以前开发的程序,由于没有科学的软件工程做指导,开发出来的程序结构不好,可能一个模块就有上千条语句,又没有相应的文档。为了修改这类程序以适应用户新的或变更的需求,可以有以下 4 种选择。
- 通过反复地修改,以实现必要的变更。
- 尽可能多地掌握程序的内部工作细节,以便更有效地做出修改。
- 重新设计、重新编码和测试那些需要变更的软件部分,把软件工程方法应用于有修改的部分。
- 用CASE工具(逆向工程和再工程工具)对程序全部重新设计、重新编码和测试。
预防性维护方法是由Miller提出来的。他的想法是“结构化翻新”,并将这个概念定义为“把今天的方法学应用到昨天的系统,以支持明天的需求”。
软件维护的特点
- 结构化维护:如果有一个完整的软件配置存在,那么维护工作从评价设计文档开始,确定软件重要的结构特点、性能特点以及接口特点。减少精力的浪费并且能提高维护的总体质量。
- 非结构化维护:如果软件配置的唯一成分是程序代码,那么维护活动从艰苦的评价程序代码开始,而且常常由于程序内部文档不足而使评价更困难。非结构化维护需要付出很大代价。
软件维护过程
对维护申请报告进行分析、评价后,在软件维护组织内部还要制定一份软件修改报告,该报告是维护阶段的另一种文档,用来指出以下 4 点。
- 为满足软件问题报告实际要求的工作量。
- 要求修改的类型。
- 请求修改的优先权。
- 关于修改的事后数据。
维护过程
一个维护申请提出之后,经评审需要维护,则按下列过程实施维护。
- 首先确定要进行维护的类型。
- 对改正性维护从评价错误的严重性开始。
- 对适应性和完善性软件维护。
- 实施维护任务。
- “救火”维护。对于有的维护申请,需要立即进行修改维护,这时申请的维护称为“救火”维护。显然,如果软件开发机构经常“救火”,就必须要认真检查一下,该机构的管理和技术存在什么重大问题。
⭐软件可维护性度量和提高软件的可维护性方法
软件可维护性是指软件被理解、改正、调整和改进的难易程度。可维护性是指导软件工程各个阶段的一条基本原则,也是软件工程追求的目标之一。
软件的可维护性受各种因素的影响。设计、编码和测试时漫不经心,软件配置不全,都会给维护带来困难。除了与开发方法有关的因素外,还有下列与开发环境有关的因素。
- 是否拥有一组训练有素的软件人员。
- 系统结构是否可理解。
- 是否使用标准的程序设计语言。
- 是否使用标准的操作系统。
- 文档的结构是否标准化。
- 测试用例是否合适。
- 是否已有嵌入系统的调试工具。
- 是否有一台计算机可用于维护。
软件可维护性度量
- 察觉到问题所耗的时间。
- 收集维护工具使用的时间。
- 分析问题所需时间。
- 形成修改说明书所用时间。
- 纠错(或修改)所用时间。
- 局部测试所用时间。
- 整体测试所用时间。
- 维护复审所用时间。
- 完全恢复所用时间。
提高软件可维护性的方法
- 软件的质量目标和优先级,对于每一种软件都应建立明确的质量目标。
- 提高软件质量的技术和工具
- 质量保证审查
- 审查检查点。在设计阶段,检查重点在于软件的可理解性、可修改性、可测试性。
- 验收检查
- 周期性的维护审查
- 维护软件包
- 程序设计语言,编码时所用的程序设计语言对软件的可维护性影响很大。
- 软件文档
为了改善软件可维护性,应尽量使用能提高软件质量的技术和工具:
- 模块化技术
- 结构化程序设计技术
- 自动重建结构和重新格式化的工具
要提高软件可维护性必须有质量保证审查。软件质量保证审查可分为以下4 种类型:
- 审查检查点
- 验收检查
- 周期性的维护审查
- 维护软件包
文档是影响软件可维护性的决定性因素。具有好的文档的软件才具有高的可维护性。软件系统的文档可分为用户文档和系统文档两类。
软件的逆向工程和再工程过程、方法
逆向工程
逆向工程是从源代码中抽取出来的设计信息。
逆向工程中得到的信息抽象层次(从低到高),依次为:软件过程的设计表示、程序和数据结构信息、数据和控制流模型和实体、关系模型。
软件再工程过程
软件再生要考虑如下 6 个方面:
- 库存目录分析。
- 文档重构。
- 逆向过程。
- 代码重构。
- 数据重构。
- 正向工程。
支持再工程,而不是维护原有程序的理由如下:
- 维护一行源代码的代价可能是 14~40 倍于初始开发该行源代码的代价。
- 软件体系结构(程序及数据结构)的重新设计使用了现代设计概念的维护方法,可能有很大的帮助。
- 由于软件的原型已经存在,开发生产率应当大大高于平均水平。
- 现行用户具有较多有关该软件的经验,因此,新的变更需求和变更的范围能够容易搞清。
- 逆向工程和再工程的工具可以使一部分作业自动化。
- 软件配置将可以在完成预防性维护的基础上建立起来。
⭐软件再工程的方法
- 用户指导下的搜索与变换
- 变换式方法
- 基于领域知识的方法
- 铅板恢复法
面向对象方法学的优点
- 与人类习惯的思维方式一致
- 稳定性好
- 可重用性好。更容易将大型软件分解成独立的小产品。
- 较易开发大型软件产品
- 可维护性好
面向对象方法学的基本概念
面向对象方法是一种新的思维方法,它不是把程序看作是工作在数据上的一系列过程或函数的集合,而是把程序看作是相互协作而又彼此独立的对象的集合。
面向对象方法学的出发点和基本原则,是尽可能模拟人类习惯的思维方式,使开发软件的方法与过程尽可能接近人类认识世界解决问题的方法与过程,也就是使描述问题的问题空间与实现解法的解空间在结构上尽可能一致。
- 对象
- 其他概念
- 类(Class)
- 实例(Instance)
- 消息(Message)
- 方法(Method)
- 属性(Attribute)
- 封装(Encapsulation)
- 继承(Inheritance)
- 多态性(Polymorphism)
- 重载(Overloading)
面向对象建模方法
所谓模型,就是为了理解事物而对事物做出的一种抽象,是对事物的一种无歧义的书面描述。
模型是一种思考工具,利用这种工具可以把知识规范地表示出来。模型可以帮助人们思考问题、定义术语、在选择术语时做出恰当的假设,并且有助于保持定义和假设的一致性。
用面向对象方法开发软件,通常需要建立 3 种形式的模型,它们分别是描述系统数据结构的对象模型、描述系统控制结构的动态模型和描述系统功能的功能模型。
对于那些因过分复杂而不能直接理解的系统,特别需要建立模型,建模的目的主要是为了减少复杂性。人的头脑每次只能处理一定数量的信息,模型通过把系统的重要部分分解成人的头脑一次能处理的若干子部分,从而减少系统的复杂程度。
在不同的应用问题中,这 3 种模型的相对重要程度会有所不同。但是,用面向对象方法开发软件,在任何情况下,对象模型始终都是最重要、最基本、最核心的。
⭐对象模型建立方法
对象模型表示静态的、结构化的系统的“数据”性质。它是对模拟客观世界实体的对象以及对象彼此间关系的映射,描述了系统的静态结构。
- 类图的基本符号
- 定义类
- 定义属性:属性的可见性(即可访问性)通常有下述 3 种,即公有的(Public)、私有的(Private)和保护的(Protected),分别用加号(+)、减号(-)和井号(#)表示。
- 定义服务
- 表示关系的符号
- 关联(Association)
- 普通关联
- 关联的角色
- 限定关联
- 关联类
- 聚集(Aggregation)
- 聚集也称为聚合,是关联的特例。
- 共享聚集
- 组合聚集
- 泛化(Generalization)
- UML中的泛化关系就是通常所说的继承关系,它是通用类和具体类之间的一种分类关系。具体类完全拥有通用类的信息,并且还可以附加一些其他信息。
- 注意,泛化针对类型而不针对实例,一个类可以继承另一个类,但一个对象不能继承另一个对象。实际上,泛化关系指出在类与类之间存在“一般—特殊”关系。
- 普通泛化
- 受限泛化
- 依赖(Dependency)
- 依赖关系
- 细化关系
- 关联(Association)
⭐动态模型建立方法
动态模型表示瞬时的、行为化的系统“控制”性质,它规定了对象模型中的对象的合法变化序列。
所谓状态,是对对象属性值的一种抽象。当然,在定义状态时应忽略那些不影响对象行为的属性。
状态有持续性,它占用一段时间间隔。状态和事件密不可分,一个事件分开两个状态,一个状态隔开两个事件。事件表示时刻,状态代表时间间隔。
通常,用UML提供的状态图来描绘对象的状态、触发状态转换的事件以及对象的行为。每个类的动态行为用一张状态图来描绘,各个类的状态图通过共享事件合并起来,从而构成系统的动态模型。动态模型是基于事件共享而互相关联的一组状态图的集合。
生存周期中的阶段也就是对象的状态。
⭐功能模型建立方法
功能模型表示变化的系统的“功能”性质,它指明了系统应该“做什么”,因此更直接地反映了用户对目标系统的需求。
UML提供的用例图是进行需求分析和建立功能模型的强有力工具。在UML中把用例图建立起来的系统模型称为用例模型。
用例图
一幅用例图包含的模型元素有系统、行为者、用例及用例之间的关系。
- 系统:系统被看作是一个提供用例的黑盒子,内部如何工作、用例如何实现,这些对于建立用例模型来说都是不重要的。
- 用例:一个用例是可以被行为者感受到的、一个系统的、完整的功能。在UML中把用例定义成系统完成的一系列动作,动作的结果能被特定的行为者察觉到。
- 行为者:行为者是指与系统交互的人或其他系统,它代表外部实体。使用用例并且与系统交互的任何人或物都是行为者。
- 用例之间的关系:UML用例之间主要有扩展和使用两种关系,它们是泛化关系的两种不同形式。(1)扩展关系(2)使用关系
用例建模
- 确定行为者:为获取用例首先要找出系统的行为者,可以通过请系统的用户回答一些问题的办法来发现行为者。
- 确定用例:一旦找到了行为者,就可以通过请每个行为者回答下述问题来获取用例
三种模型之间的关系
面向对象建模技术所建立的 3 种模型,分别从 3 个不同侧面描述了所要开发的系统。这 3 种模型相互补充、相互配合,使得我们对系统的认识更加全面。功能模型指明了系统应该“做什么”;动态模型明确规定了什么时候做(即在何种状态下接受了什么事件的触发);对象模型则定义了做事情的实体。
在面向对象方法学中,对象模型是最基本最重要的,它为其他两种模型奠定了基础,我们依靠对象模型完成 3 种模型的集成。下面扼要地叙述 3 种模型之间的关系。
- 针对每个类建立的动态模型,描述了类实例的生命周期或运行周期。
- 状态转换驱使行为发生,这些行为在数据流图中被映射成处理,在用例图中被映射成用例,它们同时与类图中的服务相对应。
- 功能模型中的处理(或用例)对应于对象模型中类所提供的服务。通常,复杂的处理(或用例)对应复杂对象提供的服务,简单的处理(或用例)对应基本的对象提供的服务。有时一个处理(或用例)对应多个服务,也有一个服务对应多个处理(或用例)的时候。
- 数据流图中的数据存储,以及数据的源点/终点,通常是对象模型中的对象。
- 数据流图中的数据流,往往是对象模型中对象的属性值,也可能是整个对象。
- 用例图中的行为者,可能是对象模型中的对象。
- 功能模型中的处理(或用例)可能产生动态模型中的事件。
- 对象模型描述了数据流图中的数据流、数据存储以及数据源点/终点的结构。
面向对象分析概述
面向对象分析(Object-Oriented Analysis,OOA),就是抽取和整理用户需求并建立问题域精确模型的过程。
面向对象分析的关键,是识别出问题域内的对象,并分析它们相互间的关系,最终建立起问题域简洁、精确、可理解的正确模型。
在面向对象分析阶段,开发人员应该首先理解在需求获取阶段产生的用例模型,找出描述问题域和系统责任所需的对象和类,将用例行为映射到对象上,进一步分析它们的内部构成和外部关系,从而建立面向对象分析模型。最后,开发人员和用户一起检查模型,保证模型的正确性、一致性、完整性和可行性。面向对象分析的目标是要建立一系列的模型来描述能够满足用户需要的计算机软件。面向对象分析模型需表示出系统的信息(或数据)、功能和行为三个方面的基本特征。
在面向对象建模的过程中,系统分析员必须认真向领域专家学习,尤其是在建模过程中的分类工作往往有很大难度。继承关系的建立实质上是知识抽取过程,它必须反映出一定深度的领域知识,这不是系统分析员单方面努力所能做到的,必须有领域专家的密切配合才能完成。此外,系统分析员还应该仔细研究以前针对相同的或类似问题域进行面向对象分析所得到的结果。由于面向对象分析结果的稳定性和可重用性,这些结果在当前项目中往往有许多是可以重用的。
面向对象的3个子模型和5个层次
3个子模型
- 对象模型
- 功能模型
- 行为模型
5个层次
面向对象分析增加了一个主题层,它可以从一个相当高的层次描述总体模型,并对读者的注意力加以指导。
- 主题层
- 对象层
- 结构层
- 属性层
- 服务层
确定类与对象、确定关联、划分主题、确定属性、识别继承关系和反复修改
编写脚本、设想用户界面、绘制事件跟踪图、绘制状态图、审查动态模型
编写脚本
建立动态模型的第一步是编写典型交互行为的脚本(编写脚本)。虽然脚本中不可能包括每个偶然事件,但是,必须保证不遗漏常见的交互行为。 第二步,从脚本中提取出事件,确定触发每个事件的动作对象以及接受事件的目标对象。 第三步,排列事件发生的次序,确定每个对象可能有的状态以及状态间的转换关系,并用状态图描绘它们。 最后,比较各个对象的状态图,检查它们之间的一致性,确保事件之间的匹配。
绘制基本系统模型图、绘制功能级数据流图、描述处理框功能
描述处理框功能
说明性描述规定了输入值和输出值之间的关系,以及输出值应遵循的规律。过程性描述则通过算法说明“做什么”。一般来说,说明性描述优于过程性描述,因为这类描述中通常不会隐含具体实现方面的考虑。
定义服务
确定类中应提供哪些服务的工作需要动态模型和功能模型构造完成之后才能进行,因为这两个子模型明确地描述了每个类中应该分担的系统责任。依据这些责任便可以确定类中应提供哪些服务。
确定一个类中的服务,主要取决于该类在问题中的实际作用以及求解过程中承担的处理责任。确定的原则如下:
- 常规行为。类中应提供访问、修改自身属性值的基本操作。这类操作属于类的内部操作,可不必在对象模型中显式表示。
- 事件的处理操作。在面向对象的系统中,一个事件,即意味着一条消息。类和对象中必须提供处理相应消息的服务。
- 完成数据流图中处理框对应的操作。功能模型中的每个处理框代表了系统应实现的部分功能,而这些功能都与一个对象(也可能是若干个对象)中提供的服务相对应。
- 利用继承机制优化服务集合,减少冗余服务。应该尽量利用继承机制优化服务功能和减少服务的数目。只要不违反问题的实际情况和一般常识,应该尽量抽取相似的公共属性和服务,以建立这些相似类的新父类,并在类等级的不同层次中正确地定义各个服务。
动态模型中,状态图描述了对象应接收的事件(消息)
功能模型中的每个处理框代表了系统应实现的部分功能,而这些功能都与一个对象(也可能是若干个对象)中提供的服务相对应。
⭐建立对象模型
面向对象分析的首要工作是建立问题域的对象模型。这个模型描述了现实世界中的“类”与“对象”以及它们之间的关系,表示了目标系统的静态数据结构。
对象模型通常有 5 个层次。典型的工作步骤是,首先确定对象类和关联(因为它们影响系统整体结构和解决问题的方法),对于大型复杂问题还要进一步划分出若干个主题;然后给类和关联增添属性,以进一步描述它们,接下来利用适当的继承关系进一步合并和组织类。对于类中操作的最后确定,则等到建立了动态模型和功能模型之后,因为这两个子模型更准确地描述了对类中提供的服务的需求。
- 确定类与对象
- 确定关联
- 划分主题
- 确定属性
- 识别继承关系
- 反复修改
确定类与对象
类与对象是在问题域中客观存在的,系统分析员的主要任务就是通过分析找出这些类与对象。首先找出所有候选的类与对象,然后筛选掉不正确的或不必要的类与对象
- 找出候选的类与对象
- 筛选出正确的类与对象
筛选时主要依据下列标准,删除不正确或不必要的类与对象:
- 冗余:如果两个类表达了同样的信息,则应该保留在此问题域中最富于描述类的名称。
- 无关:现实世界中存在许多对象,不能把它们都纳入到系统中去,仅需要把与本问题密切相关的类与对象放进目标系统中。
- 笼统:通常把这些笼统的或模糊的类去掉
- 属性:在需求陈述中有些名词实际上描述的是其他对象的属性,应把这些名词从候选类与对象中去掉。
- 操作:在需求陈述中有可能使用一些既可作为名词又可作为动词的词,应慎重考虑它们在本问题中的含义,以便正确地决定把它们作为类还是作为类中定义的操作。
- 实现:应该去掉仅与实现有关的候选的类与对象。
确定关联
- 初步确定关联
- 筛选
- 进一步完善
在需求陈述中使用的描述性动词或动词词组,通常表示关联关系。因此,在初步确定关联时,大多数关联可以通过直接提取需求陈述中的动词词组而得出。
筛选:
- 已删去的类之间的关联
- 与问题无关的或应在实现阶段考虑的关联
- 瞬时事件
- 三元关联
- 派生关联
进一步完善:
- 正名
- 分解
- 补充
- 标明重数
划分主题
确定主题的方法如下
- 为每一个结构追加一个主题
- 为每一个对象追加一个主题
- 若当前主题的数目超过 7 个,则对已经存在的主题进行归并
确定属性
- 分析
- 选择
删除掉不确定和不必要的属性。主要考虑以下一些情况:
- 误把对象当作属性
- 误把关联类的属性当作一般对象的属性
- 把限定误当成属性
- 误把内部状态当成子属性。如果某个性质是对象的非公开的内部状态,则应该从对象模型中删去这个属性。
- 过于细化
- 存在不一致的属性
识别继承关系
- 自底向上
- 自顶向下
⭐建立动态模型
建立动态模型的第一步是编写典型交互行为的脚本。虽然脚本中不可能包括每个偶然事件,但是,必须保证不遗漏常见的交互行为。第二步,从脚本中提取出事件,确定触发每个事件的动作对象以及接受事件的目标对象。第三步,排列事件发生的次序,确定每个对象可能有的状态以及状态间的转换关系,并用状态图描绘它们。最后,比较各个对象的状态图,检查它们之间的一致性,确保事件之间的匹配。
- 编写脚本
- 设想用户界面
- 绘制事件跟踪图
- 绘制状态图
- 审查动态模型
编写脚本
编写脚本的目的是保证不遗漏重要的交互步骤,它有助于确保整个交互过程的正确性和清晰性。
编写脚本时,首先编写正常情况下的脚本。然后,考虑特殊情况,例如输入或输出的数据为最大值(或最小值)。最后,考虑出错情况。例如,输入的值为非法值或响应失败。在编写脚本的过程中,需要与用户充分交换意见,编写后还应该经过他们审查与修改。
绘制事件跟踪图
- 确定事件
- 画出事件跟踪图
应该仔细分析每个脚本,以便从中提取出所有外部事件。事件包括系统与用户(或外部设备)交互的所有信号、输入、输出、中断和动作等。从脚本中容易找出正常事件,但是,应该小心仔细,不要遗漏了异常事件和出错事件。一类事件相对它的发送对象来说是输出事件,但是相对它的接受对象来说则是输入事件。有时一个对象把事件发送给自己,在这种情况下,该事件既是输出事件又是输入事件。
绘制状态图
状态图描绘事件与对象状态的关系。当对象接受了一个事件以后,它的下一个状态取决于当前状态及所接受的事件。
审查动态模型
各个类的状态图通过共享事件合并起来,就构成了系统的动态模型。在完成了每个具有重要交互行为的对象的状态图之后,应该检查系统一级的完整性和一致性。一般来说,每个事件都应该既有发送对象又有接受对象,当然,有时发送者和接受者是同一个对象。对于没有前驱或后继的状态应该重点审查,如果这个状态不是交互序列的起点或终点,则表明发现了一个错误。应该认真审核每个事件,跟踪它对系统中各个对象所产生的效果,以保证这些事件与每个脚本相匹配。
从脚本中提取各类事件并确定每类事件的发送对象和接受对象。在事件跟踪图中,一条竖线代表一个对象,每个事件用一条水平的箭头线表示,箭头方向从事件的发送对象指向接受对象。仅考虑事件跟踪图中指向某条竖线的那些箭头线。把这些事件作为状态图中的有向边(即箭头线),边上标出事件名。两个事件之间的间隔就是一个状态。考虑完正常事件之后再考虑边界情况和特殊情况,当状态图覆盖了所有脚本,包含了影响某类对象状态的全部事件时,该类的状态图就构造出来了。
⭐建立功能模型
功能模型表达的是系统内部数据流的传送和处理的过程。功能模型由一组数据流图组成。在面向对象的开发方法中,采用功能模型的形式描述系统做什么。建立功能模型有助于软件开发人员更深入地理解问题域,改进和完善自己的设计。通常在建立对象模型和动态模型之后再建立功能模型。
建模顺序:对象模型->动态模型->功能模型
- 绘制基本系统模型图
- 绘制功能级数据流图
- 描述处理框功能
基本系统模型由若干数据源点、终点和一个逻辑处理框构成,这个处理框代表了系统加工、变换数据的整体功能。基本系统模型指明了目标系统的边界,由数据源点输入的数据和输出到数据终点的数据,是系统与外部世界之间的交互事件的参数。
⭐面向对象设计准则、启发规则
系统设计确定实现系统的策略和目标系统的高层结构。
面向对象设计的准则
- 模块化
- 抽象化
- 信息隐藏和封装
- 对象的高内聚和弱耦合
- 可扩充性
- 可重用性
抽象化
面向对象方法不仅支持过程抽象,而且支持数据抽象。类实际上是一种抽象数据类型,它对外开放的公共接口构成了类的规格说明(即协议),这种接口规定了外界可以使用的合法操作符,利用这些操作符可以对类实例中包含的数据进行操作。使用者无须知道这些操作符的实现算法和类中数据元素的具体表示方法,就可以通过这些操作符使用类中定义的数据。通常把这些类抽象称为规格说明抽象。此外,某些面向对象的程序设计语言还支持参数化抽象。所谓参数化抽象,是指当描述类的规格说明时并不具体指定所要操作的数据类型,而是把数据类型作为参数。这使得类的抽象程度更高,应用范围更广,可重用性更高。
信息隐藏和封装
在面向对象方法中,信息隐藏是通过对对象的封装来实现的。类和对象在构造中将接口与事件过程分离,从而支持了实现过程信息的隐蔽。封装是一种数据的构造方式,它从手段上保证了对象的数据结构和服务实现的隐蔽。
对象的高内聚和弱耦合
内聚与耦合是软件设计中评价模块独立性(即模块划分的质量)的指标。在面向对象方法中,对象和类成为基本模块,因此,模块内聚就是指一个对象或类中其内部属性和服务相互联系的紧密程度。
在对象或类中存在 3种不同类型的内聚:
- 服务内聚,一个服务应该且仅完成一个功能。
- 类内聚,类的构造原则是,一个类应该只有一个用途,其属性和服务应该是高内聚的,类属性和服务应该是完成该类承担的任务所必需的,其中不应该有与任务无关的属性或服务。如果某个类有多个用途,通常应该把它分解成多个专用的类。
- 一般—特殊内聚,设计出的一般—特殊结构,应该符合多数人的概念,更准确地说,这种结构应该是对响应的领域知识的正确抽取。
耦合是指一个软件结构内不同模块之间相互联系的紧密程度。在面向对象方法中是最基本的模块。因此,耦合主要是指不同对象之间相互关联的紧密度。弱耦合是优秀设计的一个重要标准,这有助于使得系统中某一部分的变化对其他部分的影响降到最低程度。在理想情况下,对某一部分的理解、测试或修改,无须涉及系统的其他部分。
对象之间的耦合可分为两大类:交互耦合、继承耦合
可扩充性
面向对象易扩充设计,继承机制以两种方式支持扩充设计。第一,继承关系有助于复用已有定义,使开发新定义更加容易。随着继承结构的逐渐变深,新类定义继承的规格说明和实现的量也就逐渐增大。这通常意味着,当继承结构增长时,开发一个新类的工作量反而逐渐减少。第二,在面向对象的语言中,类型系统的多态性也支持可扩充的设计。
启发规则
| 启发规则 | 解释 |
|---|---|
| 设计结果应该清晰易懂 | (1)用词一致(2)使用已有的协议(3)减少消息模式的数目(4)避免模糊的定义 |
| 一般—特殊结构的深度应适当 | 应该使类等级中包含的层次数适当。一般来说,在一个中等规模(大约包含 100 个类)的系统中,类等级层次数应保持为 7±2。不应该仅仅从方便编码的角度出发随意创建派生类,应该使一般—特殊结构与领域知识或常识保持一致。 |
| 设计简单的类 | 应该尽量设计小而简单的类,以便于开发和管理 |
| 使用简单的协议 | 一般来说,消息中的参数不要超过 3 个。当然,不超过 3 个的限制也不是绝对的,但是,经验表明,通过复杂消息相互关联的对象是紧耦合的,对一个对象的修改往往导致其他对象的修改。 |
| 使用简单的服务 | 面向对象设计出来的类中的服务通常都很小,一般只有 3~5 行源程序语句,可以用仅含一个动词和一个宾语的简单句子描述它的功能。如果一个服务中包含了过多的源程序语句,或者语句嵌套层次太多,或者使用了复杂的CASE语句,则应该仔细检查这个服务,设法分解或简化它。 |
| 把设计变动减至最小 |
软件类构建和重用的效益
类构件
| 类构件 | 说明 |
|---|---|
| 可重用软件构件应具备的特点 | (1)模块独立性强 (2)具有高度可塑性 (3)接口清晰、简明、可靠 |
| 类构件的重用方式 | (1) 实例重用 (2)继承重用(3) 多态重用 |
继承重用:面向对象方法特有的继承性,提供了一种对已有的类构件进行裁剪的机制。当已有的类构件不能通过实例重用完全满足当前系统需求时,继承重用提供了一种安全地修改已有类构件的方法,以便在当前系统中重用的手段。
为充分实现多态重用,在设计类构件时,应把注意力集中在下列一些可能影响重用性的操作上:
- 与表示方法有关的操作。例如,不同实例的比较、显示、擦除等。
- 与数据结构、数据大小等有关的操作。
- 与外部设备有关的操作。例如,设备控制。
- 实现算法在将来可能会改进(或改变)的核心操作。
软件重用的效益
- 质量
- 生产率
- 成本
软件重用是指在软件开发过程中重复使用相同或相似的软件元素的过程。
软件子系统之间的交互方式、组织系统方案和拓扑结构
| 子系统之间的两种交互方式 | 说明 |
|---|---|
| 客户—供应商关系 | 在这种关系中,作为“客户”的子系统调用作为“供应商”的子系统,后者完成某些服务工作并返回结果。使用这种交互方案,作为客户的子系统必须了解作为供应商的子系统的接口,然而后者却无须了解前者的接口,因为任何交互行为都是由前者驱动的。 |
| 平等伙伴关系 | 在这种关系中,每个子系统都能调用其他子系统,因此,每个子系统都必须了解其他子系统的接口。由于各个子系统需要相互了解对方的接口,因此这种组织系统的方案比起客户—供应商方案来,子系统之间的交互更复杂,而且这种交互方式还可能存在通信环路,从而使系统难于理解,容易发生不易察觉的设计错误。 |
组织系统的两种方案
- 水平层次组织
- 垂直块状组织
所谓封闭式,就是每层子系统仅仅使用其直接下层提供的服务。由于一个层次的接口只影响与其紧相邻的上一层,因此,这种工作模式降低了各层次之间的相互依赖性,更容易理解和修改。但开放模式的系统不符合信息隐藏原则,对任一个子系统的修改都会影响处在更高层次的那些子系统。
设计系统的拓扑结构:由子系统组成完整的系统时,典型的拓扑结构有管道形、树形、星形等。设计者应该采用与问题结构相适应的、尽可能简单的拓扑结构,以减少子系统之间的交互数量。
设计问题域子系统、人机交互子系统、任务管理子系统和数据管理子系统
设计问题域子系统
- 按照需求信息的最新变动调整并修改模型
- 调整和组合问题域中的类
- 调整对象模型中的继承的支持级别
- 改进系统的性能
- 增加底层细节
设计人机交互子系统
| 设计人机交互子系统 | 说明 |
|---|---|
| 设计人机交互界面的准则 | (1)一致性 (2) 减少步骤 (3) 及时提供反馈信息 (4)提供“撤销”命令 (5)无须记忆 (6)易学 |
| 设计人机交互子系统的策略 | (1) 分类用户 (2) 描述用户 |
设计任务管理子系统
- 确定事件驱动型任务
- 确定时钟驱动型任务
- 确定优先任务和关键任务:(1)高优先级 (2)低优先级
- 确定协调任务
- 尽量减少任务数
- 确定资源需求
设计数据管理子系统
- 选择数据存储管理模式:(1)文件管理系统 (2) 关系数据库管理系统 (3) 面向对象数据库管理系统
- 设计数据管理子系统:
- 设计数据格式。(1) 文件系统 (2) 关系数据库管理系统 (3)面向对象数据库管理系统
- 设计相应的服务
- 介绍使用不同数据存储管理模式时的设计要点。(1)文件系统 (2)关系数据库管理系统 (3)面向对象数据库管理系统
确定类中应有的服务、设计实现服务的方法
确定类中应有的服务
需要综合考虑对象模型、动态模型和功能模型,才能正确确定类中应有的服务。下列规则有助于确定操作的目标对象:
- 如果某个处理的功能是从输入流中抽取一个值,则该输入流就是目标对象。
- 如果某个处理具有类型相同的输入流和输出流,而且输出流实质上是输入流的另一种形式,则该输入/输出流就是目标对象。
- 如果某个处理从多个输入流得出输出值,则该处理是输出类中定义的一个服务。
- 如果某个处理把对输入流处理的结果输出给数据存储或动作对象,则该数据存储或动作对象就是目标对象。
设计实现服务的方法
- 设计实现服务的算法
- 设计实现服务的算法时,应该考虑下列 3 个因素
- 算法复杂度,通常选用复杂度较低(即效率较高)的算法,但是也不要过分追求高效率,应该以能满足用户需求为准。
- 容易理解与容易实现,容易理解与容易实现的要求往往与高效率有矛盾,设计者应该对这两个因素适当折中。
- 易修改,应该尽可能预测将来可能做的修改,并在设计时预先做些准备。
- 选择数据结构
- 定义内部类和内部操作
⭐设计类的关联
关联:在对象模型中,关联是连接不同对象的纽带,它指定了对象相互间的访问路径。
- 关联的遍历
- 实现单向关联
- 实现双向关联
- 关联对象的实现
在应用系统中,使用关联有两种可能的遍历方式:单向遍历和双向遍历。在应用系统中,某些关联只需要单向遍历,这种单向关联实现起来比较简单,另外一些关联可能需要双向遍历,双向关联实现起来稍微麻烦一些。
实现双向关联有下列 3 种方法:
- 只用属性实现一个方向的关联,当需要反向遍历时,就执行一次正向查找。如果两个方向遍历的频度相差很大,而且需要尽量减少存储开销和修改时的开销,则这是一种很有效的实现双向关联的方法。
- 两个双向的关联都用属性实现。
- 用独立的关联对象实现双向关联。关联对象不属于相互关联的任何一个类,它是独立的关联类的实例。
可以引入一个关联类来保存描述关联性质的信息,关联中的每个连接对应着关联类的一个对象。实现关联对象的方法取决于关联的重数。对于一对一关联来说,关联对象可以与参与关联的任一个对象合并。对于一对多关联来说,关联对象可以与“多”端对象合并。如果是多对多关联,则关联链的性质不可能只与一个参与关联的对象有关,通常用一个独立的关联类来保存描述关联性质的信息,这个类的每个实例表示一条具体的关联链及该链的属性。
设计优化
- 确定优先级
- 提高效率的技术
- 调整继承关系
系统的各项质量指标并不是同等重要的,设计人员必须确定各项质量指标的相对重要性(即确定优先级),以便在优化设计时指定折中方案。系统的整体质量与设计人员所制定的折中方案密切相关。最终产品成功与否,在很大程度上取决于是否选择好了系统目标。若没有站在全局的高度正确确定各质量指标的优先级,系统中各子系统则会按照相互对立的目标做优化,导致系统资源严重浪费。
提高效率的技术:
- 增加冗余关联以提高访问效率,提高访问效率的一种方法是使用哈希表
- 调整查询次序,优化算法的一个途径是尽量缩小查找范围。
- 保留派生属性,避免重复计算复杂表达式所带来的开销。
索引也必然带来开销:占用内存空间,而且每当修改其关联时也必须相应地修改索引。因此,应该只给那些经常执行并且开销大、命中率低的查询建立索引。
与建立类继承有关的问题:
- 抽象与具体
- 首先创建一些满足具体用途的类,然后对它们进行归纳,如果在一组相似的类中存在公共的属性和公共的行为,则可以把这些公共的属性和行为抽取出来放在一个共同的祖先类中,供其子类继承。在对现有类进行归纳的时候,要注意下述两点:(1)不能违背领域知识和常识。(2)应该确保现有类的协议(同外部世界的接口)不变。
- 利用委托实现行为共享
- 有时程序员只想用继承作为实现操作共享的一种手段,并不打算确保基类和派生类具有相同的行为。在这种情况下,如果从基类继承的操作中包含了子类不应有的行为,则可能引起麻烦。
- 如果只想把继承作为实现操作共享的一种手段,则利用委托(即把一类对象作为另一类对象的属性,从而在两类对象间建立组合关系)也可以达到同样目的,而且这种方法更安全。
- 【例题】在实现Stack类时,如果类库中已经有一个List类,但Stack类只需要List类的部分操作,且不希望继承不必要的操作,应该采用什么方法?(利用委托实现行为共享)
设计模式的概念和常用设计模式
设计模式的概念
设计模式使人们可以更加简单方便地复用成功的设计和体系结构。设计模式帮助做出有利于系统复用的选择,避免设计损害系统复用性。通过提供一个显式类和对象作用关系以及它们之间潜在联系的说明规范,设计模式甚至能够提高已有系统的文档管理和系统维护的有效性。简而言之,设计模式可以帮助设计者更快更好地完成系统设计。
一般而言,一个模式有 4 个基本要素:
- 模式名称(Pattern Name),一个助记名,它用一两个词来描述模式的问题、解决方案和效果。
- 问题(Problem),描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题。
- 解决方案(Solution),描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。
- 效果(Consequences),描述了模式应用的效果及使用模式应权衡的问题。在Smalltalk-80 中,类的模型/视图/控制器三元组(Model/View/Controller,MVC)被用来构建用户界面。MVC包括三类对象。模型Model是应用对象,视图 View是它在屏幕上的表示,控制器Controller定义用户界面对用户输入的响应方式。不使用MVC,用户界面设计往往将这些对象混在一起,而MVC则将它们分离以提高灵活性和复用性。
为了达到设计复用,我们必须同时记录设计产生的决定过程、选择过程和权衡过程。具体的例子也是很重要的,它们让你看到实际的设计。用统一的格式描述设计模式,每一个模式根据以下的模板被分成若干部分。模板具有统一的信息描述结构,有助于你更容易地学习、比较和使用设计模式。
面向对象语言的优点及选择原则
面向对象语言的优点
- 使用一致的表示方法
- 广泛运用重用机制
- 便于维护
面向对象语言的选择原则
- 选择将来能占主导地位的语言。
- 考虑具有良好的类库和开发环境的语言。
- 考虑其他因素:为用户学习面向对象分析、设计和编码技术所能提供的培训服务;在使用这个面向对象语言期间能提供的技术支持;能提供给开发人员使用的开发工具、开发平台和发行平台,对机器性能和内存的需求,集成已有软件的难易程度等。
面向对象方法开发软件的一个主要优点是通过可重用性提高软件生产率。决定可重用性的因素,不仅仅需要选用能够最完整、最准确地表达问题域语义的面向对象语言,开发环境和类库也是非常重要的因素。事实上,语言、类库和开发环境这三个因素综合起来,共同决定了可重用性。
⭐面向对象程序设计风格
- 提高可重用性
- 提高可扩充性
- 提高稳健性
提高可重用性
| 提高可重用性 | 说明 |
|---|---|
| 提高方法的内聚、降低耦合 | 一个方法应该只完成单个功能,如果某个方法涉及两个或多个不相关的功能,则应该把它分解成几个更小的方法。 |
| 减小方法的规模 | 如果某个方法规模过大,则应该把它分解成几个更小的方法。 |
| 保持方法的一致性 | 保持方法的一致性,有助于实现代码重用。功能相似的方法应该有一致的名字、参数特征、返回值类型、使用条件及出错条件等。 |
| 尽量做到全面覆盖 | 则应该针对所有组合写出方法,还应该考虑到一个方法不能只是处理正常值,也要处理空值、极限值及界外值等异常情况。 |
| 分开策略方法和实现方法 | 根据完成的功能的不同,方法分为两种:一类是策略方法,这类方法负责做出决策,提供变元,管理全局资源;另一类是实现方法,这类方法只负责完成具体的操作,不做出任何决策,也不管理资源。 |
| 尽量不使用全局信息 | 降低方法与外界的耦合程度 |
| 利用继承机制 | (1) 调用公共代码 (2) 调用分解因子 (3)使用委托机制 (4)把代码封装在类中 |
提高可扩充性
| 提高可扩充性 | 说明 |
|---|---|
| 封装实现策略 | 应该把类的实现策略(包括描述属性的数据结构、修改属性的算法等)封装起来,对外只提供公有的接口,否则将降低今后修改数据结构或算法的自由度。 |
| 慎用公有方法 | |
| 不要用一个方法遍历多条关联链 | 一个方法应该只包含对象模型中的有限内容。违反这条准则将导致方法过分复杂,既不易理解,也不易修改扩充。 |
| 避免使用多分支语句 |
提高稳健性
为了提高健壮性应该遵循以下 4 条准则:
- 具备处理用户操作错误的能力
- 检查参数的合法性
- 不要预先确定限制条件
- 先测试后优化
面向对象测试模型及面向对象各阶段的测试方法
面向对象测试模型:面向对象分析的测试、面向对象设计的测试、面向对象编程的测试、面向对象的单元测试、面向对象的集成测试、面向对象的系统测试
面向对象分析的测试
- 对认定的对象的测试
- 对认定的结构的测试
- 对认定的主题的测试
- 对定义的属性和实例关联的测试
- 对定义的服务和消息关联的测试
面向对象设计的测试
- 对认定的类的测试
- 对构造的类层次结构的测试
- 对类库的支持的测试
面向对象编程的测试
面向对象程序是把功能的实现分布在类中。能正确实现功能的类,通过消息传递来协同实现设计要求的功能。因此,在面向对象编程(OOP)阶段,忽略类功能实现的细则,将测试的目光集中在类功能的实现和相应的面向对象程序风格,主要体现为以下两个方面。
- 数据成员是否满足数据封装的要求
- 类是否实现了要求的功能
面向对象的集成测试
传统的集成测试是通过自底向上或自顶向下集成完成功能模块的集成测试,一般可以在部分程序编译完成以后进行。但对于面向对象程序,相互调用的功能是分布在程序的不同类中,类通过消息相互作用申请并提供服务。类相互依赖极其紧密,根本无法在编译不完全的程序上对类进行测试。所以,面向对象的集成测试通常需要在整个程序完成编译以后进行。此外,面向对象的集成测试需要进行两级集成:一是将成员函数集成到完整类中;二是将类与其他类集成。
面向对象的系统测试
不论是传统的测试方法还是面向对象的测试方法,都应该遵循下列的原则:
- 应当尽早和不断地测试。
- 程序员应避免检查自己的程序,测试工作应该由独立的专业的软件测试机构来完成。
- 设计测试用例时,应该考虑到合法的输入和不合法的输入,以及各种边界条件,特殊情况下要制造极端状态和意外状态,比如网络异常中断、电源断电等情况。
- 注意测试中的错误集中现象,这和程序员的编程水平以及习惯有很大的关系。
- 对测试错误结果一定要有一个确认的过程。一般由A测试出来的错误,一定要由一个B来确认,严重的错误可以召开评审会进行讨论和分析。
- 制订严格的测试计划,并把测试时间安排得尽量宽松,不要希望在极短的时间内完成一个高水平的测试。
- 回归测试的关联性一定要引起充分的注意。修改一个错误而引起更多错误出现的现象并不少见。
- 妥善保存一切测试过程中的文档,测试的重现性要以测试文档为依据。
类内测试及类间测试用例的设计
设计测试用例有以下 3 个要点:
- 应该唯一标识每一个测试案例,并且与被测试的类明显地建立关联。
- 陈述测试对象的一组特定状态。
- 对每一个测试建立一组测试步骤,要思考或确定的问题包括:对被测试对象的一组特定状态、一组消息和操作。考虑当对象测试时可能产生的一组异常、一组外部条件、辅助理解和实现测试的补充信息。
设计类测试用例
- 类级随机测试
- 随机测试是针对软件在使用过程中随机产生的一系列不同的操作序列设计的测试案例,可以测试不同的类实例生存历史。
- 类级划分测试
- 划分测试方法与传统软件测试采用的等价划分方法类似,减少了测试类所需要的测试用例的数量。首先,把输入和输出分类,然后为测试划分出来的每个类别设计测试用例。
- 类级基于故障的测试
- 基于故障的测试与传统的错误测试推测法类似。首先,推测软件中可能有的错误,然后,设计出最可能发现这些错误的测试案例。为了推测出软件中可能存在的错误,应该仔细研究分析模型和设计模型,很大程度上要依靠测试人员的经验。
测试类间测试用例
从面向对象的集成测试开始,设计测试用例就要考虑类间的协作,通常可以从OOA的类—关系模型和类—行为模型中导出类间测试用例。类间测试方法有随机测试方法、划分测试方法、基于场景的测试和行为测试。
- 基于场景的测试
- 基于场景的测试关注的是用户做什么,这正是基于故障测试所忽略的,即不正确的归约和子系统间的交互。
- 行为测试
- 行为测试即从动态模型导出测试用例。用状态转换图作为表示类的动态行为模型,类的状态图可以导出测试该类的动态行为的测试用例。设计的测试用例,一方面应该覆盖所有状态,另一方面应该导出足够的测试用例,以保证该类的所有行为都被适当地测试过。
软件工程标准化的概念、类型和意义
软件工程标准化的概念
标准:对重复性的事物和概念所做的统一规定。
标准化:是指在经济、技术、科学及管理等社会实践中,对重复性事物的概念通过制定、发布和实施标准达到统一,以获得最佳秩序和社会效益的活动。它是一门综合性学科,具有综合性、政策性和统一性的特点。
⭐软件工程标准化的类型及意义
所有这些都要求提供统一的行动规范和衡量准则,使得各种工作都能有章可循。
软件工程标准的类型也是多方面的,它可能包括过程标准(如方法、技术、度量等)、产品标准(如需求、设计、部件、描述、计划、报告等)、专业标准(如职别、道德准则、认证、特许、课程等)以及记法标准(如术语、表示法、语言等)。
意义:
- 提高软件可靠性、可维护性和可移植性
- 提高软件的生产率,提高软件人员的技术水平
- 提高软件人员之间的通信效率,减少差错和误解
软件工程标准的制定与推行
软件工程标准的制定与推行通常要经历一个环状的生命期。最初,制定一项标准仅仅是初步设想,经发起后沿着环状生存周期,顺时针进行要经历以下的步骤。
- 建议:拟订初步的建议方案。
- 开发:制定标准的具体内容。
- 咨询:征求并吸收有关人员意见。
- 审批:由管理部门决定能否推出。
- 公布:公开发布,使标准生效。
- 培训:为推行准备人员条件。
- 实施:投入使用,需经历一定的期限。
- 审核:检验实施效果,决定修订还是撤销。
- 修订:修改其中不适当的部分,形成标准的新版本,进入新的周期。
其中影响软件工程标准顺利实施的一些不利因素应当特别引起重视。这些因素可能有以下 5 种。
- 标准本身制定得有缺陷,或是存在不够合理、不够准确的部分。
- 标准文本编写有缺点,如文字叙述可读性差、理解性差,或是缺少实例供读者参阅。
- 主管部门未能坚持大力推行,在实施的过程中遇到问题未能及时加以解决。
- 未能及时做好宣传、培训和实施指导。
- 未能及时修订和更新。
软件工程标准的层次和体系框架
软件工程标准的层次
- 国际标准
- 国家标准
- 行业标准
- 企业标准
- 项目规范
中国的软件工程标准化工作
- 基础标准
- 开发标准
- 文档标准
- 管理标准
ISO9000国际标准介绍
⭐软件文档的作用和分类
软件文档(Document)也称文件,通常指的是一些记录的数据和数据媒体,它具有固定不变的形式,可被人和计算机阅读。
文档的作用
- 提高软件开发过程的能见度
- 管理人员可把这些记载下来的材料作为检查软件开发进度和开发质量的依据,实现对软件开发的工程管理
- 提高开发效率
- 作为开发人员在一定阶段的工作成果和结束标志
- 记录开发过程中有关信息,便于协调以后的软件开发、使用和维护
- 提供对软件的运行、维护和培训的有关信息,便于管理人员、开发人员、操作人员、用户之间协作、交流和了解。使软件开发活动更科学、更有成效。
- 便于潜在用户了解软件的功能、性能等各项指标,为他们选购符合自己需要的软件提供依据。
软件文档编制的质量要求
高质量的文档应当体现在以下一些方面:
- 针对性
- 精确性,文档的行文应当十分确切,不能出现多义性的描述。同一课题若干文档内容应是一致的。
- 清晰性
- 完整性,任何一个文档都应当是完整的、独立的,它应自成体系。
- 灵活性
- 可追溯性
软件文档的管理和维护
以下 6 个方面是应注意做到的:
- 软件开发小组应设一位文档保管人员,负责集中保管本项目已有文档的两套主文本。
- 软件开发小组的成员可根据工作需要在自己手中保存一些个人文档。
- 开发人员个人只保存着主文本中与他工作相关的部分文档。
- 在新文档取代了旧文档时,管理人员应及时注销旧文档。
- 项目开发结束时,文档管理人员应收回开发人员的个人文档。
- 在软件开发过程中,可能发现需要修改已完成的文档,特别是规模较大的项目,主文本的修改必须特别谨慎。
⭐软件质量的定义、特性
ANSI/IEEE Std 729—1983 定义软件质量为“与软件产品满足规定的和隐含的需求的能力有关的特征或特性的全体”
McCall等人定义的质量特性如下:
- 正确性
- 可靠性
- 效率
- 完整性
- 可使用性
- 可维护性
- 可测试性
- 灵活性
- 可移植性
- 可复用性
- 互连性
软件质量反映了以下三方面的问题:
- 软件需求是度量软件质量的基础。不符合需求的软件就不具备质量。
- 规范化的标准定义了一组开发准则,用来指导软件人员用工程化的方法来开发软件。如果不遵守这些开发准则,软件质量就得不到保证。
- 往往会有一些隐含的需求没有显式地提出来,如软件应具备良好的可维护性。如果软件只满足那些精确定义了的需求而没有满足这些隐含的需求,软件质量也不能保证。
ISO9162 定义的 6 个质量特性如下:
- 功能性(Functionality):是与一组功能及其指定的性质有关的一组属性,这里的功能是指满足明确或隐含的要求的那些功能。
- 可靠性(Reliability):是与在规定的一段时间和条件下,软件维持其性能水平的能力有关的一组属性。
- 可用性(Usability):是与一组规定或潜在用户为使用软件所需做的努力和对这样的使用所做的评价有关的一组属性。
- 效率(Efficiency):是在规定的条件下,软件性能水平与所使用的资源量之间的关系有关的一组属性。
- 可维护性(Maintainability):是与进行指定的修改所需的努力有关的一组属性。
- 可移植性(Portability):是与软件从某一环境转移到另一环境的能力有关的属性。
软件度量和软件质量的度量
软件度量的基本概念
软件度量是对软件开发项目、过程及其产品进行数据定义、收集以及分析的持续性定量化过程,目的在于对此加以理解、预测、评估、控制和改善。
软件产品度量方法(即软件质量度量方法)
软件产品度量用于对软件产品进行评价,并在此基础之上推进产品设计、产品制造和产品服务优化。软件产品的度量实质上是软件质量的度量。
软件质量度量的常用方法如下:
- Halstead复杂性度量法,基本思路是根据程序中可执行代码行的操作符和操作数的数量来计算程序的复杂性。操作符和操作数的量越大,程序结构就越复杂。
- McCabe复杂性度量法,其基本思想是程序的复杂性很大程度上取决于程序控制流的复杂性,单一的顺序程序结构最简单,循环和选择所构成的环路越多,程序就越复杂。
⭐软件质量的度量模型
- Boehm模型
- McCall模型
- ISO/IEC 9126 质量模型
软件质量保证的概念、任务和策略
软件质量保证的概念
质量保证是为保证产品和服务充分满足消费者要求的质量而进行的有计划、有组织的活动。
软件质量保证的主要任务
- 为项目制定SQA计划
- 参与开发该软件项目的软件过程描述
- 评审各项软件工程活动,核实其是否符合已定义的软件过程
- 审计指定的软件工作产品,核实其是否符合已定义的软件过程中的相应部分
- 确保软件工作及工作产品中的偏差已被记录在案,并根据预定规程进行处理
- 记录所有不符合部分,并向上级管理部门报告
软件质量保证的策略
- 以检测为重
- 以过程管理为重
- 以新产品开发为重
软件技术评审
技术评审(Technical Review,TR)的目的是尽早地发现工作成果中的缺陷,并帮助开发人员及时消除缺陷,从而有效地提高产品的质量。
技术评审有如下好处:
- 通过消除工作成果的缺陷而提高产品的质量。
- 技术评审可以在任何开发阶段执行,不必等到软件可以运行之际,越早消除缺陷,就越能降低开发成本。
- 开发人员能够及时地得到同行专家的帮助和指导,无疑会加深对工作成果的理解,更好地预防缺陷,一定程度上提高了开发生产率
技术评审有两种基本类型:
- 正式技术评审(FTR)。FTR较严格,需举行评审会议,参加评审会议的人员较多。
- 非正式技术评审(ITR)。ITR的形式较灵活,通常在同伴之间开展,不必举行评审会议,评审人员较少。
- 准备评审
- 评审主持人首先确定评审会议的时间、地点、设备和参加会议的人员名单(包括评审员、记录员、作者、旁听者等),并告知所有相关人员(例如Email)。
- 评审主持人把工作成果及相关材料、技术评审规程、检查表等发给评审员。
- 评审员阅读(了解)工作成果及相关材料。
- 举行评审会议
- 主持人宣讲本次评审会议的议程、重点、原则、时间限制等。
- 作者扼要地介绍工作成果。
- 评审员根据“检查表”认真查找工作成果的缺陷。作者回答评审员的问题,双方要对每个缺陷达成共识(避免误解)。
- 作者和评审员共同讨论缺陷的解决方案。对于当场难以解决的问题,由主持人决定“是否有必要继续讨论”或者“另定时间再讨论”。
- 评审小组给出评审结论和意见,主持人签字后本次会议结束。评审结论有 3 种。
- 工作成果不合格,需要做比较大的修改,之后必须重新对其评审。
- 工作成果合格,“无须修改”或者“需要轻微修改但不必再审核”。
- 工作成果基本合格,需要作少量的修改,之后通过审核即可,转向第3 步
- 跟踪与审核
- 作者修正工作成果,消除已发现的缺陷。评审主持人(或者指定审查员)跟踪每个缺陷的状态。直到工作成果合格为止。
软件产品质量管理的特点、指导思想和管理体系
软件产品质量管理的特点
- 软件质量管理应该贯穿软件开发的全过程,而不仅仅是软件本身。
- 对开发文档的评审是产品检验的重要方式。
- 通过技术手段保证质量。
软件质量管理的指导思想
- 缺陷预防
- 紧紧扣住用户需求
软件质量管理体系
- 基于CMM的质量管理体系
- ISO基于ISO 9000 的质量管理体系
⭐软件工程项目管理的概念
软件项目管理的特点、主要职能和主要内容
软件项目管理的特点
- 软件产品是不可见的
- 软件过程没有统一的标准
- 大型软件项目常常是“一次性的”
软件项目管理的主要职能
- 制订计划
- 建立组织
- 配备人员
- 指导
软件项目管理的主要内容
软件项目管理的内容主要包括如下 7 个方面:人员的组织与管理、软件度量、软件项目计划、风险管理、软件质量保证、软件过程能力评估、软件配置管理
软件项目管理活动
- 计划项目
- 项目组织
- 控制项目
- 软件质量保证(Software Quality Insurance,SQA)是在软件过程中的每一步都进行的“保护性活动”。
- 软件配置管理(Software Configuration Management,SCM)是应用于整个软件过程中的保护性活动,它是在软件整个生存周期内管理变化的一组活动。
- 终结项目
【在软件项目管理中,项目终结的过程包括哪些级别?】第 1 级,没有正式结束所有交接的终结过程,无合同执行结束过程,工程文件记载没有收集,没有分类,更没有储存。第 2 级,明确了非正式的结束过程,整个项目管理过程的关键性技术和学习及质量进行了非正式审议。第 3 级,完成所有的终结活动,工程文件已经储存,并得以管理。项目团队积极参与,对项目管理方法提出总结建议,并将项目管理最好的方法编制成文。第 4 级,合同执行终结,管理执行终结,工程文件编制并综合集成。第 5 级,项目终结过程得到优化,项目管理过程得以持续改进。
基于CASE技术的开发工具
- 图形工具:绘制结构图、系统专用图。
- 代码生成器:从原型系统的工具中自动产生可执行代码。
- 测试工具:测试用例生成工具、调试程序。
CASE技术是软件工具和软件方法的结合。它不同于以前的软件技术,因为它强调了解决整个软件开发过程的效率问题,而不仅仅是实现阶段。
Rational Rose
Rational Rose包括一体化建模语言UML、OOSE及OMT。
Rose工具集中体现了当代软件开发的先进思想,把面向对象的建模和螺旋上升式的开发过程相结合,支持最新的UML标准。并且,Rose工具中集成了许多的软件技术,支持团队开发,支持代码的自动生成(如Java 、C++、VB、Oracle等的代码生成),与多种外部程序相联系,如版本管理程序等,为软件系统的开发提供了一个全面的支持环境。Rose的逆向工程功能可以保证模型和代码的一致性,对软件工程的全过程进行支持,包括系统建模、模型集成、代码生成、软件系统测试、软件文档生成、逆向工程、软件开发的项目管理、团队开发管理等。
PowerDesigner
业务或系统分析人员、设计人员、数据库管理员DBA和开发人员可以对其裁剪以满足他们的特定需要。模块化的结构为购买和扩展提供了极大的灵活性。Power Designer灵活的分析和设计特性允许使用一种结构化的方法有效地创建数据库或数据仓库,而不要求严格遵循一个特定的方法学。PowerDesigner提供了直观的符号表示使数据库的创建更加容易,并使项目组内的交流和通信标准化,同时能更加简单地向非技术人员展示数据库和应用的设计。Power Designer不仅加速了开发的过程,也向最终用户提供了管理和访问项目的信息的一个有效的结构。
⭐软件成本估算
软件开发成本主要是指软件开发过程中所花费的工作量及相应的代价。它不同于其他物理产品的成本,它不包括原材料和能源的消耗,主要是人的劳动消耗。人的劳动消耗所需代价是软件产品的开发成本
要注意到成本估算和预算之间的差别。成本估算作为预算的一个输入,预算各项作为预算的不同账目或报表的输入。预算要受到授权实体的审核,此实体是一个预算管理部门,或者是项目策略经理。作为一名项目经理,也许无法控制预算,但可以根据成本估算结果来控制预算输入。
- 代码行技术
- 功能点技术
代码行技术
代码行技术依据以往开发类似产品的经验和历史数据,估计实现一个功能所需要的源程序行数。当有以往开发类似产品的历史数据可以供参考时,用这种方法估计出的数值是比较准确的。
功能点技术
功能点技术依据对软件信息域特性和软件复杂性的评估结果估算软件规模,即用功能点(FP)为单位度量软件规模。
工作量是软件规模的函数,单位通常是人月(pm)
基本估算方法分为三类:
- 自顶向下的估算方法
- 这种方法的思想是从项目的整体出发,进行类推。即估算人员根据以前已完成项目所耗费的总成本(或工作总量),推算将要开发的软件总成本(或工作总量),然后按比例分配到各开发任务中去,再检验它是否能满足要求。这种方法的优点是估算量小、速度快;缺点是对项目中的特殊困难估计不足,估算出来的成本盲目性大,有时会遗漏被开发软件的某些部分。
- 自底向上的估算法
- 这种方法的想法是把待开发的软件细分,直到每一个子任务都已经明确所需要的开发工作量,然后把它们加起来,得到软件开发的总工作量。这是一种常见的估算方法。它的优点是估算各个部分的准确性高。缺点是缺少各项子任务之间相互联系所需要的工作量,还缺少许多与软件开发有关的系统级工作量(配置管理、质量管理和项目管理)。所以往往估计值偏低,必须用其他方法进行校验和校正。
- 差别估算法
- 这种方法综合了上述两种方法的优点,其方法是把待开发的软件项目与过去已完成的软件项目进行比较,不同的部分则采用相应的方法进行估算,这种方法的优点是可以提高估算的准确度,缺点是不容易明确“类似”的界限。
软件项目计划的制定、人员组织和管理
项目计划的制订
项目计划详细说明了所需软件工作及如何实现。它定义了每一个主要任务,并估算其所需时间和资源,同时为管理层的评估和控制提供了一个框架。项目计划应在项目开始初期制订出,并随着工程的进展不断地加以精化。起初,由于软件需求通常是模糊而又不完整的,人们的工作重点应在于明确该项目需要哪些领域的知识,并且如何获取这些知识。在每一次后续的需求精化时,也应同时精化资源映射,项目规模估算和工程进度。
人们着重强调对项目规模和资源的估算,是因为低质量的项目资源估算将不可避免地造成资源短缺、进度延迟和预算超支。
项目组人员组织与管理
- 项目组的组织结构
- 项目责任制度
- 人员少而精
- 程序设计小组的组织形式
- 主程序员组
- 主程序组由主程序员、程序员和后备工程师为核心组成。主程序员是经验丰富、能力强的高级程序员,负责小组全部技术活动的计划、协调与审查工作,并负责设计和实现项目中的关键部分。
- 民主小组
- 小组由经验丰富的技术人员组成。项目有关的所有重大决策都由全体成员集体讨论后决定。这种组织形式强调发挥每个成员的积极性,要求每个成员充分发挥主动精神和协作精神。通过充分讨论,也是在互相学习,因而在组内形成一个良好合作的工作气氛。
- 层次小组
- 小组内人员分为 3 级:组长、高级程序员和程序员。
⭐软件开发进度计划的制定
从众多的软件工程项目实施记录来看,软件开发计划的准确性很难把握,这有多种因素影响:首先,项目进度计划不切合实际;其次,在开发过程中,用户需求不断变更,并且这种变更的因素在制定计划的时候还没有考虑进去;再者,对可能的各种风险、工作量、资源数量估计不足,项目组成员之间沟通不畅等。所有这些因素可能导致项目延期。
⭐甘特图与时间管理
时间管理是在项目的执行和实施过程中进行的,通过经常性的检查进度是否按进度计划完成,来发现和纠正偏差,并找出原因,以便在短时间里找到解决的方法。
Gantt图是表示项目进度计划使用最广泛的工具之一
在Gantt图中,每一项任务的开始时间和结束时间先均用空心小三角表示,两者用横线相连,令人一目了然。当活动开始时,将横线左面的小三角涂黑,当活动结束时,再把横线右边的小三角涂黑。
Gantt图表示任务之间并行和串行关系,简单明了,易画、易读、易改,使用十分方便。由于图中显示了年、月,用它来检查工程完成的情况十分直观、方便。但是,它不能显示各项子任务之间的依赖关系,以及哪些是关键任务等。要弥补这一不足,可采用工程网络技术。
⭐工程网络与关键路径
关键路径是指一系列决定项目最早完成时间的事件链接。
画工程网络图的步骤:
- 计算最早时刻
- 计算最迟时刻
- 机动时间
⭐项目进度跟踪与控制
- 跟踪项目范围的状态
- 跟踪项目进度
- 分析变化
- 管理有效的范围变化
软件项目风险识别与分类
风险管理的主要目标是预防风险,但并非所有风险都能预防,因此,项目组还必须制订一个处理意外事件的计划,以便一旦风险变成现实时能够以可控的和有效的方式做出反应。
风险识别与分类:
- 需求风险
- 计划编制风险
- 组织和管理风险
- 人员风险
- 开发环境风险
- 客户风险
- 产品风险
- 设计和实现风险
- 过程风险
识别风险是风险管理的第一个阶段,这一阶段是要系统化地识别已知的和可预测的风险,在可能时免这些风险,且当必要时控制这些风险。
识别风险的一个方法是建立风险清单,清单上列举出在任何时候可能碰到的风险,最重要的是要对清单的内容随时进行维护,更新风险清单,并向所有的成员公开,应鼓励项目团队的每个成员勇于发现问题并提出警告。建立风险清单的一个办法是将风险输入缺陷追踪系统中,建立风险追踪工具,缺失追踪系统一般能将风险项目标示为已解决或尚待处理状态,也能指定解决问题的项目团队成员,并安排处理顺序。
软件项目风险评估与分析
在此,需强调的是如何评估风险的影响,如果风险真的发生了,它所产生的后果会受到 3 个因素影响:风险的性质、范围及时间。风险的性质是指当风险发生时可能产生的问题。风险的范围是指风险的严重性及其整体分布情况。风险的时间是指主要考虑何时能够感到风险及持续多长时间。
软件项目风险策划与管理
软件项目风险规避与监控
一旦建立了RMMM计划,就开始了风险缓解及监控,风险缓解是一种避免问题的活动,风险监控则是跟踪项目的活动。它有三个主要目的:评估一个被预测的风险是否真的发生了;保证为风险而定义的缓解步骤被正确地实施;收集能够用于未来的风险分析信息。
⭐软件配置的概念、任务及过程
在软件开发过程中,从开发环境的建立,到各阶段产生的大量文档、代码等,开发活动会需要一些必不可少的工具,不断地产生一些记录成果的文档以及源代码,这些内容构成了软件产品的主体内容和开发软件必不可少的东西,即软件配置项(Software Configuration Items,SCI)。
软件配置管理的任务
软件配置管理的任务就是在计算机软件的整个生命周期内管理变化
- 基线,IEEE把基线定义为:已通过正式复审的软件中间产品或软件文档,它可以作为进一步开发的基础,并且只有通过正式的变化控制过程才能改变它。 简而言之,基线就是通过了正式复审的软件配置项集合。
- 软件配置项
软件配置管理的过程:
- 标识软件配置中的对象
- 版本控制
- 变化控制
- 配置审计
- 状态报告
项目管理认证体系
UML的五大模型
会根据实际问题应用五大模型来描述,如用例图、类图、时序图等
⭐如何理解和应用五大模型。
用例与用例之间的关系:
- 扩展关系是指一个用例被定义为基础用例的增量扩展,这样通过扩展关系,就可以把新的行为插入到已有用例中。在UML语言中,扩展关系用虚箭头加
<<extend>>来表示,注意,箭头指向基础用例。 - 包含关系是指当存在若干用例共有的步骤序列,则可以将该序列抽取出来,形成一个子用例,以被基础用例调用。在UML语言中,包含关系用虚线箭头加
<<include>>表示,箭头所指向的是被包含的用例。 - 泛化关系是指一个用例也可以被特别细化为一个或多个子用例。任何子用例都可以用于其父用例能够应用的场合。在UML语言中,泛化关系用实线三角箭头表示,箭头从子用例指向父用例。



