概述
22级软件工程考试细节及复习相关问题见下面这篇帖子,作者自己复刻了一版真题吉林大学软件工程2025年期末真题(回忆复刻版)-CSDN博客
下面是作者复习时整理的笔记,放到csdn之后序号排版稍微有点乱
重点结构梳理
车海燕老师总结的考试重点,仅供参考
第一章
软件与软件危机涉及到的概念,软件危机的一些现象,软件工程的基本原则
软件的主要特点
软件生命周期的划分,软件定义,软件开发和软件的运行维护
定义阶段包括问题定义、可行性研究、需求分析
开发阶段包括软件设计(主体设计和详细设计)、实现(编码和测试)
运行维护
第二章 软件过程
各种模型:瀑布模型、V模型(简单了解)、快速原型模型、阶段式开发、螺旋模型、喷泉模型
RUP和敏捷过程(概念、核心思想、优点缺点)、微软过程(略过)
第三章 可行性研究
可行性研究:概念,技术、经济、操作、法律等方面的可行性
系统流程图不做要求,成本效益分析简单了解
第四章 需求分析(重点)
软件的需求、从哪几个方面、为什么重要、为什么获取困难
软件需求要满足的特征:正确性、完整性、可追溯性、可实现性、可测试性(不具有可测试性时该如何调整,量化)
数据流图:相关概念、分步骤绘制(基本系统模型、功能级的数据流图、细化的数据流图),填写缺失内容,需要会完整画最顶层的数据流图
数据流图注意事项:父子图的平衡、数据流的命名问题、什么时候使用数据存储文件
第五章 总体设计
设计原理和启发式规则(含义,重点回顾)
重点掌握7个不同程度的划分(内聚和耦合)
面向数据流的设计方法,变换流、事务流(映射过程特别注意)
体系结构风格(重点掌握管道过滤器、层次两种)
第六章 详细设计
过程设计(表达方式)
人机交互界面(重要性、考虑的问题)
程序复杂度的定量度量(会画流图)
第七章 编码和测试(重点)
编码风格
测试的原则和步骤
黑白盒测试(等价位划分、边界值分析)
基本路径测试法
集成测试(重点)
软件可靠性:计算平均故障时间MTTF
第八章 软件维护
维护的类型、影响
决定软件的可维护性有哪些
提高可维护性
软件开发的各个阶段都要做哪些事(维护性复审)
第九章 项目管理
工程网络图的绘制以及相关的关键路径,空余时间的计算
人员组织方式(两大)
软件配置管理(软件配置项、期限、如何处理流程变化)
第十章 面向对象
UML基础知识
需求建模+OOA
第一章 软件工程概论
一、软件的定义
软件=程序+数据+文档(逻辑实体)
程序:按实现设计的功能和性能要求执行的指令序列
数据:使程序正常操作信息的数据结构
文档:与程序开发、维护有关的图文材料
二、软件的特点
A. 逻辑实体而不是具体物理实体,有抽象性
B. 软件开发中无硬件那样明显的制造过程,对软件的质量控制必须着重在软件开发方面下功夫
C. 软件无硬件那样的机械磨损与老化问题
软件故障源于开发过程中存在错误,软件不存在磨损与老化,但存在退化,退化源于修改。
三、软件危机
1. 定义:计算机软件在开发和维护过程中所遇到的一系列严重问题
a. 如何开发软件,以满足日益增长的软件的需求;
b. 如何维护软件。
2. 软件危机的原因:
1. 软件本身的特点
软件的逻辑性
程序的复杂性、规模庞大
2. 软件开发和维护方法不正确
忽略软件定义时期的工作,特别是需求分析
认为软件开发就是写程序并使之运行
轻视维护
3. 消除软件危机的途径(正确认识、取长补短、更新换代):
A. 对软件有正确认识(软件定义)
B. 充分认识软件并不是一种个体劳动的神秘技巧而是一种组织良好、管理严密、各类人员协调配合共同完成的工程项目
C. 充分吸取借鉴人类长期以来在工程项目中积累的行之有效的原理、概念、技术和方法
D. 推广使用实践中总结出的开发软件成功的技术和方法,迭代方法,消除早期阶段的错误观念和做法
E. 开发和使用更好的软件工具
4. 软件产品质量的最理想情况是以最低成本满足所有要求
四、软件工程定义
软件工程是指导计算机软件开发和维护的一门工程学科。采用工程的概念、原理、技术和方法来开发与维护软件,把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来,以经济地开发出高质量的软件并有效地维护它,这就是软件工程
“软件工程”术语是在1968年NATO会议被首次提出
五、软件工程的本质特性
A. 软件工程关注大型程序构造(规模)
B. 软件工程中心课题是控制复杂性
C. 软件经常变化
D. 开发软件效率很重要
E. 和谐合作是开发软件的关键
F. 软件必须有效地支持它的用户(价值)
G. 软件工程领域中是由具有一种文化背景的人替具有另一种文化背景的人创造产品(两个空间)
六、七条原理
用分阶段的生命周期计划严格管理
坚持阶段评审(技术+管理)
实行严格的产品控制(实行基准配置管理)
现代化程序设计技术
结果应能清楚地审查(可见性+清晰的标准)
开发人员少而精(对应不能通过增加程序员数量来赶项目进度)
承认不断改进软件工程实践的重要性
七、软件工程基本内容
软件工程包括管理和技术两方面内容。
管理就是通过计划、组织和控制等一系列活动,合理地配置和使用各种资源,以达到既定目标的过程。
技术方面通常把软件生命周期全过程中使用的一整套技术方法的集合称为方法学(范型)。
八、软件工程方法学的三要素
方法:完成软件开发各项任务技术的方法,回答“How”
工具:软件工具为软件工程方法提供了自动或半自动的软件支撑环境
过程:为了获得高质量软件所需完成的一系列任务的框架,它规定了完成各项任务的步骤,将软件工程的方法和工具综合起来以达到合理、及时地进行软件开发的目的。
使用最广泛的软件工程学方法:
传统方法学:生命周期法/结构化范型(采用结构化技术,每一阶段技术审查和管理复审,存在语义断层问题,把数据和操作分开),不适用软件规模庞大、需求模糊或经常变化的情况
面向对象方法学:对象+类+继承+消息通信,以数据为主线,封装数据+操作,尽量模拟人类习惯的思维方式,使问题空间与求解空间在结构上尽可能一致
九、软件生命周期
软件定义
问题定义
可行性研究
需求分析
软件开发
总体设计
详细设计
编码和单元测试
综合测试(集成测试/验收测试/现场测试/平行运行)
运行维护
适应性维护
改正性维护
完善性维护
预防性维护
第二章 软件过程
一、重要概念
过程:产生某种预定输出的一系列可预测的步骤(包含一组活动、约束、资源要素) 或使用资源将输入转化为输出的活动所构成的系统
过程的特性:
使用一定资源、受一定限制,生成一定中间及最终产品
过程包含的活动事先都规定好
过程中每个活动的开始、结束有明确的规定
每项活动都有相应指导规则,用以明确目标
各活动以顺序组织
过程可能包括若干相关关联子过程
过程中的活动、资源以及产品都可能受约束
为什么使用过程:
能保证各活动之间是有组织和一致的
可被检查、理解、控制和改进
过程也是传授经验的一种方式
软件过程:为建造高质量软件所需完成任务的框架,它规定了完成各项任务的工作步骤
软件过程与软件工程的关系:
软件工程实践应该是由有创造力、有知识的人在定义好的、成熟的软件过程中进行的,该过程适合于他们建造的产品和他们的市场需要
软件过程定义了软件开发中采用的方法,但软件工程还包含该过程中应用的其他技术和工具
当谈及的过程涉及到某种产品的建造时,常把过程称作为生命周期
软件过程与软件生命周期的关系:
软件过程是在软件生命周期中所实施的一系列活动的集合
软件生命周期与选择的软件过程有关,不同的软件过程可能对应不同的软件生命周期
二、瀑布模型
瀑布模型的特点:
阶段间具有顺序性和依赖性
尽量推迟物理实现
质量保证:每个阶段都必须完成规定的文档,并对文档评审
瀑布模型的优点:
可强迫开发者采用规范的方法
严格规定每阶段必须提交文档
每阶段的产品都经过质量保证小组验证
实质是一种文档驱动的模型(既是优点也是缺点)
瀑布模型的缺点:
要求用户不经过实践就直接提出完整准确的需求,大多数情况下不切实际
仅通过纸面上的规格说明(静态)很难完整正确地认识软件产品(动态)
软件开发过程被强行线性化,不符合非线性化的开发实际
软件开发耗时长,可运行的软件版本在后期才会得到,一旦出问题,代价巨大
“文档驱动”可能最终开发出的软件产品不能真正满足用户的需求
三、V模型(简单了解)
本质是把瀑布模型中隐含的迭代过程明确出来,使抽象等级的概念更加明显
改进了的瀑布模型,强调测试和分析设计之间的关联
单元、集成测试校验程序设计
系统测试校验系统设计
验收测试确认需求
是活动驱动的
过于简单、理想化的抽象,仍未解决对需求变化适应性差的问题
验证与确认:是否正确的完成某部分/完成某部分工作是否满足需求
四、原型/快速原型模型
快速建立起一个可以在计算机上运行的程序,往往是最终产品的一个子集
1. 采用原型法时,关键的因素是建立模型的速度,而不是原型运行的效率。
2. 原型化方法从用户界面的开发入手,首先形成系统界面模型,并就“同意什么和不同意什么”提出意见
优点:
不带反馈环,软件的开发基本线性进行
原型系统已与用户交互得到验证
开发人员在建立原型时已经学到很多(避免错误)
利用原型有助于统一和增强客户和开发者对需求的理解、定义、确认
可以结合瀑布模型,两者互补性强
缺点:
原型不可能面面俱到
客户:不可把原型当作正式运行的软件
开发者:同上,还需牢记没有考虑质量因素的部分
原型只是模型而已,不能从原型得到最终的产品(进化原型模型可以,从原型迭代得到最终产品)
五、阶段式开发(演化模型)
(1)渐增式开发:
增量模型是一种需要快速构建核心产品的好方法
增量模型把软件作为一系列增量构件来设计、编码、集成和测试
每个构建由多个相互作用的模块构成,完成特定的功能
第一个构建实现基本需求、提供最核心功能
优点:
适用于人手不足、不能在软件项目期限之前实现一个完全版本的软件的情况
能有计划地管理技术风险
每个增量都发布一个高质量的可操作的版本,用户能在较短时间内使用上部分内容
逐步增加产品功能,使用户有比较充裕的时间来学习和适应新产品
难点:(核心:增量模型结构导致的矛盾和冲突)
软件体系结构必须是开放的,每个新的增量构件无缝集成到现有软件体系结构,增加了设计阶段的投入
本身具有矛盾性,一方面要求将软件看作一个整体,一方面要求把软件看作构件序列,且构件之间彼此独立,需要开发人员协调这一矛盾
(2)迭代式开发(螺旋模型):风险驱动
基本思想:使用原型以及其他方法来尽量降低风险
可看做每个阶段之前都增加风险分析过程的快速原型模型
优点:
是对瀑布模型的发展,由客户对阶段性结果做出评审,对保证软件质量十分有利
引入风险分析,测试活动的确定性增强
最外层代表维护,开发与维护采用同样方式,使维护与开发得到同样的重视
缺点:
适合内部开发,否则风险分析要在合同前完成或者客户理解(分析之后可能会取消,风险若消除不掉就终止)
只适合大项目,风险分析占比过大,占用资源,增加成本
对风险分析能力要求高,否则会退化为瀑布模型或更糟
六、喷泉模型
喷泉体现了面向对象软件开发过程迭代和无缝的特性(相邻两个过程可能有重合)
阶段之间可以重叠,没有明显的界限
迭代性,像喷泉一样可以来回上下
为了避免过于无序,应该把一个线性过程作为总目标
容易进行维护工作
七、RUP(统一软件开发过程)
RUP是一种过程框架、详细规范性过程模型
其软件知识库基本上涵盖了软件开发的所有活动
为面向对象设计和开发提供了定义明确的结构
RUP之中的设计和文档记录都使用UML
理想开发环境下软件过程的一种完美形式,但没有给出具体完整的部署方案
RUP最佳实践
迭代式开发 :容纳需求变更/减少风险
管理需求(用例和脚本)
基于构件的体系架构(一开始就需要给出)
可视化建模(UML,便于确保一致性和沟通交流,尽早发现问题)
验证质量(贯穿全流程,越晚发现成本越高)
控制软件变更
迭代式开发特点
在大规模投资之前,关键风险已得到解决
初始迭代可以支持早期的用户反馈
经常进行测试和集成
通过目标里程碑来聚焦于短期工作重点(短周期)
进度是通过评估实施情况来衡量的(用实现多少功能评估)
可以部分实现、部分部署(目的:尽快部署,对应瀑布模型风险问题)
九个核心工作流(静态)
过程工作流
支持工作流
业务建模
需求
分析与设计
实现
测试
部署
配置与变更管理
项目管理
环境提供
工作阶段(动态) 二维生命周期
Inception(先启):生命周期目标里程碑
建立业务模型,定义最终产品视图,确定项目的范围
Elaboration(精化):生命周期架构里程碑
设计并确定系统的体系结构,制定项目计划,确定资源需求
Construction(构建):初始可操作性能里程碑
开发所有构件和程序,集成为客户需要的产品,测试所有功能
Transition(移交):产品发布里程碑
把开发出的产品提交给用户使用
6. RUP 迭代式开发
每个项目开发过程由多个迭代过程组成
每次迭代只考虑一部分系统需求
每个迭代都是风险驱动的
每个迭代都可以看作是一个“小型瀑布模型”过程(以一个交付版本结束,结果是一个增量)
每次迭代以不同的重点和强度访问核心工作流
7. RUP生命周期的特点(与螺旋模型相比)
给出每个阶段迭代完成交付的增量的具体要求,即四个阶段的里程碑
详细阐述了九大核心工作流程中活动内容的重点和强度不同
对每次迭代过程中不同核心工作流程活动的并行化支持
8. RUP 优点:
用例驱动、以架构为中心、迭代和增量;
具有二维迭代性,有利于降低风险、适应需求变化(每次迭代都是风险驱动的);
是可配置的,具有通用性;
9. RUP 缺点:
在理想的项目开发环境下软件过程的一种完美模式;
未给出具体的剪裁、扩充等配置实施的方法准则。
八、敏捷过程
敏捷过程的价值观:
个体和交互 胜过 过程和工具
可以工作的软件 胜过 面面俱到的文档
客户合作 胜过 合同谈判
响应变化 胜过 遵循计划
价值驱动,资源、时间有限且固定
高可视性,高可适应性,持续产出业务价值
强调适应而非预测变化,以人为中心
人是软件项目获得成功最为重要的因素
软件开发的主要目标是交付可以工作的软件,编辑的文档应尽量短小并且主题突出
制定细致度逐渐降低的计划
简单化设计,只涉及与当前迭代最吻合的手段,不会进行过度设计(对质量要求较高的情况下可以采用RUP)
到了开发的后期也欢迎改变需求,利用变化来创造竞争优势
交付的时间间隔越短越好
工作的软件是首要的进度度量标准
实例:
(1)极限编程(XP):把最好的开发实践运用到极致
适用:需求模糊且经常改变
特点:对变化和不确定性反应更迅速、更敏捷;快速的同时保持可持续的开发速度
使用用户素材获取需求、测试驱动开发(先写测试代码,再具体编写程序)、结对编程
持续的集成、可持续的开发速度、重构、使用隐喻(全局视图)
(2)Scrum方法:
三个特点:关注当下(简单性)、放权(团队自组织,自己协调如何完成任务)、提高沟通效率(面对面交流)
重要概念:待办事项列表(产品列表),2-4周的冲刺/迭代(每次增量待办事项or用户故事),团队做出承诺主动认领任务,每天进行反思战略会议
三个角色:产品负责人,Scrum Master(教练,监督,不要管理团队),开发团队(多功能团队,保证团队不受外界干扰)
5. 敏捷过程的特点:
生命周期:敏捷过程是一维,RUP是二维、双重
人员:
敏捷过程强调了客户这一角色的重要性,各成员个体地位关系平等,职责是共同的;首要协作方式为面对面交谈
RUP按照角色分工,未给出地位关系,通过”形式化的文档--模型“协作交互
方法:敏捷过程动态满足需求,简单化;RUP以用例驱动方法,以架构为中心;整个过程建模都采用UML
产品:RUP未确定形式化的文档--模型与软件两者的优先级;敏捷过程认为软件胜过面面俱到的文档
敏捷开发适合需求模糊或频繁变化、小型创业项目软件、客户深度参与(短平快,团结一致做)
6. 微软过程:对XP和RUP的结合,选取各自的优势
九、DevOps(开发运维一体化)
持续集成(确保软件可运行)->持续交付(确保处于随时可部署的状态同时实现快速交付)->持续部署(从代码变更提交到生产环境部署,端到端快速推进)
开发部署速度快,提高效率
快速解决故障,产品质量更高
第三章 可行性研究
目的:用最小的代价,在尽可能短的时间内确定问题能否解决
实质:一次压缩、简化的系统分析和设计过程
根本任务:对日后行动方针提出建议
可行性研究的成本一般为预期总成本的5%-10%
方面:两个重点(技术可行性,经济可行性)
技术可行性:使用现有的技术能实现这个系统吗,要解决技术风险问题
经济可行性:这个系统的经济效益能超过他的开发成本吗?
操作可行性:系统的操作方式在这个用户组织内是否行得通
法律可行性:可能导致的任何侵权、妨碍和责任
开发方案的选择性研究:提出多种方案并推荐较优方案
可行性研究过程
7. 系统流程图(未做要求):
是概括地描绘物理系统的传统工具
它的基本思想是用图形符号以黑盒子形式描绘组成系统的每个部件
它表达了数据在系统各部件之间的流动情况
8. 系统流程图的分层:
用一张高层次的系统流程图描绘系统的总体概貌,表明系统的关键功能
分别把每个关键功能扩展到适当的详细程度,画在单独的一页纸上
便于阅读者按从抽象到具体的过程逐步深入地了解一个复杂的系统
9. 成本/效益分析的第一步是估计开发成本、运行费用和新系统将带来的经济效益
10. 成本估计:
(1)代码行技术:
估计的成本:e=(a+4m+b)/6
其中,a是最乐观估计的成本,b是最悲观估计的成本,m是一般估计的成本
(2)任务分解技术
(3)自动成本估计技术
运行费用:操作费用和维护费用
经济效益:使用新系统增加的收入和可以节省的运行费用
投资回收期:使得累积的经济效益等于最初投资所需要的时间
货币的时间价值
第四章 需求分析
一、概述
1. 需求:就是系统的特征(Features),或对系统为达到某个目标所能做的事情的一个描述(是问题信息和系统行为、特性、设计及制造约束的描述的集合)
2. 需求分析的目标
需求分析要求系统必须确定完成哪些工作,及对目标系统提出完整、准确、清晰、具体的需求
3. 需求难以建立的原因
误解;交流障碍;“完整性”问题;需求永远不会稳定;用户意见不统一;错误的要求;认识混淆
4. 两种需求:(系统需求/客户需求)
功能需求:系统与环境之间的交互——描述系统必须支持的功能和过程的系统需求
非功能需求:客户给出的具体约束、指标——描述操作环境和性能目标的系统需求
区别:功能性需求描述系统应该做什么,非功能性需求描述为如何实现功能性需求设定约束
非功能需求必须依附于功能需求而存在
5. 两种需求文档
需求定义:需求的描述,用应用域语言,客户/和用户和开发人员共同编写
需求规约:软件需求规格说明,用开发人员擅长的技术术语编写,分析人员编写
二、需求分析的任务
必须理解并描述问题的信息域:根据这条准则应该建立数据模型(ER模型,类图)
必须定义软件应完成的功能:这条准则要求建立功能模型(数据流图,用例模型)
必须描述作为外部事件结果的软件行为:这条准则要求建立行为模型(状态转换图,UML行为图)
必须对描述信息、功能和行为的模型进行分解:用层次的方式展示细节
确定对系统的综合要求
分析系统的数据要求
导出系统的逻辑模型
修正系统的开发计划
三、需求过程
定义:用来导出、确认和维护系统需求文档的一组结构化活动
需求过程本质:在问题空间与求解空间中间架设桥梁
三个阶段:
针对进行了需求分析和协商之后进行最终确认。也可以使用原型技术(能用于试验,反应系统性能、效率)
需求提取
发现需求的过程,从项目干系人角度考虑发现它们的真正需求。
系统需求的资料来源 主要是项目干系人
与用户沟通获取需求的方法:访谈
面向数据流自顶向下求精
简易的应用规格说明技术FAST(面向团队)
场景分析
快速建立软件原型(是开发用户接口的唯一有效方式,需求提取阶段仅用于发现功能需求)
不提倡把原型做成最终系统(原型趋向于非结构化,系统灵活性降低、长期成本增加、系统生存期缩短)
需求提取中注意的事项:
确定系统的边界
从多个角度考察待解决的问题(确定需求优先级)
把需求按照必要程度分三类
需求分析与协商
初步筛查(是否满足需求的特性)、不同干系人需求的冲突和解。
比原始需求更加精准和完整。
需求分类:发现需求之间的共性和例外关系、提高文档跟踪能力、帮助找到遗漏的需求
使用交互矩阵发现冲突与重叠
保证需求是可测试的:摒除需求中不确定、不完整、不正确的部分,使用定量描述
需求确认
四、需求的表达方法-系统模型
流程:综合要求->数据要求->导出目的系统的逻辑模型->修整系统的开发计划
分类:
(1)行为模型(包含所有过程层面的内容):
功能模型:描述数据的功能转换,两种方式:DFD,面向对象触发相应服务
动态模型:描述与时间有关的变化
(2)结构模型(静态模型):描述系统的实体结构
五、结构化分析方法
传统的结构化分析方法是一种面向数据流进行需求分析的方法
具体而言,传统的结构化分析方法就是用抽象模型的概念,按照软件内部数据传递、变换的关系,自顶向下逐层分解,直到找到满足功能要求的所有可实现的软件为止
导出目标系统的逻辑模型
4. 结构化分析方法从三个方面建模:
核心:数据字典
数据建模——实体关系图
功能建模——数据流图
行为建模——状态转换图
六、状态转换图
活动表的语法格式如下: 事件名(参数表)/动作表达式
事件表达式的语法如下: 事件说明[守卫条件]/动作表达式
其中,事件说明的语法如下: 事件名(参数表)
数字电路中的时序逻辑电路图就涉及了状态转换图的画法,操作系统中就绪态、挂起态和运行态的转换也可用状态图表示
七、数据流图(DFD)
定义:描述数据在系统中如何被传送或变换,以及描述如何对数据流进行变换的功能
数据流图所使用的符号:
源点、终点:系统之外的实体,是为了帮助理解系统接口而引入的
加工/变换:对数据进行处理的单元。要编号和起合理的名字
数据流:一组数据项组成,不能在源点/终点和数据存储之间流动
文件:暂存数据。
特殊符号:*:表示且;$$\oplus$$:表示异或
特别注意:
数据流和数据存储支持了数据的抽象,将数据视为系统中的实体,而不关注数据的具体内容或实现细节
数据流不能在源点/终点、数据存储之间流动。
对于一些简单的加工过程可以直接写在数据字典的条目里,而如果是一些很复杂的加工就要使用判定表等方式来进行记录。
一个加工的分解子加工个数应当控制在7+2以内。
通常先为数据流命名,然后再为与之相关联的处理命名。
3. DFD
4. 层次化的数据流图
为了表达详细的加工情况,将采用层次结构的数据流图。具体来说,采用自顶向下逐层构建数据流图的方式
步骤:
首先构建顶层数据流图(基本系统模型):只含有一个代表软件系统整体处理功能的转换
画出系统的内部(系统功能级数据流图):将顶层中的处理分解为若干多个处理
画处理的内部:把每个处理看成一个小的系统,用第2步的方法画出每个处理的数据流图子图
重复3,直到尚未分解的处理都足够简单
编号原则:
每个处理的子处理编号由父处理加细得到。例如:3号处理的子处理依次编号为3.1,3.2等
父图与子图的平衡问题:
子图的输入/输出数据流必须与父图的输入/输出数据流必须一致,不得添加或减少(然而,如果父图中的数据流可以被加细为多个子图中的数据流,也认为是平衡的)
局部文件问题:
文件(数据存储)总是局部于分层数据流图的某一层或某几层,所以数据流图中引入的文件都是局部文件。
命名问题:
首先为数据流命名:名字要代表整个数据流的内容,要具体有含义,如果命名困难,则说明应当继续分解 然后为与数据流关联的处理命名:规则与数据流命名类似,当一个处理命名时要使用两个或多个动词时,将该处理继续分解。
5. 数据流图用于:
作为交流信息的工具
作为分析和设计的工具,考虑系统的物理实现
映射出软件结构
八、数据字典
数据字典是对数据流图中包含的所有元素的定义的集合。相当于是画出数据流图之后对其中的元素进行详细说明,即定义。让模型不出现歧义性!
数据字典与数据流图共同构成系统的逻辑模型。
数据字典包括了对 数据流、数据流分量(数据元素)、数据存储、处理 四类元素的定义
数据流和数据流分量的区别在于,数据流分量是不可再分的单位,是最小的单位。
写加工逻辑说明的工具:
结构化英语
判定表(依赖多个逻辑条件的取值,不包含处理的顺序)
判定树
九、其他图形工具
层次方框图(描绘数据的层次结构)、Warnier图(描绘信息的逻辑组织)、IPO图(描绘数据的关系)
从哪些方面验证软件需求的正确性:(验证软件需求的方法就是验证这四个性质)
一致性
完整性
现实性
有效性
第五章 总体设计
一、软件设计过程
软件设计的本质:
软件设计是软件开发过程中承前启后的工作
软件设计是在软件开发中形成质量的地方
是将需求准确转换为完整的软件产品或系统的唯一办法
软件设计过程图:
3. 概要设计:即总体设计,将需求转化为数据结构和软件的系统结构,即系统的模块划分
详细设计:得到软件的详细数据结构和算法
4. 系统设计阶段:确定系统的具体实现方案
结构设计阶段:确定软件结构
系统设计阶段通常涉及确定系统的整体功能、模块划分、数据结构、算法选择等,以便实现系统所需的功能。这个阶段关注的是系统的整体实现方案,包括了系统的功能性和非功能性需求的实现方法。
而结构设计阶段则是在系统设计阶段的基础上,进一步明确软件系统的组织结构和各个模块之间的关系,以及确定具体的编程语言、开发框架、数据库设计等方面的细节。在这个阶段,重点是确定软件的架构,包括模块化设计、接口定义、数据流和控制流的设计等。
9个步骤了解
软件设计规约:
需求分析 -> 确认测试
概要设计 -> 系统测试
详细设计 -> 单元测试
详细设计规约主要作为软件设计人员与编程人员之间交流的媒体
概要设计规约主要作为软件项目管理人员、系统分析人员与设计人员之间交流的媒体
二、软件设计原理
模块化:将程序划分成独立命名且可独立访问的模块,每个模块完成一个子功能,把这些模块集成起来构成一个整体,可以完成指定的功能满足用户的需求
比直接完成整个功能工作量要小,但链接成本可能会增加,模块并非越多越好
模块是可单独命名和可编址的部分
抽象:抽出事物的本质特性 而暂时不考虑它们的细节。
软件工程过程的每一步都是对抽象的细化(精化)
过程抽象:把一个功能抽象成一个函数,比如connect。
数据抽象:把一个数据对象抽象成一个数据类型,比如class person
3. 逐步求精:为了能集中精力解决主要问题而尽量推迟对问题细节的考虑
抽象程度不断下降实际上是一个逐步求精的过程。自顶向下的设计策略,就是从抽象到具体。
4. 信息隐藏和局部化
信息隐藏:模块其中包含的信息对不需要他们的其他模块来说是不可访问的,隐藏的是模块的实现细节。相当于只使用,不关心实现。
局部化:把关系密切的软件元素物理地放得彼此靠近。相当于把关系密切的元素封装成一个类
5. 模块独立性:每个模块完成一个相对独立的子功能,并且和其他模块之间的关系很简单
上面四个的直接结果。
模块独立程度的定性标准度量
耦合:不同模块之间彼此互相依赖,耦合度高表示模块之间联系紧密。耦合度越高模块独立性越小。
内聚:模块内部元素之间互相结合的紧密程度,与耦合相反
三、耦合
耦合强弱取决于接口的复杂程度、进入或访问一个模块的点、通过接口的数据
在软件设计中应该追求尽可能松散耦合的系统,即尽可能独立的模块。
耦合类型:(耦合性从低到高,模块独立性从强到弱)
(1)非直接耦合(弱):无直接连接,由主模块调用。
如编译原理课程设计,main函数分别调用语义分析和语法分析
(2)数据耦合(弱):相当于使用一个简单的参数调用另外一个模块的功能
(3)标记耦合(弱):与数据耦合不同的是,这里传递的参数不是一个简单变量而是一个记录信息(参数表),但是可能用到的只是记录中的非常小的部分。
把在数据结构上的操作全部集中在一个模块中,可消除或转化这种耦合。相当于本来是将整个记录传入另外一个模块,现在是外加一个模块用来操作这个记录,最后传入一个简单变量。
(4)控制耦合(中等):明显地控制选择另一模块的功能。就比如程序A给出的控制信息决定了B是开凉水,还是开热水,A必须知道B内部的逻辑关系。如果B改掉了,那么模块A会受到影响,比如B换了控制信息。。//传控制变量 or 传地址(传地址更高一点)
(5)外部耦合(较强):全局的简单变量。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
如C语言程序中有模块访问被修饰为extern的外部变量。注意是简单变量!
(6)公共耦合(强):它与外部耦合的区别在于,它是一组模块都访问同一个公共数据环境(如一个数据结构)!而非一个简单全局变量。
(7)内容耦合(超强,高级语言不允许出现): 还算是比较离谱的,相当于涉及到程序的指令地址了
四、内聚
内聚表示的是模块内部 各个元素的紧密程度。
内聚度越高,则一个模块内部越紧密,模块独立性就越强,这和耦合是相反的,耦合是不同模块之间,不同模块之间耦合度越高则独立性越小。总的来说就是合字,都是紧密的意思,内部越紧密那它就越独立,它若和别的模块紧密则不独立
内聚类型:
(1)偶然内聚(弱):没有联系,或者即使有联系,这种联系也很松散。
实际上就是多个模块有相同代码,执行相同功能,提取出来当做一个新模块使用。这个没有内在联系,只是用到了同一段代码而已
(2)逻辑内聚(弱):这种模块把几种相关的功能组合在一起,每次调用时,由传送给模块的判定参数来确定该模块应执行哪一种功能
整个模块实现多种功能,具体执行哪种功能需要根据传递给该模块的参数来确定(执行Kruskal还是Prim呢?)
(3)时间内聚(弱):这个模块大多是多功能模块,在同一时间段内执行
时间内聚的这些模块会在同一时间段全部执行完!但是他们之间可能没啥联系。比如数据库初始化、界面初始化等一开始就得执行的
(4)过程内聚(中):使用流程图做为工具设计程序时,把流程图中的某一部分划出组成模块,就得到过程内聚模块。
例如,把流程图中的循环部分、判定部分、计算部分分成三个模块,这三个模块都是过程内聚模块。(有一定的过程性,但不一定顺次执行)
这些模块之间的耦合度可能比较高,单独的一个模块由于不是完整功能,内聚度可能也比较低。
(5)通信内聚(中):如果一个模块内部的各个功能使用了相同的输入数据,或产生了相同的输出数据
相当于模块内部的东西,操作同一个数据集。
(6)顺序内聚(强):模块内部的各个处理和同一个功能密切相关,并且必须顺序执行
但是可能不包含整个功能,所以还不是最高的。
(7)功能内聚(强):所有部分都是为了完成一项具体功能而协同工作,紧密联系,不可分割的
五、启发式规则
七条启发式规则:
改进软件结构提高模块独立性
降低耦合,提高内聚
模块规模应该适中
深度、宽度、扇出和扇入都应适当
模块的作用域应该在控制域之内
控制域:直接或间接从属于它的模块的集合
作用域:它的一个判定影响的所有模块集合
力争降低模块接口的复杂程度
设计单入口单出口的模块
避免出现内容耦合
模块功能应该可以预测 ,避免对模块施加过多限制
只要输入的数据相同就产生同样的输出,这个模块的功能就是可以预测的
将作用范围移动到控制范围的方法:
将判定所在模块合并到父模块中,使判定处于较高层次;
将受判定影响的模块下移到控制范围内;
将判定上移到层次中较高的位置
六、面向数据流的设计方法
结构化设计方法(SD方法)
把信息流映射成软件结构
信息流可以分为变换流和事务流
变换型系统结构图由输入、变换中心和输出等三部分组成。
一旦确定了软件结构就可以把它作为一个整体来复查,从而能够评价和精化软件结构
特别注意全局特征和局部特征
映射步骤:
第1步 复查基本系统模型。
第2步 复查并精化数据流图。
第3步 确定数据流图具有变换特性还是事务特性。
第4步 确定输入流和输出流的边界,从而孤立出变换中心。
第5步 完成“第一级分解”
第6步 完成“第二级分解”(为每一个模块写一个简要说明)
第7步 使用设计度量和启发式规则对第一次分割得到的软件结构进一步精化。
变换流:第一级分解;第二级分解
事务流:
七、软件体系结构风格
下列对软件体系结构的描述错误的是( D )。
A、软件体系结构是对子系统、系统构件以及它们之间相互关系的描述。
B、子系统和构件一般定义在不同的视图内,以显示软件系统的相关功能属性和非功能属性。
C、软件体系结构是软件系统的一组关键设计决策。
D、软件体系结构是软件需求活动的一种工作产品。
(1)管道和过滤器
每个构件都有一组输入和输出,构件读输入的数据流,经过内部处理,然后产生输出数据流。
连接件就象是数据流传输的管道(Pipes),将一个过滤器的输出传到另一过滤器的输入 。
在输入被完全消费之前,输出便产生了
过滤器不知道它上游和下游的标识
优点:
具有良好的隐蔽性和高内聚、低耦合的特点
允许将整个系统的输入/输出行为看作是多个过滤器的行为的简单合成
支持软件重用
易于维护和增强系统
允许对吞吐量、死锁等性质进行分析
支持并行执行,每个过滤器可作为一个单独的任务完成
缺点:
导致进程成为批处理的结构
不适合处理交互的应用
可能会出现重复执行预备函数,导致系统性能下降,增加了编写过滤器的复杂性
应用:编译器、Unix shell
(2)数据抽象与面向对象风格
构件是对象,通过方法和过程调用来交互。
缺点:一个对象和另外一个对象调用进行交互,必须知道对象的标识。
(3)基于事件/隐式调用风格
构件通过发布或广播一个或多个事件来隐式激发另外一些模块中的过程。
基于事件/隐式调用风格增加了构件之间进行数据交换的难度
这些被激发的过程是对这些事件注册了过程的。
比如理解为数据库中的触发器,某些数据库对这个表设置了触发器(注册过程),当用户在表中插入一些数据时(构件),隐式激发了数据库的触发器。
优点:
为软件重用提供了强大的支持,加入一个构件时只需要将它注册到系统的事件中
为改进系统带来了方便,用一个构件代替另一个构件时,不会影响到其他构件的接口
缺点:
构件放弃了对系统计算的控制
声明或广播某个事件的过程的语义依赖于被触发事件的上下文约束,关于正确性的推理存在问题
(4)层次系统风格
每一层为上层服务,并作为下层的客户(上层调用下层),内部的层只对于相邻的外层可见(除了输出函数)
构件:各个层次内部包含的构件
连接件:层次间的协议
如操作系统的那个层次图。
优点:
支持基于抽象程度递增的系统设计
支持功能增强,只影响相邻的上下层
支持重用,服务接口
对标准化的支持,促进实现标准化的任务和接口开发
缺点:
划分为分层的模式并不容易
效率降低
如何界定层次间的划分非常复杂
(5)仓库风格
两种构件:中央数据结构 一组独立构件
两种风格:传统的数据库体系结构(输入事务触发选择)
黑板系统(中央数据结构当前状态触发选择)
(6)客户/服务器风格
传统是两层C/S结构
瘦客户机模型:操作都在服务器,客户只负责标识
第六章 详细设计
一、详细设计概要
(1)接口设计
软件构件之间的接口
模块和消息生产者/消费者的接口
人和计算机的接口,人机界面
(2)过程设计
结构化程序设计技术是详细设计的逻辑基础
三种控制结构:顺序、选择、循环,取消GOTO语句
只有一个入口和一个出口
二、过程设计技术和工具
1. 一个程序流程图对应的盒图表示不是唯一的 PAD图可以纵横延伸,图形的空间效果好
程序流程图不支持逐步求精
程序流程图
不是逐步求精的好工具,过早地考虑了细节而非全局结构
用箭头代表控制流,因此程序员可以不受约束控制,随意转移控制
不易表示数据结构
盒图/N-S图
功能域明确(某个特定控制结构的作用域)
不可能随意转移控制
很容易确定局部和全程数据的作用域
方便表示嵌套关系和模块的层次结构
PAD图
使用结构化的PAD符号设计出的程序必然是结构化程序
描绘的程序结构清晰、容易记忆
方便转化为高级语言源程序,可用转换工具自动完成
既可以表示程序逻辑,又能用于描绘数据结构
支持自顶向下、逐步求精
判定表
静态逻辑,不能表达加工的顺序,不能表达循环结构
要求将程序流程图的多分支判断都改成两分支判断
判定树 判定树就是将判定表根据判定表中的所有条件做成分支。从根节点开始一路判断,最后的叶子节点就是要执行的动作。
过程设计语言PDL
也被称为伪代码,具有严格的关键字外部语法,用于定义控制结构和数据结构。同时又有着灵活的内部语法(表示实际操作和条件),可以适应各种工程需要
可以直接作为注释插入程序片段中,可以使用普通的文字编辑系统编辑,有软件可以自动由PDL生成程序代码
不如图形界面清晰,且在复杂的条件组合和动作对应关系上不如判定表清晰
三、人机界面设计
人机界面可以看作是基于计算机的系统或产品的最重要的元素
黄金规则
赋予用户控制权
减少用户的记忆负担
保持界面一致
设计过程
(1)用户、任务和环境分析及建模
(2)界面设计活动
系统响应时间,用户完成某个控制动作,到软件给出预期响应之间的时间
用户帮助设施(集成/附加,部分功能/全部功能,方式,显示,返回,结构)
出错信息处理(可以理解,从错误恢复,负面后果,听觉/视觉,不带指责)
命令交互(命令行/窗口)
(3)界面构造
(4)界面确认
4. 设计指南:
1. 一般交互指南
2. 信息显示指南
3. 数据输入指南
四、程序复杂程度的定量度量
用途
程序的复杂程度乘以适当的参数可以估算软件中地错误数量以及软件开发需要的工作量
结果可以比较两个算法的优劣
可以作为模块规模的精确限度
2. McCabe方法
流图/程序图:仅仅描述程序的控制流程,完全不表现对数据的具体操作及分支和循环的具体条件 注意:流图的选择语句都是单一的,遇到多重选择问题要将多个选择条件转换为多个节点
节点(N):用圆表示,代表一条或多条语句
边(E):用箭头表示,一边必须终止于一个节点
区域(V):由边和结点围成的面积.特别的,开区域也算作一个区域
环形复杂度
V(G)=V,环形复杂度等于区域数量
V(G)=E-N+2
V(G)=P+1,P是流图中判断的数目
在标准的流图中,一个判断节点代表一个判断
在某些非标准流图中,可能一个节点可以引出n条路径,这代表着存在n-1个判断
用途
程序的环形复杂度取决于程序控制流的复杂程度,也是取决于程序结构的复杂程度
对软件的可靠性给出某种预测
环境复杂度是可加的
第七章 编码和测试
一、编码
好的程序的代码逻辑简明清晰、易读易懂
程序的内部文档
数据说明
语句构造
输入/输出方法
效率问题
二、测试
测试是程序的执行过程,目的在于发现错误
一个好的测试用例在于能发现至今未发现的错误
一个成功的测试用例在于发现了至今未发现的错误
2. 测试方法的种类
(1)黑盒测试
把测试对象看作一个黑盒,测试人完全不考虑程序内部逻辑和内部特性,只依据程序的需求规格说明书,检查程序的功能是否符合他的功能说明。——功能测试/数据驱动测试
黑盒测试不可能用所有的输入输出条件来确定测试数据
黑盒测试基于程序接口
(2)白盒测试
把测试对象看作一个透明的盒子,它允许测试人员利用程序内部的逻辑结构及有关信息,设计或选择测试用例,对程序所有的逻辑路径进行测试。——结构测试、玻璃盒测试或逻辑驱动测试
3. 软件测试步骤
模块测验(单元测验)--编码和详细设计
子系统测验(集成测验)--概要设计和详细设计
系统测验(集成测验)--软件设计(概要设计)和需求说明
验收测试(确认测试)--系统需求说明书
平行运行:同时运行开发出的新版本和被他取代的旧版本,比较两个系统的处理结果
三、单元测试
测试重点
模块接口
局部数据结构
重要的执行通路
出错处理通路
边界条件
代码审查:可以审查出30%-70%的设计错误和编码错误
预排:一人扮演“测试者”,其他人扮演计算机
3. 驱动程序:接收测试数据,把这些数据传送给被测试的模块,印出有关结果
存根程序:代替被测试模块所调用的子模块,“虚拟子程序”/“桩模块”:做最少量的数据操作,把控制归还给调用它的模块
四、集成测试
非渐增式测试:先分别测试每个模块,再一次性把所有模块设计要求放在一起结合成所要的程序
渐增式测试:把下一个要测试的模块同已测试好的那些模块结合起来进行测试,以此类推,每次增加一个模块。这种方法实质上是同时完成单元测试和集成测试
具体分类:
一次性集成:当所有的组件都单独测试完毕之后,将他们一次性混合起来组成最终的系统,查看其是否能运行成功。(类似于非渐增式测试)
缺点:
需要大量编写驱动程序和存根程序
所有组件一次进行合并,很难找出所有错误的原因
不容易区分接口错误与其他类型的错误
自顶向下集成:从主模块开始,沿着程序的控制层向下移动,逐渐把各个模块结合起来。在把【附属于主模块的那些模块】装载到程序结构中时,使用DFS或BFS策略。
优点:
能够早期对主要的控制或关键的抉择进行检验
不需要驱动程序
选择DFS时,可以在早期实验一个完整的功能并验证此功能
缺点:
需要编写存根程序
为了充分测试高层,可能需要低层的处理
可能需要很多存根程序
自底向上集成:从原子模块开始组装和测试,不需要存根程序。
优点:
适用场景:当底层有许多组件是有多种用途的公共例程而经常被其他组件调用时、当设计是面向对象的或者当系统由大量孤立的复用的组件组成时,自底向上集成很有用。
不需要写存根程序
测试驱动程序数目较少
底层往往承担着主要的计算和输出,更容易出错,该方法能早发现这类错误
底层模块可以并行测试
缺点:
对顶层测试较晚,会推迟主要错误的发现
程序最后一个模块加入时,才有具体整体形象
三明治集成:将自顶向下和自底向上结合起来,选取某一层作为基准层。选取不同的基准层,整个集成测试会有很大不同。
优点:
允许在测试的早期进行集成测试
结合了自顶向下和自底向上测试的优点,在测试的最开始就对控制和公用程序进行测试
缺点:
在集成之前没有彻底地测试单独的组件
五、回归测试
回归测试谁指重新执行已经做过的测试的某个子集,以保证由于调试或其他原因引起的变化,不会导致非预期的软件行为或额外错误
回归测试集:
代表性测试用例
可能受修改影响
被修改过
六、确认测试
确认测试有时也叫验收测试,目标是验证软件的有效性 软件有效性:像预期那样运行 确认测试以用户为主来进行 确认:为了保证软件确实满足了用户需求而进行的一系列活动——you built a right thing 验证:为了保证软件正确地实现了某个特定的要求地一系列活动——you built it right
Alpha测试:用户在开发者的场景下,在开发者的指导下进行测试
Beta测试:开发者不在现场,用户在一个或多个客户现场进行测试
七、白盒测试
逻辑覆盖
语句覆盖(每条可执行语句都被执行)
判定覆盖(每个判定节点真假都覆盖)
条件覆盖(每个判定中每个条件真假都覆盖,满足条件覆盖不一定满足判定覆盖)
判定/条件覆盖(判定+条件)
条件组合覆盖(每个判定所有条件可能组合出现一次)
点覆盖(=语句)
边覆盖(=判定)
路径覆盖(每条可能路径都经过一次,流图中每个环至少经过一次)
2. 控制结构测试(基本路径测试,条件测试和循环测试不作要求)
步骤:
根据过程设计画出流图
计算流图环形复杂度
确定线性独立路径的基本集合
独立路径指至少引入程序的一个新处理语句集合或一个新条件的路径,用流图术语描述,包含至少一条定义该路径之前不曾用过的边
环形复杂度 >= 独立路径数量
设计可强制执行每一条独立路径的测试用例 注:某些独立测试并不能依靠程序正常执行,需要使用驱动程序或放在更大的程序中执行
八、黑盒测试
黑盒测试和白盒测试不能互相代替,两者互为补充
等价划分:将所有可能的输入数据划分为若干类,每一类导出一个测试用例,一个理想的测试用例可以发现一类错误。 确定测试用例:
设计一个新的测试用例,使得尽可能多的覆盖并未被覆盖的有效等价类,重复直到每个有效等价类被覆盖。
设计一个新的测试用例,使得仅覆盖一个尚未被覆盖的无效等价类,重复直到所有的无效等价类被覆盖。
边界值分析:大量的错误发生在边界上而不是输入范围的内部。(最大值,最小值,最大值加1,最小值减1)
错误推测法:依靠经验和直觉推测程序中可能会存在的各种错误,从而有针对性地编写检查错误的例子。
综合策略:
任何时候都必须使用边界值分析方法
必要时使用等价划分增加测试用例
用错误推断法增加用例
黑盒与白盒对比:
白盒只考虑测试软件产品;黑盒只考虑需求规约
黑盒会发现遗漏的缺陷:规格的哪些部分没有被完成;白盒会发现提交的缺陷:提出哪些实现是错误的
白盒的成本远高于黑盒,因为测试前先有源码
一个白盒的失败会导致所有的黑盒测试被重复执行并且重新决定白盒测试路径
九、软件可靠性
1. 软件可靠性(R):在程序给定的时间间隔内,按照规格说明书成功运行的概率
软件可用性(A):在程序给定的时间点,按照规格说明书成功运行的概率
2. R(250)=0.95:100个相同系统中,有95个无故障的运行了250小时,5个在此期间发生了故障。
A(250)=0.95:在运行的第250个小时,有95正在正常运行,有5个处于故障待处理状态。
3. MTTF
十、调试
调试是在成功测试之后才开始的工作。任务是进一步诊断和改进程序中潜在的错误
调试方法:
蛮干法
通过内存全部打印来调试
在程序内部特定位置设置打印语句
自动调试工具
回溯法:一旦发生错误,先确定最先发生“症状“的位置,然后人工沿控制流程回追错误产生位置
原因排除法
对分查找法:在几个关键节点注入变量的正确值,观察结果正确性,正确则问题发生在节点前,反之则发生在节点后。
归纳法
演绎法
第八章 软件维护
一、重要概念
软件维护的定义:交付使用之后,为了修改错误或增加新需求而修改软件的过程
维护在软件生存期中占70%以上
软件维护的种类:
适应性维护(25%)
改正性维护(20%)
完善性维护(50%)
预防性维护(5%):为了提高软件的可靠性、可维护性等,为以后进一步改进软件打下良好基础而对软件进行的修改
软件维护的特点
结构化维护和非结构化维护差别巨大
结构化维护:有完整的软件配置,维护整体质量高
非结构化维护:缺少相关文档,维护代价巨大
维护的代价高昂(有形/无形,生产率)
维护的问题很多
二、软件的可维护性
软件的可维护性=可理解性+可测试性+可修改性+可重用性+可移植性
文档的要求
描述如何使用该系统
必须描述如何安装和管理该系统
必须描述系统需求和设计
必须描述系统实现和测试
3. 用户文档和系统文档,各类文档必须如实反映软件的当前状态
三、可维护性复审
需求分析复审:
标注可能的改进和修改
软件可移植性
系统界面
设计复审:
评价软件的结构和过程
对可能修改的部分预作设计
代码复审:
编码风格
内部说明文档
设计和编码:
使用可重用的软件构件
配置复审:
审查软件配置成分
在完成每项维护工作后都应该对软件维护本身进行复审
四、软件再工程(预防维护)
库存目录分析
应当仔细分析库存目录,按照业务重要程度,寿命,当前可维护性,预期修改次数等指标,把库中应用系统排序,从中找到再工程候选者,然后明智分配再工程资源
以下程序可能成为预防性维护对象
预定使用多年的程序
当前正在成功使用的程序
最近的将来可能要做重大修改或增强的程序
文档重构
稳定不变的程序:保持现状
需要更新文档但资源有限:使用时建文档
关键应用+需要重构全部文档:文档工作减小到必需的最小值
逆向工程
分析程序以便在【比源代码更高的抽象层上】创建【程序的某种表示】的过程
从现有程序代码中抽取有关数据、体系结构和处理过程的设计信息,恢复设计结果的过程
代码重构
重构难以理解、测试和维护的个体模块的代码
数据重构
对数据体系结构做适应性增强
当数据结构较差时,应该对数据进行再工程
发生在较低层次,是一种全范围的再工程活动
正向工程
改变或重构现有系统,提高整体质量,重新开发
五、练习
第九章 项目管理
一、概述
是否需要管理,是区别专业开发和业余编程的重要区别之一
项目(project):为了创造独特的产品,实现独特的服务,达成独特的结果的暂时性努力。
特点:
独特的
暂时性的,存在明确的起止日期
实现目标之后就完成了
无法实现的话,也算是结束了/取消了
一个成功的项目需要满足甚至超过项目干系人的预期
运营(Operation):连续的,没有起止日期,往往是重复同一工作程序
项目约束:时间、金钱、质量
二、软件成本与工作量
软件成本需要定期修正。对于多数项目,工作量是软件成本最大的一块,并且也是最不确定的一块
工作量的估算首先从软件规模估算开始
代码行技术:依据以往的产品,估计一个功能要多少行
依赖开发语言
跨组织由于标准不同,因此不能类比
源程序仅是软件配置的一个成分,用其来估计整个项目不合理
语言效率高,则估算的生产率偏低.原因:代码效率越高,写出来的代码行数就越少,由此得到的经验指导下生产率就会很低
功能点(FP)技术:依据功能数量,通过对软件信息域特性和软件复杂性评估软件规模
上图中每部分取值为0-5,0表示该部分对系统无影响,5表示该部分对系统很重要,TFC=0.65+0.01(SUM(Fi)),TFC取值在0.65到1.35之间
使用输入数、输出数、查询数(联机输入)、主文件数和外部接口数加权求和可以计算出未经调整的功能点计数(UFP)
FP=UFP×TFC
对于相同的代码行数或功能点数,使用不同模型估算将得到不同的结果。主要原因是模型多数都是仅根据若干应用领域中有限个项目的经验数据推导出来的,适用范围有限。
软件开发工作量是软件规模(KLOC或FP)的函数,其单位通常是人月(PM)。
5. 项目进度计划的实现方式
工作分解结构
缺点:
WBS
任务责任矩阵
最高层是项目本身,接下来是项目的可交付成果以及进一步分解的、更小的可交付成果
没有指明活动间的相互依赖关联
无法表示可以并行的成分
Gantt图:它具有直观简明和容易掌握、容易绘制的优点
工程网络
三、进度计划-甘特图
Gantt图:
例子:假设有一座陈旧的矩形木板房需要重新油漆。这项工作必须分3步完成: 首先刮掉旧漆,然后刷上新漆,最后清除溅在窗户上的油漆。假设一共分配了15名工人去完成这项工作,然而工具却很有限: 只有5把刮旧漆用的刮板,5把刷漆用的刷子,5把清除溅在窗户上的油漆用的小刮刀。 甘特图画法:
![]()
优点:
形象的描绘任务的分解情况和子任务开始结束时间
缺点:
不能显示描述各项作业之间的依赖关系
进度计划的关键部分不明确
有潜力的部分和潜力的大小不明确,可能会造成潜力的浪费
四、进度计划-工程网络图
特点和要求:
描述任务的分解情况
注明作业的开始/结束时间
显示的描述作业之间的依赖关系
要求绘制者理解项目中哪些地方可以并行
活动(Activity):项目的一部分,要耗费一段时间,有开始和结束,用箭头表示
里程碑(Milestone):是某个活动完成的标志,是一个特定的时间点,用圆圈表示
3. 活动的参数:
前置条件(Precursor):活动开始前必须发生的事件
持续时间(Duration):完成活动所需的时间
最终期限(Due Date):日期,活动必须在此之前完成
结束点(Endpoint):通常是活动对应的里程碑/可交付的成果
4. 事件的最早时刻(EET):该事件能够发生的最早时间(正向选最大)
事件的最迟时刻(LET):不影响竣工的前提下,最晚可以发生的时间(逆向选最小)
可见,工程网络类似于数据结构中的活动图
机动时间 = (LET)结束 - (EET)开始 - 持续时间
变式:
机动时间=可用时间-持续时间
机动时间=作业最晚开始时间-最早开始时间
机动时间=作业最晚结束时间-最早结束时间
关键路径:
充分条件:持续时间最长,各活动机动时间为0
必要条件:事件的最早和最迟时刻相同
关键路径的空闲时间总和最小(现实中不一定为0)
关键路径的压缩,用边表示
五、人员组织
项目干系人:有既得利益者
关键项目干系人:能够促成和破坏项目的成功
客户:负责说明开发软件的需求和其他风险的承担
用户:最终使用软件的人
2. 民主制程序员组:
小组成员平等;
两两之间存在通信信道;
规模小(2-8人);
组织方式非正式
3. 主程序员组:
最好的程序员是主程序员,提供所有支持。通信由一两个人进行。
专业化和层次化
主程序员,和主程序员一样高水平的后备程序员,负责事务性工作的编程秘书,辛勤工作的程序员
变化形式:现代程序员组
各种制度的适用情况:
集中式:简单、重复的问题;模块化程度高的问题;大项目;周期固定、较短的项目;
分散式:复杂、创新的问题;模块化程度低的问题;小项目;周期长的项目;
六、软件配置管理(SCM)
软件配置管理是软件系统发展过程中管理和控制变化的规范。
目的:针对变化,控制变化
软件配置管理的目标是,使变化更正确且更容易被适应,在必须变化时减少所需花费的工作量。
软件配置管理不同于软件维护,贯彻于整个软件生命周期
软件配置项(SCI):为了配置管理而作为单独实体处理的一个工作产品或一段软件。即软件过程输出的全部计算机程序、文档、数据
配置管理聚集:SCI的一个集合,简称CM聚集
版本:在一确定的时间点上,某个SCI或某个配置的状态
基线:通过了正式复审的软件配置项
项目数据库:一旦一个SCI成为基线,就被放到项目数据库中
为什么需要软件配置管理:
软件开发项目的产品数量急剧增加且易被修改和变化
软件开发蕴含变化
产品各部件的版本问题
工作人员之间既独立又联系的关系使得通常的管理手段力不从心
软件配置管理的5项任务:
标识
版本控制
变化控制(非正式/项目级)
配置审计(技术复审/软件配置审计)
配置状态报告
配置管理是诸多管理活动中最易操作、最容易实现并且能在项目最先体现出效果的管理手段
软件质量保证
软件质量:软件与(明确地/隐含地定义的)需求相一致的程度
软件质量的保证措施:
基于非执行的测试(复审或评审)
基于执行的测试(软件测试)
程序正确性说明
9. 能力成熟度模型(CMM)
目的:通过定义能力成熟度的五个等级,引导软件开发机构不断识别出其软件过程的缺陷,并指出应该做那些改正
能力成熟度模型(CMM)
等级1:初始级
等级2:可重复级
等级3:已定义级
等级4:已管理级
等级5:优化级
七、风险管理
风险:能造成恶果的有害事件,未发生。
风险转化时刻:风险变成问题的时候。
风险=机遇
风险管理的策略:
被动策略:针对可能发生的风险监督项目,直到他们变成真正的问题时,才拨出资源来处理他们。
主动策略:标识潜在风险,评估出现概率和产生的影响,按重要性加以排序,建立计划管理风险。设立应急计划,对未知风险能采取可控有效方式回应
第十章 面向对象
一、面向对象方法学概述
对象的定义(选择):
对象是具有相同状态的一组操作的集合
对象是对属性值和操作的封装
对象:=(ID,MS,DS,MI),标识/操作集合/数据结构/受理的消息名集合(对外接口)
面向方法学的优点:
面向对象方法以对象为中心,比较稳定;
稳定性好:传统软件开发方法以算法为核心,依赖功能;
可重用性好:自含性,灵活性
轻易开发大型软件产品
可维护性好(五个方面:可理解、可修改、可移植、可重用、可测试)
二、UML基础
UML图:
用例图:展示用例(Use Case)、参与者(Actor)及其关系
类图:展示类、接口、包及其关系
对象图:某个时间点上系统中各对象的快照
组件图:展示系统各构件及其关系
部署图:展示交付系统中软硬件间物理关系
顺序图:按时序展示对象间消息传递
协作图:强调收发消息的对象间的组织结构
状态图:展示对象在其生命周期中的可能状态以及在这些状态上对事件的响应
活动图:展示系统从一个活动转到另一活动的可能路径和判断条件
Use Case用例图
简介
用例是非形式化的,可以结构化
用例间的关系:泛化、包含、扩展
如何绘制用例图:【6 分钟学会 UML 用例图-哔哩哔哩】 https://b23.tv/lay1Wnt
类图
类之间的主要关系:泛化(实线空心三角箭头)、依赖(虚线箭头)、关联(横线+文字)、聚合(空心菱形+文字)、组合(实心菱形+文字)、细化(虚线空心三角箭头)
如何绘制类图:【6 分钟学会 UML 类图-哔哩哔哩】 https://b23.tv/2QAqZa8
如何理解顺序图:【5 分钟学会 UML 时序图(顺序图、序列图)-哔哩哔哩】 https://b23.tv/rf5Rv0E
如何理解活动图:【3 分钟学会 UML 活动图-哔哩哔哩】 https://b23.tv/yfAKm80
UML拓展:标记值、构造型、约束
三、面向对象的需求提取
用例的定义:本质上,一个用例是用户与计算机之间为达到某个目的而进行的一次典型交互作用,作为结果,用例代表的就是系统的一个完整功能
场景(Scenario):场景是用例的真实例子。场景通过举例说明情况,帮助理解问题域,进而归纳用例。具体执行一次用例,得到一个场景。场景重在可理解性,用例重在完整性
注意:场景是用例的实例,因此其名字带有下划线(对象)
3. 用例的编写:
开发组织必须事先确定用例规约说明的编写标准
可以用结构化自然语言编写用例:三种基本控制结构
主要参与者(也称主动参与者)触发用例,次要参与者不触发用例
前置条件约束了用例开始之前的系统状态,后置条件约束了用例执行之后的系统状态
主事件流描述用例中产生“美满结局”的步骤,备选流描述用例中各种可能“节外生枝、横生变故、遭遇不测、异常例外”而导致“糟糕结局”的步骤
4. 编写用例的步骤
总结基本流程(基本流图)
编写基本流
绘制事件流示意图
编写备选流
找出用例中的参与对象
5. 分析类的划分:实体类、边界类、控制类
在UML用例图中,参与者之间可以有泛化关系,表示子参与者的实例可以与其父参与者对应的用例实例进行交互。
6. 用UML描述系统的5个视图
用例视图:系统行为、动力
逻辑视图:问题及解决方案的术语词汇、支持功能需求的逻辑结构
组件视图:组件、子系统、文件、第三方类库、框架、系统软件
进程视图:资源的有效利用、并行、线程通信与同步、异步事件处理、容错、可伸缩性、吞吐量
部署视图:软硬件映射、性能、规模、可靠性、部件的发布、交付、安装
四、分析类的定义与作用
概念:分析类是概念层模型,用于捕获系统对象模型的雏形,聚焦问题域而非技术细节。
目的:通过实体、边界、控制类划分职责,隔离变化,实现高内聚、低耦合。
关键原则:保持粗略性,避免技术细节(如数据库、界面实现)。
五、三类分析类及其职责
类型 | 职责 | 来源示例 |
实体类 | 存储核心业务数据及操作(如持久化信息)。 | 领域名词(如紧急情况报告、事件) |
边界类 | 处理系统与外部实体的交互(如输入/输出转换)。 | Actor与系统的交互点(如报告表单) |
控制类 | 协调用例行为,封装业务逻辑流程。 | 每个用例对应一个(如管理紧急情况控制对象) |
关键区别:
实体类:跨用例复用(如
用户
)。控制类:用例专属(如
支付处理控制
)。边界类:隔离外部变化(如硬件/协议适配)。
六、分析类的识别方法
1. 实体类识别:
来源:领域名词、系统需跟踪的实体(如
销售订单
)、数据源(如打印机
)。试探法:
检查用例中的名词(如“事件”、“资源”)。
避免过度细化:无独立行为或无标识的客体→转为属性(如“订单ID”作为属性而非类)。
2. 边界类识别:
规则:每个Actor至少关联一个边界类(如出纳员→
销售终端界面
)。试探法:
输入点:表单、传感器(如
条形码扫描仪接口
)。输出点:通知、消息(如
确认弹窗
)。
3. 控制类识别:
规则:每个用例一个控制类,复杂用例可拆分(如
报告紧急情况
→分现场控制
和调度控制
)。生命周期:控制类随用例启动/终止而创建/销毁(如
支付流程控制
在支付完成后释放)。
七、动态模型到静态模型的转换
转述用例:
用顺序图描述对象交互(Actor→边界类→控制类→实体类)。
消息映射职责:如
创建报告
消息→实体类的//create()
职责。
整理分析类:
参与类图(VOPC):汇总用例中所有参与类及其关联(如
调度员 -- 管理紧急情况控制对象
)。关联关系:基于交互图中的消息路径确定(如控制类访问实体类)。
属性与职责:
属性:仅实体类需定义核心属性(如
紧急情况报告.location
),避免细节。职责:由消息推导(如边界类
//提交表单()
、控制类//验证数据()
)。
八、关键注意事项
迭代与验证:
分析模型需多次循环(开发人员自查→客户联合评审),确保正确性、一致性。
用例完善:动态建模可能暴露需求缺失(如顺序图发现遗漏的
确认对象
)。
抽象层次控制:
实体类:不包含业务逻辑(仅数据操作)。
边界类:不涉及UI细节(仅定义交互契约)。
控制类:不包含技术逻辑(仅流程协调)。
模型演进:
分析类在设计阶段演变为设计类(职责→方法,关联→引用)。
避免过早优化:如泛化关系在分析阶段仅用于概念组织(非继承复用)。