【软件工程】山东大学软件工程复习提纲
时间:2023-01-25 02:30:00
软件工程复习提纲
本提纲可完全摘抄,考试命中率为100%,先考试A4纸:
1. 名词解释题
1. 软件工程三要素
- 方法:为软件开发提供如何做 项目规划与估算、软件系统需求分析、数据结构、系统总体结构设计等技术;
- 工具:为软件工程方法提供自动或半自动的软件支持环境,如集成软件工具,建立称为计算机辅助软件工程 (CASE) 软件开发支撑系统。
CASE
组合各种软件工具、开发机器和存储开发过程信息的工程数据库,形成软件工程环境; - 过程:将软件工程的方法和工具结合起来,实现计算机软件开发合理及时的目的。它定义了软件开发在各个阶段完成的里程碑,以确保质量和适应变化所需的管理。
2. 软件工程原则
- 抽象:提取事物最基本的特征和行为,忽略非基本细节,采用分层抽象、自上而下、逐层细化的方法来控制软件开发过程的复杂性;
- 信息隐藏:将模块设计成黑箱,实现的细节隐藏在模块内,防止模块用户直接访问。这就是信息包装、使用和实现分离的原则。用户只能通过模块接口访问模块中包装的数据;
- 模块化:模块是程序中相对独立的逻辑组成部分,是一个独立的编程单元,应该有良好的界面定义。例如,C语言程序中的函数过程,C 语言程序中的类别。模块化有助于隐藏和抽象信息,有助于表达复杂的系统;
- 确定性:软件开发过程中所有概念的表达都应确定、歧义和标准化。这有助于人们在沟通时不产生误解和遗漏,确保整个开发工作的协调;
- 一致性:整个软件系统(包括程序、文档和数据)的每个模块应使用相同的概念、符号和术语。程序的内部接口应保持一致。软件、硬件和操作系统的接口应保持一致。系统规格说明应与系统行为一致。用于形式化规格说明的合理系统应保持一致;
- 完整性:软件系统可以完全实现系统所需的功能,而不会丢失任何重要组成部分。为保证系统的完整性,在软件开发和运行过程中需要严格的技术评价;
- 可验证性:大型软件系统的开发需要上而下分解系统。系统分解应遵循易于检查、测试和评估的原则,以确保系统的正确性。
3. 软件生命周期
4. 软件开发过程
根据项目进度、成本和质量限制,开发和维护满足用户需求的软件开发活动。
5. 统一开发过程
它不仅是一个简单的软件过程,而且是一个通用的过程框架,可以用于不同类型的应用程序系统。它是基于组件的,构建的软件系统是由软件组件通过明确定义的界面相互链接构建的。 UML
语言制定系统的所有蓝图。
6. 软件过程模型
软件过程模型是软件开发活动及其关系的结构框架,用于指导软件开发。常用的软件过程模型包括:
瀑布模型
瀑布模型将开发阶段描述为从一个阶段到另一个阶段,一个阶段必须在另一个阶段之前完成,每个阶段都伴随着明确的里程碑和可交付产品。顺序如下:
- 需求分析:软件需求规格说明书;
- 系统设计:软件系统设计文档;
- 程序设计:软件功能模块算法及数据描述文档;
- 编码:源程序和注释;
- 单元及集成测试:单元及集成测试报告;
- 系统测试:系统测试报告;
- 验收试验:验收试验报告;
- 运行维护:维护报告;
优点:中间产品和界面的定义明确,开发人员可以专注于完成每个小阶段,完整的测试和审核过程,整体系统质量高;
缺点:缺乏灵活性,速度慢,不能反映实际代码开发方法;开发人员要求高,项目开始前必须说明所有需求;最终产品直到最终出现,软件客户不能直到软件原型。
适用场合:①产品定义和需求分析稳定;②易于理解但复杂的项目;③质量要求高于成本要求和进度要求;④技术实力弱或缺乏经验的开发团队。
原型模型
在与用户进行需求分析时,开发人员以较低的成本快速建立一个能够反映用户需求的原型系统,并根据用户的意见改进原型系统,然后由用户进行评估,重复这个过程,直到用户满意,然后开始正式设计。
优点:符合人们理解事物的规律,缩短用户与开发人员之间的距离,确保设计软件符合用户要求,缩短整体开发周期;
缺点:大型系统和批处理系统难以模拟,文档容易被忽视,项目难以清晰规划和管理。
适用场合:①软件系统不能提前准确定义要求;②开发人员沟通不好。
增量模型
先开发系统的主要功能,然后,随着时间的推移,新的次要功能不断增加,最终开发出完整的软件产品。
优点:①有利于增加客户对系统的信心;②降低系统失败风险;提高系统可靠性、稳定性和可维护性;
缺点:①难以选择增量粒度;②每个增量必须依靠以前的增量,耦合度增加;③软件流程的完整性很容易退化为边做边改。
适用场合:①升级现有产品或开发新版本;②严格要求完成期限的产品;③现有原型系统;④开发人员整体水平有限,可边学边开发。
螺旋模型
螺旋模型的基本做法是在瀑布模型的每个开发阶段之前引入一个非常严格的风险识别、风险分析和风险控制。它将软件项目分解项目,每个小项目识别一个或多个主要风险,直到确定所有主要风险因素。
四象限:螺旋模型每次迭代有四个任务,其次是:计划、目标/可选方案、风险评估、 开发与测试;
四个循环:螺旋模型有四个迭代,其次是:操作概念、软件需求、软件设计、开发和测试。
优点:①支持需求的动态变化,具有良好的可扩展性;②用户和开发人员很容易理解需求,也可以作为继续开发的基础;③强调风险分析,大大提高软件的最终安全性和稳定性;
缺点:①开发人员需要有丰富的风险评估知识;②开发周期长,迭代次数多,项目验收时间可能延迟。
敏捷开发模型
为了缓解传统开发模型的缺点,提出了以用户需求演变为核心的敏捷开发模型,采用迭代、渐进的软件开发方法。总体目标是尽快、持续地交付有价值的软件,以满足客户。
敏捷宣言:
- 个人和交互 > 过程和工具:面对面交流而不是文档交流;
- 软件的生产 > 文档的编写:成功的指标是软件正确工作的程度;
- 与客户合作 > 合同谈判:以客户需求为核心;
- 对变化的反应 > 遵循计划:一开始就不可能预测所有的变化。
极限编程:
它是一种典型的敏捷开发方法,包括 4
个理念:①沟通:客户与开发人员不断交换意见;②简单:选择简单的实现来满足客户的需求;③勇气:尽快和定期承诺交付;④反馈:各种反馈活动应贯穿整个过程。
敏捷管理实践:
- 每日站立会议:每日工作前,由
Scrum Master
带领团队成员按时沟通项目进度和解决方案; - 可视化管理:通过物理实体实时显示项目状态,让团队全体成员直观获取当前项目进度信息;
- 用户故事:从用户的角度,用简短的句子描述用户的需求;
- 配对编程:使用键盘,两个成对程序员,一个负责敲入代码,另一个负责实时检查每行敲入的代码;
- 测试驱动开发:在编写任何代码之前,首先编写定义代码功能的测试用例,并通过用例编写代码;
- 持续集成:团队成员经常集成工作,通常每人每天至少集成一次,大大缩短反馈周期;
优点:①无需长期计划和复杂模型,采用简单的计划策略,开发周期短;②在全过程采用迭代增量开发、反馈修正和反复测试的方法,能够适应用户经常变化的需求;③注重市场快速反应能力,早期客户满意度高;
缺点:①注重人员沟通,忽视文档的重要性,如果项目人员流动太大,给维护带来很多困难;②对编码人员的经验要求很高如果项目中有更多的新手,老员工就会更累。
适用范围:①项目经常发生变化;②实施高风险项目;③开发人员水平高,可参与决策;④有优秀的敏捷顾问。
7. 跟踪项目进度
-
项目进度:项目进度是对特定项目软件开发周期的描述。包括对项目阶段、步骤和活动的分解、对各离散活动的互动关系的描述、对各活动完成时间和整个项目完成时间的初步估计。
-
活动:项目的一部分一般占用项目进度计划的一段时间;
-
里程碑:特定的时间节点,标志着活动的结束,通常伴随着提交产品;
-
WBS:Work Breakdown Struture,项⽬按⼀定的原则分解,项⽬分解成任务,任务再分解成⼀项项⼯作,再把⼀项项⼯作分配到每个⼈的⽇常活动中,直到分解不下去为⽌,树形表示;
-
WBS 分解方法:①类比法:参考类似的项目方法或模板;②自顶向下法;③自底向上法;
-
活动图:描述活动之间的依赖关系,图中结点是项目里程碑,线表示活动;
-
AOE 网络:有向图
G
中,若用顶点代表事件,有向边表示活动,有向边上的权值表示一项活动持续的时间,则称图G
为AOE
网络;- 关键路径:从源点到汇点的最长路径;
- 关键活动:关键路径上的活动。或对整个工程的最短完成时间有影响的活动。即如它不能按期完成就会影响整个工程。;
-
项目组织:
- 民主制程序员组:小组成员完全平等,互相交流以做出决策,适用规模较小的组织;
- 主程序员组:每个小组成员必须经常与主程序员交流,而不必与其他小组成员交流;
- 现代程序员组:结合民主制程序员组和主程序员组的优点,“主程序员”由两人担任,技术负责人负责小组的技术活动,行政负责人负责所有非技术的管理决策。
8. 软件规模评估方法
①代码行分析法;②功能点分析法;③专家判断技术;④标准回归技术;⑤神经网络技术;⑥贝叶斯分析技术;⑦类比法。
9. 软件风险
使软件项目的实施受到影响和损失、甚至导致失败的、可能会发生的事件。
- 风险影响:发生风险后会造成的损失;
- 风险概率:发生风险的概率;
- 风险暴露:= 风险影响 × 风险概率,量化风险所造成的影响;
- 降低风险的三种策略:
- 避免风险:通过改变软件的性能或功能需求;
- 转移风险:把风险分配到其它系统中,或购买保险;
- 控制风险:预先假设风险会发生,接受并采取措施提前控制;
- 风险杠杆:= (降低前的风险暴露 - 降低后的风险暴露) / 降低风险的成本,如果杠杆值不够高,不足以证明采取措施的理由,那么可以采取其它措施降低风险。
10. CASE
计算机辅助软件工程,用来支持管理系统开发的、由各种计算机辅助软件和工具组成的大型综合性软件开发环境,如图工具、流程建模工具、文档编写工具、集成测试工具的集合。
11. 需求
需求是对期望的行为的表达,是用户需解决某一问题或达到某一目标所需的软件功能;
- 功能需求:描述系统预期提供的功能或服务;
- 非功能需求:指那些不直接与系统具体功能相关的一类需求,如并发性,响应时间,可靠性;
- 领域需求:源于系统的应用领域需求,如敏感信息下载后要及时删除;
12. OCL(对象约束语言)
Object Constraint Language,是一个形式化语言,和 ER 图或 UML 紧密结合在一起,用于表示类图中的不变量、前置条件、后置条件、转移条件等,帮助类图表示地更清晰。
13. 原型化需求
开发人员快速构建一个原型以确保是否符合用户需求,在需求确定后再开始正式开发;
- 抛弃型原型:编写快速但不考虑质量的原型,可以迅速抓住问题的核心,一旦确定需求就抛弃掉它,速度快但可迭代性差;
- 演化型原型:编写的原型不仅要抓住问题核心,还要迭代成为最终产品,速度慢但可迭代性高;
- 快速原型化:上述两种方法统称为快速原型化。
14. 设计过程模型:
软件系统设计是一个迭代的过程,最终结果是软件体系结构文档(SAD)。
15. 分解
自顶向下地分解软件涉及的每一个模块,帮助开发者更好地把握软件设计流程,几个流行的分解方案为:
- 面向功能的分解:把功能或需求分解成模块;
- 面向特征的分解:也是把功能或需求分解成模块,但是为模块制定了细化的特征;
- 面向数据的分解:将数据分解成模块;
- 面向进程的分解:将系统分解成一系列并发的进程;
- 面向事件的分解:将系统处理的事件分解成模块;
- 面向对象的分解:将对象分解成模块;
16. 模块化的
当系统的每个活动都仅由对应的软件单元实现,并且每个软件单元的输入和输出都已经明确地被定义时,设计才可以说是模块化的;
17. 定义明确的
如果一个软件单元的接口能够准确无误地指定该单元的外部可见行为,则称该软件单元是定义明确的;
18. 视图
将软件分解形成为构件后,将它们合适地排列以展示各个构件之间的交互和系统整体的架构的一种表示形式;体系结构视图包含以下几种:
- 分解视图:例如 UML 用例图,是层次化的分解,使用了多种模型,每个模块是可编程的;
- 依赖视图:展示了软件单元之间的依赖关系,帮助确定哪些单元是独立的,哪些是耦合的;
- 泛化视图:展示了一个软件单元是否被另一个单元所泛化,在设计抽象单元时经常使用;
- 执行视图:传统的 方框->箭头 视图,展示了系统运行时的结构,每个构件都是执行实体;
- 实现视图:在代码单元和源文件之间建立映射,有助于管理源代码;
- 部署视图:在运行实体和 IT 资源之间建立映射,帮助分析系统的底层设计;
- 工作分配视图:每个构件是分配给团队成员的任务,有助于项目管理;
19. 体系结构风格
体系结构风格反映了领域中众多系统所共有的结构和语义特性,并指导如何将各个模块和子系统有效地组织成一个完整的系统。
经典的体系结构风格:
-
管道/过滤器:将数据输入到过滤器中得到输出数据,通过管道将数据从一个过滤器中传输到下一个过滤器;
- 优点:
- 各个模块相对独立,在输入和输出定义清楚后,便于模块化开发和测试;
- 支持一些特定的分析,例如吞吐量计算和死锁检测;
- 并发性高,当一个模块的吞吐量达到瓶颈后,可以新增一个同样的构建分担压力;
- 缺点:
- 交互式处理能力弱;
- 具体实现比较复杂,例如要考虑数据流同步问题和数据加解密问题;
- 优点:
-
层次结构:整个系统被组织成一个分层结构,每一层为上层提供服务,并作为下一层的客户。
-
优点:
- 层次结构风格支持系统设计过程中的逐级抽象;
- 基于层次结构风格的系统具有较好的可扩展性;
- 层次结构风格支持软件复用;
-
缺点:
- 并不是每个系统都可以很容易地划分为分层的模式;
- 很难找到一个合适的、正确的层次抽象方法;
-
-
C/S 风格:C/S 体系结构有三个主要组成部分:客户机、服务器和专用的网络;
- 优点:①界面丰富,可操作性高;②安全性高;③响应速度快;
- 缺点:①适用范围窄;②用户群固定;③维护成本高;④需安装专门的软件;
-
B/S 风格:B/S体系结构有三个主要组成部分:浏览器、 Web 服务器、数据库服务器;
- 优点:①维护和升级方式简单;②交互性强;③对客户机的配置要求低;
- 缺点:①在速度和安全性上需要花费巨大的成本;②需要刷新页面;③通信开销大;
20. 设计原则
设计原则是指把系统功能和行为分解成模块的指导方针。
六种重要的设计原则:
-
模块化:把系统中各不相关的部分进行分离的原则,以便于各部分能够独立研究,也称为关注点分离,每个模块都有自己唯一的目的,并且相对独立于其它模块。
使用两个概念来度量模块的独立程度:耦合度和内聚度;
-
耦合度:两个模块之间存在着很强的依赖关系称为紧密耦合,两个模块之间存在较少依赖关系称为松散耦合,模块之间没有任何依赖关系称为无耦合。耦合越松散,模块之间的联系就越小,模块的独立性就越强。
常见的耦合类型:
- 非直接耦合:两个模块都能独立地工作而不需要另一个模块的存在,耦合度最低;
- 数据耦合:两个模块彼此之间通过传参交换数据,称为数据耦合;
- 标记耦合:两个模块之间传参交换复杂的数据结构变量,如数组、类名;
- 控制耦合:两个模块之间传递的信息是控制信息,表现在循环和条件判断语句中;
- 公共耦合:各模块访问同一个公共数据,如共享内存区,只读的公共耦合是松散耦合,读写的公共耦合是紧密耦合;
- 内容耦合:耦合度最高,①一个模块直接访问另一个模块的内部数据;②一个模块通过分支进入了另一个模块;③模块之间的代码发送重叠;
尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,完全不采用内容耦合。
-
内聚度:内聚是衡量一个模块内部各个元素彼此结合的紧密程度,一个模块内聚程度越高,说明该模块内部各元素之间的关联也就越强。
内聚度由低到高为:
- 偶然内聚:只是因为巧合或方便的原因,不相关的功能、进程或数据处于同一个模块中,是最差的一种内聚,模块不易理解,不易维护,不易重用;
- 逻辑内聚:模块中的各个部分只通过代码的逻辑结构相关联,但实际功能没有关联;
- 时间内聚:各个元素必须在同一时间内执行(比如系统初始化),优点是实现简单,缺点是模块结合了许多无关的任务,一旦模块失败,难以定位错误位置;
- 过程内聚:元素之间有顺序关系,调用前面元素之后,紧接着调用后面的,例如必须先进行词法分析,才能进行语法分析;
- 通信内聚:模块各功能部分都操作同一输入 ,或产生同一输出,例如语法分析和语义分析都操作词法分析的结果,都输出中间代码;
- 顺序内聚:模块内的处理元素和同一个功能密切相关,而且这些处理必须顺序执行,前一元素的输出就是下一元素的输入,例如将词法分析–>语法分析–>语义分析和中间代码生成整合成一个模块;顺序内聚模块中的各个部分在功能和执行顺序上都密切相关,构成个不可分割的整体,因此内聚程度高且易于理解;
- 功能内聚:一个模块内所有处理元素仅为完成功能而协同工作,紧密联系,不可分割,且没有副作用,则称为功能内聚。功能内聚是最高程度的内聚。在设计时应尽可能使模块到达功能内聚这一级。
-
-
接口:接口定义了模块能正确工作的环境,以及格式化的输入输出,隐藏了模块实现细节;
-
信息隐藏:对使用模块的用户隐藏模块实现相关的信息,例如用户只需要知道函数能够排序,而不需管其底层是冒泡排序还是快速排序;
-
增量式开发:将每个模块看作一个增量组件,并按优先级分批次分析、设计、开发、测试和交付增量组件;
-
抽象:忽略细节,提炼出模块之间的共性,或建设整体视图,或设计接口类;
-
通用性:开发软件时,尽量使其成为通用的软件,以便在将来将其复用到另一系统中;
21. 面向对象三大原则
封装、继承和多态;
22. 面向对象设计原则
-
单一职责原则:一个类只负责一个职责,这样可以增加模块的可复用性,系统整体高内聚、低耦合;
-
开闭原则:一个软件实体应当对扩展开放,对修改关闭,即在不改变本身代码的情况下其行为能扩展,面临新的需求时,不要更改已有的代码,而是在其基础上进行扩展;
- 抽象化是开闭原则的关键;
-
里氏代换原则:一个软件如果使用的是一个父类的话,那么一定适用于其子类,而察觉不出父类对象和子类对象的区别。也即是说,在软件里面,把父类替换成它的子类,程序的行为不会有变化,简单地说,子类型必须能够替换掉它们的父类型;
- 反过来则不成立,如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类;
- 在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象;
- 子类可以扩展父类的方法,但是不能修改父类的方法,本质上也是对开闭原则的一种完善;
-
依赖倒转原则:高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
- 要针对接口编程,不要针对实现编程;
- 常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中;
- 以抽象方式耦合是依赖倒转原则的关键;
例如,下图1未实现依赖倒转原则,图2实现了依赖倒转原则:
-
合成复用原则:尽量使用对象组合,而不是继承来达到复用的目的;
- 复用时要尽量使用 组合/聚合 关系,少用继承;
- 继承复用称为白箱复用,因为父类对子类是完全透明的,组合/聚合 复用是黑箱复用;
例如,下图1是继承复用,冗余项多且庞杂,图2是聚合复用,简介且易维护:
-
迪米特法则:又称为最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用,这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易;
- 可以降低类之间的耦合度,从而允许它们独立地被开发、优化、使用和修改;
- 迪米特法则的主要用途在于控制信息的过载,例如应尽量创造松耦合的类,尽量降低对类成员变量和函数的访问权限,尽量降低类对象对其它对象的引用次数;
例如,下图1未使用迪米特原则,图2使用了迪米特原则:
23. 面向对象设计模式
是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,设计模式通过实现面向对象七大原则,从而达到了代码复用、增加可维护性的目的。
-
单例模式:这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
- 为避免其它程序过多地建立该类的对象,先禁止其它程序建立该类对象实例;
- 饿汉式:对象预先加载,线程是安全的,在类创建好的同时对象生成,调用获得对象实例的方法反应速度快,代码简练;
- 懒汉式:对象延迟加载,效率高,只有在使用的时候才实例化对象,若设计不当线程会不安全,代码相对于饿汉式复杂,第一次加载类对象的时候反应不快。
-
建造者模式:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
-
实例:
用建造者(Builder)模式描述客厅装修。
分析:客厅装修是一个复杂的过程,它包含墙体的装修、电视机的选择、沙发的购买与布局等。客户把装修要求告诉项目经理,项目经理指挥装修工人一步步装修,最后完成整个客厅的装修与布局,所以本实例用建造者模式实现比较适合。
这里客厅是产品,包括墙、电视和沙发等组成部分。具体装修工人是具体建造者,他们负责装修与墙、电视和沙发的布局。项目经理是指挥者,他负责指挥装修工人进行装修。
另外,客厅类中提供了
show()
方法,可以将装修效果图显示出来。客户端程序通过对象生成器类ReadXML
读取XML
配置文件中的装修方案数据,调用项目经理进行装修。其类图如图所示:-
建造者模式的主要优点如下:
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象;
- 每一个具体建造者都相对独立,与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,扩展方便,符合开闭原则;
- 可以更加精细地控制产品的创建过程;
-
其缺点如下:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,不适合使用建造者模式,因此其使用范围受到一定的限制;
- 如果产品的内部变化复杂,可能会需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加了系统的理解难度和运行成本。
-
-
-
观察者模式:观察者(Observer)是指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
-
模式动机:建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。
-
实例:
利用观察者模式设计一个程序,分析人民币汇率的升值或贬值对进口公司进口产品成本或出口公司的出口产品收入以及公司利润率的影响。
分析:当“人民币汇率”升值时,进口公司的进口产品成本降低且利润率提升,出口公司的出口产品收入降低且利润率降低;当“人民币汇率”贬值时,进口公司的进口产品成本提升且利润率降低,出口公司的出口产品收入提升且利润率提升。
这里的汇率(Rate)类是抽象目标类,它包含了保存观察者(Company)的 List 和增加/删除观察者的方法,以及有关汇率改变的抽象方法 change(int number);而人民币汇率(RMBrate)类是具体目标, 它实现了父类的 change(int number) 方法,即当人民币汇率发生改变时通过相关公司;公司(Company)类是抽象观察者,它定义了一个有关汇率反应的抽象方法 response(int number);进口公司(ImportCompany)类和出口公司(ExportCompany)类是具体观察者类,它们实现了父类的 response(int number) 方法,即当它们接收到汇率发生改变的通知时作为相应的反应。下图所示是其 UML 结构图。
-
观察者模式的优点:
- 具体目标和具体观察者是松耦合关系,观察者接口的引入降低了系统的复杂度;
- 观察模式满足“开-闭原则”。目标接口仅仅依赖于观察者接口,系统的可复用性和可扩展性得到提升。
-
观察者模式的缺点:
- 如果一个观察者关联很多实例的话,将所有的实例都通知到会花费很多时间;
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
-
-
中介者模式:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
-
实例:
用中介者模式编写一个“房地产交流平台”程序。
分析:首先,定义一个中介公司(Medium)接口,它是抽象中介者,它包含了客户注册方法 register(Customer member) 和信息转发方法 relay(String from,String ad);再定义一个房地产中介(EstateMedium)公司,它是具体中介者类,它包含了保存客户信息的 List 对象,并实现了中介公司中的抽象方法。
然后,定义一个客户(Customer)类,它是抽象同事类,其中包含了中介者的对象,和发送信息的 send(String ad) 方法与接收信息的 receive(String from,String ad) 方法的接口,由于本程序是窗体程序,所以本类继承 JPmme 类,并实现动作事件的处理方法 actionPerformed(ActionEvent e)。
最后,定义卖方(Seller)类和买方(Buyer)类,它们是具体同事类,是客户(Customer)类的子类,它们实现了父类中的抽象方法,通过中介者类进行信息交流,其结构 UML 图如下:
-
中介者模式的优点为:
- 类之间各司其职,符合迪米特法则;
- 降低了对象之间的耦合性,使得对象易于独立地被复用;
- 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展;
-
中介者模式的缺点为:
- 中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。
-
24. 复用
有两种类型的复用:生产者复用和消费者复用;
- 生产者复用:正在设计的构件要在以后的应用中进行复用;
- 消费者复用:正在使用的构件是之前为其它应用开发的构件;
25. 编程过程
XP、结对编程、融合、小组协同;
26. 软件测试术语
- 错误(error):往往是人为导致的故障;
- 故障(fault):程序中不正确的步骤,过程或者数据定义,导致程序出现了非故意的、不可预料的行为,通常是由
error
导致的; - 失效(failure):一个系统或者组件不能完成它被要求的功能,通常是
fault
导致的;
27. 故障类型
- 算法故障:源代码编写或算法设计上的一些错误导致的故障;
- 计算故障和精度故障:算术表达式错误或计算结果未达到要求的精度;
- 文档故障:文档与程序实际做的事情不一致;
- 能力故障或边界故障:系统活动到达指定的极限时,系统性能会变得不可接受;
- 计时故障或协调故障:实时系统的并发进程之间同步互斥关系出现故障;
- 吞吐量故障或性能故障:系统不能以需求规定的速度执行;
- 标准和过程故障:代码规范或风格不合符组织的要求;
28. 软件测试基本步骤
- 单元测试:将系统中的每个程序构件与其它构件隔离,单独测试每个构件是否能正常运行;
- 集成测试:确保构件之间的接口能正常运行,各构件正常协作;
- 功能测试:是对系统的整体评估,以确定集成的系统是否确实执行了需求规格中描述的功能,其结果是一个可运转的系统;
- 性能测试:测试系统在客户的实际工作环境中能否成功执行,其结果是一个确认的系统;
- 验收测试:与客户交换意见,以确定满足了客户的预期,与客户进行确认后完成验收;
- 安装测试:将验收后的系统安装在它实际工作的环境中,确保能正确工作。
29. 单元测试方法
-
静态测试(程序不执行):一般是检查代码的风格和规范。
- 静态分析器(自动工具);
- 代码评审(人工方式);
-
动态测试(程序执行):通过选择适当的测试用例,执行程序。
-
黑盒测试(测试功能):不考虑程序的内部结构与特性,只根据程序功能或程序的外部特性设计测试用例。
-
等价类分类法:将可能的输入划分成若干等价的类,每一个类选择一个测试用例;
- 有效等价类:有意义、合理的输入数据,用于评估系统功能;
- 无效等价类:无意义、不合理的输入数据,用于检测程序异常;
例如,注册某网站。要求:用户名的长度为812的数字与字母组合而成的字符,,密码长度为616位的数字、字母的组合。请写出测试案例:
确定了等价类之后我们就可以设计测试用例了,测试用例需要覆盖到所有的等价类,即有效等价类和无效等价类。具体情况请看下表:
-
边值分析法:对输入的边界值进行测试,例如,对16-bit 的整数而言 32767 和 -32768 是边界;屏幕上光标在最左上、最右下位置。
-
错误推测法:在测试程序时,人们可以根据经验或直觉推测程序中可能存在的各种错误,从而有针对性地编写检查这些错误的测试用例的方法。
-
因果图法:适合输入条件比较多的情况,可以测试所有的输入条件的排列组合。“ 因 ” 就是输入条件,“ 果 ” 就是输出结果。
-
-
白盒测试(测试结构):分析程序的内部逻辑结构,注意选择适当的覆盖标准,设计测试用例,对主要路径进行尽可能多的测试。
白盒法又称为逻辑覆盖法,常用的逻辑覆盖标准:
- 语句覆盖:程序中每个语句至少都能被执行一次;
- 判定覆盖:程序中每个判定至少为
true
或false
各一次; - 条件覆盖:判定语句中的每个条件表达式都至少为
true
或false
各一次,满足条件覆盖的,不一定满足判定覆盖; - 判定/条件覆盖: 同时满足判定覆盖和条件覆盖;
- 条件组合覆盖: 判定中条件的各种可能组合都至少出现一次,例如判定语句
i
有n
个条件,每个条件有true
和false
选项,则至少需要2^n
个用例才能覆盖语句i
。
-
30. 集成测试
把功能模块或程序单元组合起来进行测试,发现模块在组合过程中的缺陷。
- 驱动模块:用于模拟待测模块的上级模块。驱动模块在集成测试中接受测试数据,将相关的数据传送给待测模块,启动待测模块,并打印出相应的结果;
- 桩模块:用于模拟待测模块工作过程中所调用的模块。桩模块由待测模块调用,它们一般只进行很少的数据处理,以便于检验待测模块与下级模块的接口。
集成测试的分类:
-
非增量式集成策略:对所有模块进行单元测试后,将各模块连接起来,把连接后的程序当作一个整体进行测试。
-
增量式集成策略:逐次将未曾集成测试的模块和已经集成测试的模块(或子系统)结合成程序包,再将这些模块集成为较大系统,在集成的过程中边连接边测试,以发现连接过程中产生的问题。
增量式集成测试又可以分为三种不同的方法:
-
自顶向下增量式测试:模块集成的顺序是首先集成主控模块(主程序),然后依照控制层次结构向下进行集成。从属于主控模块的按
DFS
或BFS
方式集成到结构中去。整个过程由
3
个步骤完成:- 主控模块作为测试驱动器;
- 下层的桩模块一次一次地被替换为真正的模块;
- 每个模块被集成时,都必须进行单元测试。重复第2步,直到整个系统被测试完成。
例题:对如下结构采用自顶向下深度优先策略进行测试:
优缺点分析:
- 优点:①较早地验证了主要控制和判断点;②可以首先实现和验证一个完整的软件功能;③功能较早证实,带来信心;④只需一个驱动,减少驱动器开发的费用;
- 缺点:①桩的开发量大;②底层验证被推迟;
- 适用范围:①产品控制结构比较清晰和稳定;②高层接口变化较小;③底层接口未定义或经常可能被修改;④产品控制组件具有较大的技术风险,需要尽早被验证;⑤希望尽早能看到产品的系统功能行为。
-
自底向上增量式测试:最常用的集成策略,从具有最小依赖性的底层组件开始,按照依赖关系树的结构,逐层向上集成,以检验系统的稳定性。
整个过程由
4
个步骤完成:- 起始于模块依赖关系树的底层叶子模块;
- 使用驱动模块对步骤1选定的模块进行测试;
- 用实际模块代替驱动模块,与它已测试的直属子模块组装成一个更大的模块进行测试;
- 重复上面的行为,直到系统最顶层模块被加入到已测系统中。
优缺点分析:
-
优点:①对底层组件行为较早验证;②工作最初可以并行集成,比自顶向下效率高;减少了桩的工作量;③能较好锁定软件故障所在位置。
-
缺点:①驱动的开发工作量大;②对高层的验证被推迟,设计上的错误不能被及时发现。
-
适用范围:①适应于底层接口比较稳定;②高层接口变化比较频繁;③底层组件较早被完成。
-
三明治增量式测试:把系统划分成三层,中间一层为目标层,目标层之上采用自顶向下集成,之下采用自底向上集成。
整个过程由
4
个步骤完成:- 首先对目标层之上一层使用自顶向下集成;
- 其次对目标层之下一层使用自底向上集成;
- 其三,把目标层下面一层与目标层集成;
- 最后,把三层集成到一起。
优缺点分析:
- 优点:集合了自顶向下和自底向上两种策略的优点;
- 缺点:中间层测试不充分,难以选择;
- 适用范围:适应于大部分软件开发项目。
-
31. 软件缺陷数目估计方法
-
播撒模型:
-
Mills 模型:人工随机置入错误 M 个错误,测试得出 m 个人工置入的错误,n 个程序固有的错误,则估算系统固有错误为:
N = n × M m N=\frac{n×M}{m} N=mn×M -
Hyman 模型:两人同时进行测试,A 发现 n 个错误,B 发现 m 个错误,其中共同错误有 q 个,则估算系统的固有错误为:
N = m × n q N=\frac{m×n}{q} N=qm×n
-
-
静态模型:根据软件的规模和复杂性进行估计。
-
根据测试覆盖率的预测模型:
2. 综合题
2.1 绘制 AOE 网
步骤:
- 计算事件的最早发生时间表,
earliest(j) = max{earliest(i) + w(i, j)}
; - 计算事件的最晚发生时间表,
latest(i) = mim{latest(j) - w(i, j)}
;
例1
如下图所示的AOE网(弧上权值代表活动的持续天数,求:
1)绘制事件的最早发生时间和最晚发生时间;
2)绘制活动的最早开始时间和最晚开始时间;
3)哪些是关键活动,给出关键路径;
4)完成此工程最少所需要多少天。
2.2 COCOMO 模型
基本工作量计算公式如下:
E = a × K L O C b × F E=a×KLOC^b×F E=a×KLOCb×F
① 基本 COCOMO:
方式 | a | b |
---|---|---|
有机 | 2.4 | 1.05 |
半有机 | 3.0 | 1.12 |
嵌入式 | 3.6 | 1.2 |
E = a × K L O C b × 1 E=a×KLOC^b×1 E=a×KLOCb×1
一个 33.3 KLOC
的软件开发项目,属于中等规模、半有机型的项目,采用基本COCOMO
:a = 3.0,b = 1.12,则:
E = 3.0 × 33. 3 1.12 × 1 = 152 p m E= 3.0×33.3^{1.12}×1=152pm E=3.0×33.31.12×1=152pm
② 中等 COCOMO:
方式 | a | b |
---|---|---|
有机 | 2.8 | 1.05 |
半有机 | 3.0 | 1.12 |
嵌入式 | 3.2 | 1.2 |
F = ∏ i = 1 17 E M i F=\prod_{i=1}^{17}{EM_i} F=i=1∏17EMi
E = a × K L O C b × F E=a×KLOC^b×F E=a