Notes for FIT5136: Software engineering
FIT5136个人感觉应该是S1这学期里理论知识上收获最大的一门课(实践最多的是FIT5046安卓开发)
W1: Introduction
SE: 一门工程学科,涉及从最初的概念到操作和维护的软件生产的所有方面。
CS vs SE:计算机科学侧重于理论和基础; 软件工程关注开发和交付有用的软件的实用性
重要性:
• 本身就是一门学科
• 越来越多的系统由软件控制,因此正确设计软件并不是一个坏主意
• 所有发达国家的经济都依赖于软件
• 软件成本通常在计算机系统成本中占主导地位
• 延迟和失败的项目需要更多的成本、时间和资源
• 需要能够经济快速地生产可靠且值得信赖的 (!) 系统
• 从长远来看,为软件系统使用软件工程技术和方法比仅仅为编程项目编写程序更便宜
• 尽管它很重要,但仍然没有一个明确的定义!
好软件的属性:
- 可维护性:软件应该以这样的方式编写,以便它可以发展以满足客户不断变化的需求。
- 可靠性和安全性:不应在系统故障时造成物理或经济损失。 恶意用户不应能够访问或损坏系统。
- 效率:不应浪费系统资源,例如内存和处理器周期——响应能力、处理时间、内存利用率等。
- 可接受性:必须能够被其设计的用户类型所接受。 必须易于理解、可用并与他们使用的其他系统兼容。
囊括:
- 人:Team, Client, Users
- 围绕软件生命周期的过程:Agile, Waterfall
- 技术:例如platforms,object-oriented design/models,languages,methods (testing),tools (IDE)
Software Engineering Body of Knowledge (SWEBOK6)
软件工程师需要了解以下大部分(如果不是全部):
• 软件要求
• 软件设计和构建
• 软件质量和测试
• 软件维护
• 软件配置管理
• 软件工程管理
• 软件工程模型和方法
• 软件工程专业实践
• 软件工程经济学
• 计算、数学和工程方面的基础知识
经济角度:
- 软件成本通常高于硬件成本
- 软件的维护成本高于开发成本
- 对于长寿命系统,维护成本可能是开发成本的几倍。
- 软件工程关注具有成本效益的软件开发
软件过程:
• 开发软件系统所需的一组结构化活动
• 将任务分解为更小的步骤
• 许多不同类型的软件过程,但所有过程都涉及以下活动:
- 需求和分析(规范)——定义系统应该做什么
- 设计和实现——定义系统应该如何做,组织制度和执行制度
- 系统验证和确认——检查它是否符合客户的要求
- 革新(维护)——改变系统以响应不断变化的需求
付出和成本
- 糟糕的软件只能运行一次
- 不能重复使用、回收或进化。
- 对团队和公司不利
- 好的软件会进化,成为更大系统的一部分,更容易集成到另一个系统
- 可维持 10、20 年以上!
- 可以轻松应对需求的变化
- 可以轻松完成技术(例如数据库)的更改
- 大部分成本用于在软件投入使用后对其进行更改(扩展和增强)。
- 在生命周期的早期修复故障更容易、更便宜
- 软件的维护成本高于开发成本,尤其是对于寿命较长的系统。
过程模型
- 计划驱动的流程
- Waterfall 瀑布生命周期模型
- Rapid prototype 快速原型生命周期模型
- 增量开发
- 敏捷方法论 ->有staff支持,有iteration
- 统一流程模型 The unified process model
- 同步和稳定模型
- 螺旋 Spiral 生命周期模型
- 开源软件开发
Code-and-fix
(也就是solo)的弊端
• 生命周期后期的变更成本要高得多
• 无需求、分析、设计阶段——在实施或维护期间捕获所有错误
• 没有规范或设计文件,维护极其困难,更容易出现回归故障
• 仅适用于有限的情况
Waterfall
• 在计划驱动的流程中,所有活动都已计划好,并根据该计划衡量进度。
• 瀑布生命周期模型需要有具体的计划来完成项目。 当需求被很好地理解时,可以保证在设计过程中更改是有限的。
• 流程没有对错之分,然而,瀑布模型为变化或频繁的客户交互留下了有限的空间。
• 对于在多个地点开发系统的大型系统工程项目,瀑布模型的计划驱动性质有助于协调工作。
• 移动目标问题使得使用瀑布模型难以保持正轨
• 在进入下一阶段之前必须完成一个阶段
•(Schach 第 52 页)“一般来说,规范文档很长、很详细,而且坦率地说读起来很无聊”
(就是照本宣科)
不能适应由于公司变更、扩张,客户更改,Feature creep(就不断要求feature)导致的moving-target问题
对软件所做的任何更改都可能导致回归故障(软件明显不相关部分的故障)
加个带反馈的。
但是仍然不能解决问题
• 在每个阶段结束时,将生成技术文档——例如 UML 图
• 这些客户难以理解
• 在每个阶段,它没有显示成品将如何工作以及它的外观……
• 结果,当客户看到最终产品时……
Rapid prototype 快速原型生命周期模型
原型是系统的初始版本,用于演示概念和尝试设计选项。可用于
- 需求工程(W2
- 开发 UI 设计的设计流程(W7
- 在测试中运行黑盒测试(W11
快速原型制作 = 在功能上等同于产品子集的工作模型
它是面向客户的,客户可以在继续之前就原型提供反馈
moving-target问题解法:
- “没有已知的解决方案”
- 但是,为更改制作文档将有助于团队和向客户收费
- 变化是不可避免的
- 成长中的公司总是会改变
- 即便呼吁变革的个人有足够的影响力,依然无能为力
增量开发
大纲描述:概述系统要求
所有活动都相互交错,而不是分开,跨活动快速反馈。
- 需求和分析(规范)
- 设计和实施(开发)
- 验证(测试)
初始版本:开发初始实施
中间版本:从客户和用户那里获得反馈并通过版本改进软件直到系统开发
每个“增量”包括客户端请求的一些功能。
好处
- 降低适应变化的成本
- 更容易获得客户对已完成开发工作的反馈
- 可以更快速地向客户交付和部署有用的软件
问题
- 进程不可见。 管理人员需要定期交付成果来衡量进度。 如果系统开发得很快,那么生成反映系统每个版本的文档并不划算。
- 随着新增量的增加,系统结构趋于退化。 除非将时间和金钱花在重构以改进软件上,否则定期更改往往会破坏其结构,合并进一步的软件更改变得越来越困难且成本高昂。
敏捷开发
- 基于增量开发,敏捷于 1990 年代后期出现,以减少工作软件系统的交付时间
- 适用于客户不确定系统规格的情况
- 快速开发和交付至关重要。 软件必须快速发展以反映不断变化的业务需求。
- 渐进式发展:
o 程序规范、设计和实现是交错的
o 客户和用户参与版本规范和评估的一系列版本或增量 - 广泛的工具支持:例如 自动化测试工具,Trello
- 最少的文档——专注于工作代码
框架
Kanban
- 看板就是关于可视化您的工作、限制正在进行的工作和最大限度地提高效率。
- 看板团队专注于减少项目从开始到结束所花费的时间。
Scrum
- Scrum 是帮助团队合作开发、交付和维持复杂产品的框架
- 一切都分为时间框架
统一流程模型
The Unified Process 重点
- 一个迭代的、增量的、面向对象的流程模型
- 对于 FIT5136,我们专注于统一流程:(好家伙我到期末一直都以为是用的agile
- 学习敏捷概念(右倾
- 制作一些用于学期内评估的文件(左倾
开源软件开发
略:No standard lifecycle model
W2 需求工程
基于 IEEE 软件工程术语表
- 用户解决问题或实现目标所需的条件或能力。
- 系统或系统组件为满足合同、标准、规范或其他正式强制文件而必须满足或拥有的条件或能力。
- (1) 或 (2) 中的条件或能力的书面表示。
- 定义、记录和维护需求的过程
- 软件工程过程的一部分
- 建立客户从系统中要求的服务及其运行和开发的约束的过程。
- 系统需求是对需求工程过程中产生的系统服务和约束的描述。
- 客户提供的系统需求通常是非正式的和简短的,因此找到系统详细需求的过程就是需求工程
需求
需求的类型
- 业务:业务需要系统做什么
- 用户:用户(或客户)要求系统做什么。 用户可以是利益相关者。
- Functional 功能性:系统在功能上应该做什么
- Non-functional 非功能性:系统需要提供的服务质量
- 约束:系统应该禁止用户做的事情
- 实现:系统的平台等要求
• 完整需求: 它们应包括实现功能所需的所有设施的描述。
• 持续需求:描述中不应有冲突或矛盾。
在实践中,由于系统和环境的复杂性,不可能产生完整且一致的需求文档。
准确性
-
需求不得含糊不清、不完整和不一致
-
当功能性需求没有被准确表述时就会出现问题。
-
开发人员和用户可能以不同的方式解释不明确的需求。
例如:考虑“用户应该能够搜索所有郊区的房产。”
- 一种解释:在所有郊区搜索特定的房产。
- 另一种解释:搜索特定郊区的房产。 用户选择郊区然后搜索。
Functional requirements
描述功能或系统服务
描述系统应如何对特定输入做出反应以及系统在特定情况下应如何运行
表示为输入和输出
例子:
- 用户应能够搜索所有郊区的房产。
- 每月月底,系统应生成已售或出租的物业清单
- 每个房地产经纪人都应由他或她的 8 位员工编号唯一标识。
Non-functional requirements
• 可用于判断系统运行情况的特定标准,而不是特定行为
• 系统提供的服务或功能的细节。 使系统质量更好的要求。
• 例如,允许的客户数量、密码要求、响应时间、存储要求、I/O 设备能力等。
重要性:
- 通常适用于整个系统而不是单个特征或功能。
- 非功能性需求可能比功能性需求更重要。
- 如果不满足,则系统可能无法使用。
例如
• 物业管理系统应在正常工作时间(周一至周五,08:30-17:30)提供给所有物业代理。
• 任何一天内正常工作时间内的停机时间不得超过五秒。
• 客户必须使用他们的驾驶执照进行身份验证。
• 收到检验请求后,系统必须在 10 秒内回复电子邮件进行确认。
• 流程要求,例如 强制要求特定的 IDE、编程语言或开发方法。
• 在某些情况下,如果客户不确定,他们希望您推荐
实现
• 非功能性需求通常影响系统的整体架构而不是单个组件。
• 例如,为确保满足性能要求,“设计”系统以尽量减少组件之间的通信。
• 单个非功能性需求,例如安全性需求,可能会生成多个相关的功能性需求,这些需求定义了所需的系统服务
需求工程过程
- 用于 RE 的过程因应用领域、涉及的人员和制定需求的组织而异。但是,所有流程都有许多通用活动
- 需求启发;
- 需求分析;
- 需求验证;
- 需求管理。
在实践中,RE 是一种迭代活动,其中这些过程是交错的。
elicitation and analysis 需求启发和分析
• 发现客户的要求:• 需求获取(或需求收集)• 方法包括访谈和调查
• 完善和扩展初始要求:• 需求分析
• 让技术人员与客户/客户一起工作,以了解有关应用领域、系统应提供的服务以及系统的操作限制的更多信息。
• 可能涉及利益相关者——最终用户、经理、参与维护的工程师、领域专家、工会等。
确定需求(需求验证)
• 很难将软件产品及其功能可视化:从客户角度,可视化问题要更加严重
• 需要从客户那里获取适当信息的技巧
• 客户是此信息的唯一来源
解决方案:
• 从客户端获取初始信息
• 将此初始信息用作软件工程过程的输入
• 按照步骤确定客户的真实需求
步骤:
一、了解应用领域(或简称领域)
• 目标产品运行的特定环境
二、构建商业模式
• 为客户的业务流程建模
三、用商业模式来确定客户的需求
重复上述步骤
域
开发团队的每个成员都必须完全熟悉应用程序领域
• 正确的术语terminology至关重要
构建词汇表glossary
• 领域中使用的技术词及含义的列表
业务模型
业务模型是对组织业务流程的描述
需求收集技术Requirements gathering techniques
• 采访
• 问卷
• 现有文件和系统
• 研讨会
• 领域专家
• 直接观察
• “驾驭卡车”——了解业务
• 原型制作
具体介绍略
需求规范
在需求文档中写下用户和系统需求的过程。
• 没有技术背景的最终用户和客户必须能够理解用户要求。
• 系统要求是更详细的要求,可能包括更多的技术信息。
• 要求可能是系统开发合同的一部分
• 因此,尽可能完整是很重要的。
用例图
用例(用户案例)对软件产品本身与该软件产品的用户(即Actor)之间的交互进行建模
- 使您对需求的理解正式化的工具。
- 捕获一些用户可见的功能或行为。
- Actor和用例之间的关系
用例图不是:
- 流程图!
- 用例的描述。
- 定义用例的一系列步骤
用例图应该很简单!
迭代和递增,直到你做对了!
Note:
请遵循这些幻灯片中描述的用例图 UML 语法。 参考书 Satzinger et al。 使用不同的语法。
定义
- Actor是软件产品之外的世界的成员
- 通常很容易识别Actor
- Actor通常是软件产品的用户
- 大多数软件都有不止一种类型的Actor
- 一般来说,参与者在软件产品方面发挥作用。 一个Actor:
- 作为发起人:通常是发起用例的参与者;放置在用例图的左侧
- 作为参与者:通常是参与用例的参与者;放置在用例图的右侧;一个Actor可以参与多个用例
- 作为在用例中扮演关键角色的人
- 作为扮演多个角色的人。 例如。 工作人员可以是讲师和助教
Actor不必是人
- 注意:这个系统永远不能是一个Actor。 其他系统可以。
示例:电子商务信息系统必须与信用卡公司信息系统交互 - 从电子商务信息系统的角度来看,信用卡公司信息系统是一个Actor
- 信用卡公司是电子商务系统用例图中的参与者。
- 从信用卡公司信息系统的角度来看,电子商务信息系统是一个Actor
- 电子商务系统是信用卡系统用例图中的参与者。
Overlapping Actor(Actor泛化)
或者:
• 如果两个参与者以相同的方式与同一组用例进行通信,我们可以将其表示为对另一个(可能是抽象的)参与者的泛化。
• 泛化(继承/类型)
包含和扩展
包含:
• <<Include>>
• 包含是用例的强制性部分。
• 包含用例永远不会单独存在。 它始终包括包含的用例
扩展:
• <<Extend>> 用于将可选行为与强制性行为分开
行为。
• 扩展用例可能是独立的,但在某些条件下,它可能会被另一个用例扩展。
good solution
User stories 用户故事
• 当团队使用敏捷方法开发系统时编写用户故事。
• 用户故事应该非常简短,并从客户的角度编写——它们通常需要放在索引卡上。
• 由于它们是根据需要来表述的,因此通常很容易为它们编写测试。
• 这些用户故事应以客户的语言编写,并使用适合其业务的术语。
• 客户了解用户故事很重要,因为敏捷方法通常涉及客户反复选择接下来要实施的用户故事。
• 对于每个用户故事,需要包括对实现所需功能或用户故事大小或与难度或优先级相关的故事点所需的时间的估计。
例如:
- 作为调查参与者,我希望获得进度指示,以便我知道我还有多少要完成
- 作为一个 wiki 用户,想上传一个文件到 wiki 以便我可以与我的同事共享
- 作为客户,我希望系统不会损坏数据库。
使用 INVEST 助记符是制作优秀用户故事的便捷指南:
- Independent 独立:用户故事应该是独立的。
- Negotiable 可协商:用户故事应该能够轻松修改或替换,例如使用满足相同结果的不同用户故事。
- Valuable 有价值:用户故事必须对最终用户有益。
- Estimable 可估算的:用户故事应该对应于可以估算实施工作量的功能。
- Small 小:用户故事必须足够小,以便独立考虑和安排。
- Testable 可测试:用户故事必须提供足够的信息来验证它已被令人满意地实施。 某些任务或要求可能与以用户为中心的功能无关。 您可以根据用户来描述这些内容,将它们写为开发人员故事,或者只是在内部跟踪它们。
你需要利益来评估
- Negotiable 可协商:在 不知道结果(收益)的情况下难以协商
- Valuable 有价值:如果不知道用户故事给用户带来什么好处,就无法知道用户故事的价值
- Testable 可测试:有时可以通过解释进行测试,但我们不想将其留给假设
Acceptance criteria 验收标准
验收标准定义了用户故事的边界,用于确认软件何时按预期工作,即故事何时完成。简单来说,就是测试用户故事是否按预期实施的标准。
- 对于智能家居应用程序,其中一个用户故事可能是:“作为房主,我希望能够调暗或调亮单个灯泡,这样我就可以创造出理想的照明。”, 此用户故事的验收标准可能是:
- 用户将选择一个地球仪,然后看到这个视图;
- 此视图应使用滑块控件,以便用户拥有可用的全部亮度级别;
- 打开此视图时,初始滑块位置应反映所选地球的当前亮度;
- 光球响应用户更改的时间不应超过 1 秒。
W3 分析与OOP理论
系统建模
开发一个系统的抽象模型,每个模型代表该系统的不同视图或视角。
• 使用图形符号表示系统,例如统一建模语言 (UML)。
• 使用系统建模
• 在需求工程期间了解系统的功能并与客户沟通。
• 在设计期间向实施系统的工程师描述系统
• 用例图是系统建模的一个例子。
perspective and viewpoints 系统视角和观点……
• 外部视角——对系统的上下文或环境进行建模。
• 交互视角——对系统与其环境之间或系统组件之间的交互进行建模。
• 结构视角——对系统的组织或系统处理的数据结构进行建模。
• 行为视角——对系统的动态行为及其对事件的响应方式进行建模。
UML
UML 是一种标准语言,用于指定、可视化、构建和记录软件系统的人工制品,以及用于业务建模和其他非软件系统。
分类:
- 结构图,根据构成系统的组件及其关系显示系统的组织。
- 静态图,显示系统设计的结构,或
- 动态图,显示系统执行时的组织结构。
- 行为图,显示系统中对象的动态行为。例如,不同组件如何相互通信以执行任务。
类别
- 用例图,显示系统与其环境之间的交互,以及提供更多交互细节的用例场景。
- 类图,显示系统中的对象类以及这些类之间的关联。
- 交互图,显示参与者与系统之间以及系统组件之间的交互。
• 序列图
• 通讯图
• 以前称为协作图。 - 状态图,显示系统如何对内部和外部事件做出反应。
- 活动图,显示流程或数据处理中涉及的活动。 (本课程未涵盖)
作为促进关于现有或提议系统的讨论的一种方式
• 不完整和不正确的模型都可以,因为它们的作用是支持讨论。
记录现有系统
• 模型应该是系统的准确表示,但不必是完整的。
作为可用于生成系统实现的详细系统描述
• 模型必须正确且完整。
需求分析
目的:
- 对需求有更深入的了解
- 系统建模
- 以可维护的设计和实现的方式描述它们
增量和迭代地执行以下三个步骤
- 功能建模:所有用例的当前场景(一个场景是一个用例的实例)
- 类建模:确定实体类及其属性;实体类之间的相互关系和相互作用;以类图的形式呈现这些信息
- 动态建模:确定由或对每个实体类执行的操作;以状态图的形式呈现此信息
• 面向对象分析 (OOA) 是面向对象范式的半形式分析技术。
• 它是用例驱动的
• 在分析期间,类被提取
第一步:功能建模 - 用例场景
所有用例的呈现场景
- 场景 = 用例的描述
- 场景可以是“ 正常”和“例外”
正常情况:用户和系统之间的一组交互,对应于我们理解系统应该如何用于实现目标的方式。
异常场景:描述如何处理不希望发生的事件的场景,即干扰目标进展的事件。或者作为干扰正常场景的异常条件的结果的场景。
用例场景模板
Use Case Name 用例名称:简短的描述性动词短语;
Scenario: 捕捉用例本质(功能)的句子;
Trigger 触发器:实际触发用例的参与者;
Brief description 简要描述:描述用例目标的段落;
Actors 参与者: 用例中涉及的参与者(发起者和参与者参与者);
Related use cases 相关用例:该用例是否与其他用例有任何关系 - 包含/排除/泛化?
Preconditions 先决条件:在用例可以执行之前必须为真的事情——它们是对系统状态的约束;
Postconditions 后置条件: 在用例结束时必须为真的事情;
Flow of events 事件流:用例中的步骤(如果一切顺利——没有错误、中断);
Exception conditions 异常条件: 主要流程的替代列表(捕获错误,中断主要流程)。
软件需求规范
不在考试范围
• 软件需求规范(SRS) 是对要开发的软件系统及其功能和非功能需求的详细描述。
• SRS 是根据客户与您之间的协议制定的。
• 它可能包括用户将如何与软件系统交互的用例。
• 软件需求规范文档与项目开发所需的所有必要需求一致。
• 要开发软件系统,我们应该对软件系统有清晰的认识。
• 为实现这一目标,我们需要与客户持续沟通以收集所有需求。
• 一个好的 SRS 定义了软件系统如何与所有内部模块、硬件、与其他程序的通信以及人类用户与广泛的现实生活场景交互。
• 使用 QA 主管的软件需求规范 (SRS) 文档,管理人员创建测试计划。
• 测试人员必须清楚本文档中指定的每个细节,以避免测试用例及其预期结果中的错误,这一点非常重要。
• 强烈建议在开始编写测试用例和制定任何测试计划之前查看或测试 SRS 文档。 让我们看看如何测试 SRS 以及测试时要记住的重点。
- 应检查SRS的正确性
- 应避免歧义
- 要求要完整
- 一致的要求
- 预期结果的验证
- 测试环境
- 安全和性能标准
- 应避免假设
第二步:使用类图进行类建模
• 类图用于开发面向对象的系统模型以显示系统中的类以及这些类之间的关联。
• 对象类可以被认为是一种系统对象的一般定义。
• 当您在软件工程过程的早期阶段开发模型时,对象代表现实世界中的某些事物,例如患者、处方、食谱、医生、讲师、学生等。
UML类图内必要元素
- 类
- 属性
- 操作
- 关系
- 联系
- 归化
- 组合/聚合
- 依赖
- 约束规则和注意事项
OOP面向对象的概念修订
对象通常来自以下来源之一(最初由 Shlaer 和 Mellor 提供,后来由 Booch 转述,并在此处进一步改编)
• 有形的或“现实世界的东西”:书、记事本、小册子;
• 角色:图书馆成员、学生、教育主任;
• 事件:到达、离开、请求;
• 互动 : 会议、交集。
• 它们类似于现实生活中的“对象”
• 描述对象的数据(成为它的属性/特性)
• 可以由/在对象上执行的操作/方法。
一个类描述了一组对象
• 相似的属性(属性),
• 常见行为(操作),
• 与其他对象的共同关系,
• 和普通含义(“语义”)。
一个类有
• 数据属性/属性:与其实例相关的信息
• 操作/方法:对象支持的功能
• 类是对个人共同点的描述。
• 对象是具有不同价值观的个人。
UML作图略
• 一个类用 UML 符号表示为一个带有三个隔间的盒子
• 类属性:是我们想要捕获的关于类的信息!
• 例如,在现实世界中,当我们谈论一个人时,我们通常谈论
• 他们的姓名、地址、生日……
• 请注意,根据不同的域,我们可能需要捕获不同的属性!
描述类
• 绘制类时,不必在每个图中都显示属性和操作
问题域中的类
• 需要先确定类别!
他们之间的关系
• “Collaborates with”(association关联)
• “is-a”(inheritance or generalisation 继承或泛化)
• “has-a”(aggregation or composition 聚合或组合)
(后面的内容)
继承与多态
继承略
1 | public class Circle extends Shape {} |
Polymorphism 多态
源自希腊语 Poly => many 和 morph => form, shape。 所以多态意味着以许多不同的形式出现
• 多态性是对象具有多种形式的能力。
• 当使用父类引用来引用子类对象时,OOP 中多态性的最常见用途发生。
• 多态性使您能够“在一般情况下编程”而不是“在特定情况下编程”。
• 多态性使您能够编写直接或间接处理共享相同超类的对象的程序
• 实现可扩展性 Extensibility
• 只要新类是程序一般处理的继承层次结构的一部分,就可以在对程序的一般部分稍加修改或不修改的情况下添加新类。
• 依靠每个对象知道如何“做正确的事”(即,做适合该类型对象的事情)以响应相同的方法调用是多态的关键概念。 发送到各种对象的相同消息(在这种情况下,声音)具有多种形式的结果——因此术语多态性。
• 在运行时自动选择合适的方法称为动态绑定。
• 回忆:对象使用方法相互通信。
例如。 另一个对象可以执行以下代码与 aCircle
通信
使用 calcArea()
方法:
1 | Circle aCircle = new Circle(5); |
• 可以通过调用相同的方法将相同的消息(您需要计算面积)发送到不同的对象。
1 | aSquare.calcArea(); |
• 每个对象以不同的方式“响应”同一消息。
• 这称为多态性。
抽象类
• 当我们想到一个类时,我们假设程序将创建该类型的对象。 有时声明类(称为抽象类)很有用,您从未打算为其创建对象。
• 抽象超类过于笼统,无法创建真正的对象——它们只指定子类之间的共同点
• 抽象类的主要目的是提供一个合适的超类,其他类可以从该超类继承并因此共享共同的设计。
• 一个抽象类通常包含一个或多个抽象方法,如果它们是具体的,子类必须覆盖这些方法。
• 抽象类不完整
• 抽象方法不提供实现。
• Java 中的抽象方法没有方法的body,并在声明后以分号终止。
• 具有至少一种抽象方法的类必须声明为抽象的。
• 来自超类的抽象方法必须在子类中实现,否则Java 将给出错误消息。
• Shape 抽象类中的两个方法都必须在 Circle 和 Rectangle 子类中具有主体(或声明为抽象)。
W4 团队合作,统一流程,更多关于敏捷、VCS
团队
• 它包含在澳大利亚工程师第一阶段的能力中
• 团队合作是澳大利亚工作文化的重要组成部分。
• 大多数软件产品对于单个程序员来说太大了(在可用时间内)
• 如果软件组件由不同的人编写,则该软件可以满足更多人群的需求
• 不仅在 IT,还有 Coles、Kmart 等商店
• 你们争论但也互相学习
• 您将学习如何管理您的工作和您的团队
• 您将学习如何处理复杂的情况
• 您将学习如何与来自不同文化和经历的人一起工作
类型:
- 民主党队
- 首席编程团队
- 现代编程团队
- 同步和稳定团队
- 极限编程团队
- Scrum 团队
- 开源编程团队
极限编程团队
敏捷软件开发技术
- 所有代码由共享一台计算机的两个程序员编写,“结对编程”
- 驱动者:编写代码
- 导航者:检查或查看代码
- 测试用例由团队中的一名成员起草,但测试由另一名成员完成。
- 如果一名程序员离开,知识不会全部丢失
- 没有经验的程序员可以学习(向有经验的程序员学习)
Scrum 团队
•(可以说)民主模式的变体。
• 7 +/- 2 人 团队
角色
-
“Scrum Master”:确保团队遵循Scrum 规则;保护团队免受外部障碍的影响
-
“Product Owner 产品拥有者”, 一个人,客户/顾客代表。负责将要开发的内容和顺序。
-
“开发团队”:负责确定如何交付产品所有者所要求的产品。
• 敏捷方法——固定长度的“迭代”
• 民主地同意在迭代中完成多少工作
• 团队成员在迭代(Sprint)内注册任务
• 问责机制的数量
• 客户/客户在每个 Sprint 结束时都能看到进展!
• 每个人都看到其他人在做什么
• 燃尽图跟踪 Sprint 中的进度
• 它显示了在 Sprint 中还有多少任务需要完成。
• 许多团队声称使用 Scrum
• 根据我们的经验,大多数团队需要任命领导者,因为他们需要指导
• Scrum 旨在自组织,不依赖于领导者
• 可能与经验丰富、积极主动的全职开发人员合作良好
• 全栈开发人员喜欢它
• 如果一家 IT 公司想要改变他们的团队模式,他们将与敏捷大师一起举办研讨会
敏捷过程中的团队合作
• 接受整个团队的责任:团队应该对产品的各个方面负责
• 依赖专家,但要谨慎:使用彼此的才能、技能和品质,但要谨慎
• 每件事都做一点点:这就是学习的方式。 实习就是这样设计的。
• 促进团队学习:不要自满。 总是有改进的机会
• 通过承诺鼓励合作:重振精神并专注于共同的目标
• 现在齐心协力:建立正确的团队合作意识,但它具有挑战性
成功的变革并不完全是……
• 自上而下:组织跟随领导者 (CEO) 实现愿景 没有人能够制定正确的愿景,将其传达给大量人员,消除所有关键障碍,产生 短期获胜,领导和管理数十个变革项目,并在组织文化的深处扎根新方法。 (1996, 51-52)
• 自下而上:一个团队或一些个人决定需要改变,并着手实现它。
• 您可能会看到“稍后请求原谅”的态度,违反规则并尽可能长时间地躲避雷达
• 大多数成功的变革,尤其是对像 Scrum 这样的敏捷框架的变革,都必须包含两者的要素。
• 你想做一场运动,就需要得到高层的支持,否则坚持不了太久。
最终状态不可预测
• Scrum 转换中的结束状态是不正确的,在需要持续改进的过程中不可能有结束状态。
• 您能做的最好的事情就是找出我们现在所处的位置与改进的中间状态之间的差距。
• 但我们仍然需要找到关闭它们的方法。
• 很难(有时甚至不可能)预测人们将如何应对较小的变化以变得敏捷。
• 所以你摇晃着等待回应。
• 这些戳戳和刺激不是随机的,而是根据经验、智慧和直觉精心阐述的,以推动成功过渡到 Scrum
Scrum 无处不在
• 孤立的变化更容易引入组织。 例如:改变一个特定的例程
• 现在,考虑一个过渡到 Scrum 的开发人员
• 必须一次处理一个功能的较小部分,以便在每个时间盒冲刺结束时完成某项工作。
• 她可能必须编写自动化测试来处理每一段新代码。
• 可能不得不交替进行称为 TDD 的短时间测试和编码
• 配对编程,无需耳机
• 这些是根本性的变化,您正在改变开发人员工作日的一切。
阻力会更大,因为影响更大! 无孔不入!
Scrum 完全不同
• 许多变化与他们过去的大部分训练背道而驰。
• 例如:
• 传统上,程序员已经接受过在开始任何编码之前深入分析问题并设计完美解决方案的培训。
• Scrum 说,在开始编码之前并不总是需要经过充分考虑的设计。
• 传统上,测试人员了解到他们的工作是测试是否符合规范。
• Scrum 说,测试人员了解到测试也与用户需求的一致性有关。
最佳实践是危险的
• 有人找出了做某事的正确或最佳方式,我们将其作为“最佳实践”与大家分享
• 然而,在过渡到 Scrum 时,收集最佳实践可能很危险。
• 最佳实践诱使我们放松并停止对 Scrum 至关重要的持续改进的努力。
• 虽然,您应该与您的团队分享好主意,但让我们抵制将它们编入一组最佳实践的冲动。
• 让我们不要规定您何时运行每日 Scrum。 这是不必要的微观管理。
这是值得的。 为什么?…
• 更高的生产力
• 更低的花费
• 提高员工敬业度和工作满意度 (!)
• 更快的上市时间
• 更高质量
• 提高利益相关者的满意度
• 我们一直在做的事情不再奏效
开源团队
无偿、异步沟通、没有团队会议、没有经理、没有规范、没有设计、几乎没有文档
开源项目之所以成功,是因为
- 完成“有价值”任务的纯粹享受——志愿者想要学习新技能以获得晋升或更好的工作机会
- 目标产品的性质:你希望任何人和每个人都参与进来
- 煽动者的个性
- 核心组成员的才能
团队问题
-
会议是一种开销
• 会议不应超过 45 分钟
• 如果是,则您正在进行不相关的讨论和无效的会议 -
团队成员不理解 彼此;彼此的情况;彼此的符号、模型等:
• 有同理心
• 尝试相互了解
• 力所能及的地方提供帮助 -
任务分配:分工的优势 - 交流
- 更短的“上市时间”
- 质量提升(其他人可以检查)
- 更有效(通过使用专家)
- 缺乏生产力: 了解可以并行执行的操作以及必须执行的操作
依次
• 优先级!
The Unified Process 统一流程
统一过程不是构建软件产品的一系列步骤
• 不存在这种单一的“一刀切”方法
• 有多种不同类型的软件
统一流程是一种适应性强的方法论
• 必须针对要开发的特定软件产品和开发团队进行修改
面向对象范式中的迭代和增量
在实践中
• 一个工件是一块一块地构造的(增量)
和
• 每个增量都经过多个版本(迭代)
项目分解为一系列瀑布式迷你流程
每个小项目都扩展了
• 需求工件
• 分析工件
• 设计工件
• 实施工件
• 测试工件
最后一组神器就是完整的产品
好处
• 检查正确性的多种机会
• 可以相对较早地确定架构的稳健性
• 尽早降低风险
• 始终拥有软件的工作版本
看起来乱七八糟,其实管理方面也没那么差……
• 因为它是一套瀑布式迷你项目
+OOP
统一过程是一种建模技术
• 模型是一组 UML 图,代表我们要开发的软件产品的各个方面
UML代表统一建模语言
• UML 是我们用来表示(建模)目标软件产品的工具
面向对象范式本质上是迭代和增量的
• 在 UML 图令人满意之前,除了重复迭代和递增之外别无选择
工作流(垂直)
(这个图是考点……)
(在考试里,这四个Phase加起来叫一个Event,另外Event之间需要解释
需求工作流
需求工作流的目标:确定客户的需求,回顾(了解应用领域,建立商业模式:使用 UML 来描述客户的业务流程,如果在任何时候客户认为成本不合理,开发将立即终止)
确定客户的限制至关重要
• 截止日期:如今,软件产品通常是关键任务
• 并行运行
• 便携性
• 可靠性
• 快速响应时间
• 成本 - 客户很少会告诉开发商有多少钱可用:改为使用投标程序
分析工作流
目标:分析和细化需求
为什么不在需求工作流程中执行此操作?
- 客户必须完全理解需求工件
- 因此,需求工作流的工件必须以自然(人类)语言表达:所有自然语言都是不精确的
来自制造信息系统的示例:“从数据库中读取零件记录和工厂记录。 如果它包含字母 A 后跟字母 Q,则计算将该零件运输到该工厂的成本”,它指的是什么?零件记录?工厂记录?还是数据库?
需要两个独立的工作流程
- 需求工件必须用客户的语言表达
- 分析工件必须精确,并且对于设计者来说足够完整
Software Project Management Plan 软件项目管理计划
一旦客户签署了规范(可能以 SRS 形式的合同),详细的规划和估算就开始了
我们制定软件项目管理计划(SPMP),包括
• 成本估计
• 持续时间估计
• 可交付成果
• 里程碑
• 预算
这是 SPMP 最早的可能时间
设计工作流
• 设计工作流程的目的是改进分析工作流程,直到材料成为可由程序员实施的形式:许多非功能性需求此时需要最终确定,包括编程语言的选择、重用问题、可移植性问题
• 在分析工作流程中提取类并在设计工作流程中设计类
• 保留设计决策:当达到死胡同时,防止维护团队重新发明轮子
• 我们不是在谈论原型(或任何类似的东西)。 这是关于软件架构的。
实现工作流
实施工作流的目标是以选定的实施语言实施目标软件产品
- 大型软件产品被划分为子系统
- 子系统由组件或代码工件组成
可追溯性
所有工件都需要被检查,因此需要可追溯性:
- 分析工件中的每个项目都必须可追溯到需求工件中的一个项目。
- 设计和实现工件也是如此
应通过审查来检查分析工件
- 必须对 SPMP 进行类似检查,特别注意成本和持续时间估算
- 设计(用户界面和架构)审查必不可少:因为客户代表通常不在场
测试工作流
• 单元测试:每个组件在实施后立即进行测试
• 集成测试:在每次迭代结束时,组合并测试完成的组件
• 产品测试:当产品看起来是完整的时,它会作为一个整体进行测试
• 验收测试:一旦完成的产品安装在客户的计算机上,客户就会对其进行测试
阶段(水平)
初始阶段
初始阶段的目的是确定所提议的软件产品是否是
经济上可行
- 了解领域,构建业务模式
- 划定拟议项目的范围,关注商业模式的子集
被提议的软件产品所涵盖 - 开始制作最初的商业案例
1.获得商业案例的初始版本是inception阶段的总体目标 - 此初始版本包含
- 软件产品范围的描述
- 财务细节
- 休息取决于软件的使用地点(内部或销售)
细化阶段
细化阶段的目标是细化初始需求
• 优化架构
• 监控风险并优化其优先级
• 完善业务案例
• 制定项目管理计划
细化阶段的任务对应于:
• 完成需求工作流程
• 执行几乎整个分析工作流程
• 开始架构设计
施工阶段
建设阶段的目标是产生第一个操作质量
软件产品的版本
• 这有时称为测试版
这一阶段的重点是
• 执行
• 测试
• 模块的单元测试
• 子系统的集成测试
• 整个系统的产品测试
过渡阶段
过渡阶段的目的是确保确实满足客户的要求
• 软件产品中的故障得到纠正
• 已完成所有手册
• 尝试发现任何以前未识别的风险
此阶段由安装测试版的站点的反馈驱动
1 维 与 2 维生命周期模型
a. waterfall; b. agile
为什么是二维模型?
• 在理想情况下,每个工作流都将在下一个工作流开始之前完成
• 实际上,开发任务太大了
• 开发任务必须分为增量(阶段)
• 在每个增量内,执行迭代直到任务完成
• 处理好不可避免的变化
• 移动目标问题
• 不可避免的错误
• 将一个大问题视为一组较小的、很大程度上独立的子问题
• 它提供了一个增量和迭代的框架
过渡到敏捷很难!
• 成功的变革并不完全是自上而下或自下而上
• 最终状态是不可预测的
• Scrum 无处不在
• Scrum 截然不同
• 变化比以往任何时候都来得更快
• 最佳做法很危险
使用 Scrum 框架实现敏捷
• Scrum 是帮助团队合作开发、交付和维持复杂产品的框架
• Sprint 计划是 Scrum 中的一个事件,它定义了在即将到来的 Sprint 中可以交付的内容以及如何实现这些工作
• Sprint 是一个短的、有时间限制的时期,Scrum 团队在此期间完成一定数量的工作。 冲刺通常在两周左右。
• 每日 Scrum 或站会是与 Scrum 团队的其他成员在同一时间和地点举行的简短会议,以相互更新 sprint 的进度
• 在 sprint 结束时,团队聚在一起进行 sprint 审查,非正式会议以查看演示并查看已完成的内容
• 在 sprint 回顾中,团队聚在一起讨论和记录哪些行得通,哪些行不通。 回顾可以是关于冲刺、人员、项目、关系和工具
使用看板框架实现敏捷
• 看板就是关于可视化您的工作、限制正在进行的工作和最大限度地提高效率。
• 看板团队专注于减少项目从开始到结束所花费的时间。
• 看板正在为您的团队在项目上的工作方式制作自定义列。
• 主要列范围从积压、优先排序、要做、做、审查中、完成和部署。 团队可以添加或删除这些列是必需的。
• 在每一列中,制作卡片(或票)。 每张卡片都是分配给团队成员的任务。
• 取决于团队使用的软件,可以使用附加功能,例如添加清单、截止日期、标记团队成员等
好处
• 更短的周期时间可以更快地交付功能
• 对变化的反应能力
• 当优先级变化非常频繁时,看板是理想的选择
• 平衡需求与吞吐量可确保大多数以客户为中心的功能始终有效
• 只需较少的组织/房间设置更改即可开始
• 减少浪费并移除不会为团队/部门/组织增加价值的活动
• 快速反馈循环增加了更有动力、更有能力和更高绩效的团队成员的机会
如何使用看板框架?看板方法假设三件事:
• 您了解当前的流程,因为它们实际上已在实践中,并尊重当前的角色、职责和职位。
• 您同意通过渐进式变革追求持续改进。
• 您鼓励各个层面的领导行为——从个人贡献者到高级管理人员。
• 看板正在为您的团队在项目上的工作方式制作自定义列
• 主要列范围从积压、优先排序、要做、做、审查中、完成和部署。 团队可以根据需要添加或删除这些列。
• 在每一列中,制作卡片(或票)。 每张卡片都是分配给团队成员的任务。
• 取决于团队使用的软件,可以使用附加功能,例如添加清单、截止日期、标记团队成员等
敏捷过程
Backlog 积压
• 用户故事的待办事项列表可能会变得非常复杂,因此对它们进行进一步分类可能会有所帮助。 将用户故事分解为单独的开发任务也很有帮助。
• 这就是backlog 的用途——允许对用户故事和相关的开发人员任务进行优先级排序,并将任务分配给各个开发人员。
• 有关积压工作的更多信息,请参阅 Atlassian 的积压工作指南。
• 积压可以在白板上进行跟踪,但更常见的是保存在电子表格或在线系统中。
燃尽图
• 一个示例燃尽图,显示了一段时间内的剩余任务。
• 在敏捷开发方法中,进度通常用燃尽图来跟踪。
• 这些图表定期(通常每天)显示待办事项中的剩余故事,以及
指示预计完成时间范围的“估计”趋势线。
• 图表的梯度表示团队朝着目标前进的速度
项目完成情况以及大致剩余的工作量。
• 这些信息让软件工程师知道他们是否需要,比如说,减少
如果没有足够的时间来完成此功能或与客户重新协商规格
项目的一部分。
Version Control Systems 版本管理系统
略,不要太熟练😆
W5 分析(续)
问题域中的类: 需要先确定类别!
类之间的关系
- “collaborates with”(关联)
- “is-a”(继承或泛化)
- “has-a”(聚合/组合)
请记住,这是迭代过程,所以不会一次性完成所有这些!
UML 类图
类图还有用吗?
- 新程序员倾向于学习新的语言和技术,但不会首先专注于学习创建合适的软件架构
- 缺乏合适的软件架构会导致高耦合和低内聚,导致代码不可扩展和不可重用
– 即他们的代码在实施期间或实施后无法适应更改/新功能
– 所以他们所做的就是为了它而“摇摆不定”,这导致了糟糕的软件架构 - 你不需要有一个完整的类图。
- 从你能做的开始,随着你的进展不断增加和迭代。
– 对于敏捷,您可以选择在编写实际代码的同时执行此操作。
Associations 关联
- 关联表示类之间的关系。
- 它们被表示为连接两个相关类的线,最简单的就是它们。
- 如果关联具有导向性(即一端有箭头),则表明一个类知道另一个类,反之则不然。
- 如果可以帮助您了解关系的性质,您可以给关系一个名称/标签。
– “has”这个名字对理解几乎没有什么帮助(不鼓励使用)
(箭头表示学生知道怎么找到老师
有时,关联很复杂——需要一个类(有时称为关联类)来表示它……
关联的每一端都可以显示其多重性: 这表示关联中涉及每个类的对象数量。(一对一,一对多,多对一,多对多
我们从左到右读取多重性
注意:其他多重性也是可能的,例如 2, 10 等
归化关系
很多不同的术语:继承,“is a”,泛化/专化,超类/子类。
• 泛化是我们用来管理复杂性的日常技术。
• 我们不是学习我们所经历的每个实体的详细特征,而是将这些实体置于更一般的类(动物、汽车、房屋等)中并学习这些类的特征。
• 这使我们能够推断这些类的不同成员具有一些共同特征,例如 松鼠和老鼠是啮齿动物。
• 在建模系统中,检查系统中的类以查看是否有泛化的余地通常很有用。
• 在面向对象的语言(如 Java)中,泛化是使用语言内置的类继承机制实现的。
• 概括地说,与较高级别的类相关联的属性和操作也与较低级别的类相关联。
• 低级类是子类从其超类继承属性和操作。 这些较低级别的类然后添加更具体的属性和操作。
子类继承其超类的属性和操作。
– 每个讲师和每个导师都有从 User 类继承的名称、ID 和密码。
– 讲师和导师可以标记作业,因为用户可以。
子类提供额外的操作和属性。
– 例如。 讲师可以创建作业。
子类可以覆盖其超类的操作。
– 我们稍后会展示这一点。
聚合
聚合(或共享聚合)——一种特殊形式的关联,“has-a”关系
- 聚合模型展示了作为集合的类是如何由其他类组成的。
- 聚合是存在于组与其成员之间的一种关联。
- 共享聚合的三个最重要的特性是:
– 聚合对象可能在没有其组成对象的情况下存在(尽管不一定处于有用状态),
– 在任何时候,每个对象都可能是多个集合的组成部分(例如,一个人可能属于多个俱乐部),并且
– 组成对象通常属于同一类(但同样,情况并非总是如此)。
复合
复合聚合是存在于整体与其部分之间的聚合。
复合聚合相对于共享聚合的三个最重要的特征是:
– 没有组件的复合对象不存在,
– 在任何时候,每个组件都可能只是一个组合的一部分,并且
– 组件对象很可能是混合类型(尽管情况并非总是如此。)
步骤
识别类和对象
目标:创建抽象字典(名词提取)。
方式:
- 研究用例场景
- 其他要求文档。
步骤:
- 从寻找有形事物开始。
- 弄清楚问题边界(问题域)内、外的内容
- 有些东西可能是类
- 有些可能是简单的属性。
识别抽象(类和对象)的角色和职责
抽象名词(属性)
• “职责”~= 属性+方法(在详细的类图中)。
将这些记录在:
• UML 类图
• CRC 卡。
• 文本。
模式清除
寻找类之间职责的共性。
看看这些是否可以使用继承来简化: 是否存在“is-a”关系?
继承示例:
– 属性类型:屋,公寓
– 类别类型:出租,出售
三种类型的类
- 实体类(为初始类图提取)
- 边界类(稍后在设计中决定)
- 控制类(稍后在设计中决定)
实体类
模型“长期”信息
通常是“被动的”
但它们可能仍然包含复杂的算法。示例:财产、代理、公司
边界类
存在于系统“自动化边界”上的类
• 模拟产品和环境(Actor)之间的交互
• 与输入或输出相关
控制类
调解边界和实体类之间的交互。
• 为复杂的计算和算法建模
• 有时与用例相关——但并非总是如此。
• 对于简单程序,可能是包含 main() 方法的类
类的交互
- Actor 只能与边界对象对话。
- 边界对象只能与控制器和演员对话。
- 实体对象可以与控制器以及其他实体对象对话。
- 控制器可以与边界对象和实体对象对话,也可以与其他控制器对话,但不能与角色对话
Interaction Diagrams 交互图
使用交互图描述用例的特定场景的实现(准确地模拟了用例场景)(必须包括边界类对象和控制类对象)
UML Sequence Diagram 序列图
• 对系统内的逻辑流进行建模
• 描述一组对象如何以及以何种顺序协同工作
• 显示对象之间的交互。
• 规划并了解现有或未来场景的详细功能。
• 对用例的详细信息建模——用例场景。
画出
步骤:
如何细化场景并用它来展示
对象之间的交互?
- 使用类图中的信息
- 然后,添加boundary边界和control控制类(意思就是这俩其实不在类图里……)
- 如果需要,您可以添加更多箭头来显示内部工作
系统 - 添加尽可能多的信息
UML state charts 状态表
考点:动态建模,使用特定场景的状态图对对象的行为进行建模。
TODO
W6 设计:从模块到对象
什么是模块?
考点
词法上连续的程序语句序列,以边界元素为界,带有聚合标识符(例如Java一个类)
好的模块化设计是:
模块内的最大关系
+
模块之间的最小关系
模块的操作
– 它做什么,它的行为
– 例如 “计算其参数的平方根”
模块的逻辑
– 它如何执行其操作
– 例如 牛顿计算平方根的方法
模块的上下文:
– 具体用途
– 例如 计算双精度整数的平方根
分配给模块的名称,即其操作
–(不是它的逻辑或上下文)
– 例如 computeSquareRoot、compute_square_root
模块内聚 Cohesion
– 模块内的交互程度
– 模块内的元素属于一起的程度。
七个类别/级别的凝聚力(非线性尺度):
模块的高内聚性是首选,因为它与软件的几个理想特性相关,包括健壮性、可靠性、可重用性和可理解性。
Coincidental Cohesion 碰巧内聚
模块执行多个完全不相关的操作(彼此松散相关)
例如:包含以下操作的模块:
printTheNextLine
, reverseStringOfCharactersComprisingSecondArgument
add7ToFifthParameter
, convertFourthParameterToFloatingPoint
来自这样的规则:“每个模块将由 35 到 50 个语句组成”
Logical Cohesion 逻辑内聚
模块执行一系列相关操作,其中一个由调用模块选择。
§ 执行逻辑相关的任务。例子:
– printReport() 函数/方法打印各种销售报告。
§ 本地销售报告
§ 区域销售报告
§ 全国销售报告
§ 一份国际销售报告。
– 如果用户只需要区域报告怎么办?
– 如果您想生成每周报告怎么办?
出现什么问题?
- 降低可读性
- 多个操作的代码可能会交织在一起:“打印区域销售报告”和“打印全国销售报告”可能会交织在一起。
- 维护问题,难以重复使用
可以通过为每个报告制定程序来消除逻辑耦合的需要
可以引入继承来提高内聚度。
时间(经典)内聚
一个模块执行多个功能,而这些功能之间的关联在于它们必须在特定时间的同一时间跨度内发生。例子:
一个程序(模块)执行以下步骤
§ 关掉电视
§ 关灯
§ 刷牙
§ 睡觉
例子:
– 执行以下操作的“初始化”程序:
- 打开所有文件。
- 初始化数组和所有变量。
- 设置所有常量
- 设置所有标志
- 读取第一笔交易
– 捕获异常后调用过程
- 关闭所有打开的文件
- 创建错误日志
- 通知用户
时间内聚很容易被发现,因为代码被放在一个过程中并被执行,因为在程序中的某个时间这样做很方便。
Procedural 过程内聚
§ 模块执行一系列操作,这些仅与受软件影响的一般过程有关
§ 两个模块示例:
– readPartNumberAndUpdateRepairRecordOnMasterFile
– 在绘图仪上绘制图形 R2 并打印报告 XYZ。
§ R2 上的信息和报告 XYZ 不相关。
§ 至少两个操作在过程上是相互关联的。
§ 与 Temporal Cohesion 非常相似,但这不仅在特定时间执行。
§ 为什么这么糟糕?
– 动作仍然弱连接,
– 所以模块不可重用
需要将它们分成数个过程,例如上面第一个模块:
– readPartNumber()
– UpdateRepairRecordonMasterFile()
Communication 通信内聚
§ 如果一个模块支持的所有活动都使用相同的输入或输出数据,或者访问和修改数据结构的相同部分,则该模块表现出通信内聚。
§ 例子:
一个模块包含以下活动:
– 查找书名
– 查找书的价格
– 查找图书出版商
– 查找书籍作者
§ 为什么不制作一个包含所有必需值的 Book 对象?
§ 更好 - 操作更紧密地联系在一起
§ 为什么还是不行?
§ 可重用性仍然存在问题
Functional 功能内聚
一个模块只执行一个动作
§ 例子:
– getTemperatureOfFurnace
– computeOrbitalOfElectron
– writeToDatabase
– calculateSalesCommission
– sortArray
– findLargestValue
为什么这么好?
§ 更可重用
§ 纠正性维护更容易: 更少的回归错误
§ 更容易扩展产品
信息内聚
模块执行多个动作,每个动作都有自己的入口点,每个动作都有独立的代码,都在相同的数据结构上执行。
例子
– void addEmployee(int status, empRec rec);
– void updateEmployee(int status, empRec rec);
– void deleteEmployee(int status, empRec rec);
模块执行所有主文件访问操作。 它读取、添加、更新和删除主文件记录。 这些函数中的每一个都有自己的入口点。
示例:控件类
好处
§ 信息内聚使开发人员能够“打包”与给定数据结构相关的所有功能。
§ 如果通过应用程序为所有与数据结构相关的功能提供单一位置,这可以降低维护成本。
缺点
§ 如果其他应用程序不需要所有功能,则可能会增加应用程序的内存需求,从而限制其他应用程序的效用
模块耦合
– 模块之间的交互程度
– 软件模块之间的相互依赖
– 衡量两个例程或模块的紧密程度
如果对一个模块的更改不会对另一个模块产生太大影响,则这两个模块是松散耦合的。
如果两个模块完全不相关,则它们是解耦的。
低耦合是结构良好的计算机系统的标志。 当与高内聚性相结合时,支持高可读性和可维护性的一般目标。
内容耦合
一个模块直接引用另一个模块的内容
一个模块修改或依赖另一个模块的内部工作
§ 示例 1:模块 p 修改模块 q 的语句
§ 示例 2:模块 p 指的是模块 q 的局部数据,表示 q 内的一些数值位移(变化)。
§ 示例 3:模块 p 分支到模块 q 的本地标签
§ 示例 4:公共与私有属性。
为什么这么糟糕?
§ 几乎对模块 q 的任何更改,甚至用新编译器重新编译 q,都需要对模块 p 进行更改
§ 违反信息隐藏
§ 数据封装
公共耦合
§ 公共耦合,当两个或多个函数共享全局数据时发生。
– 对它们的任何更改都会产生连锁反应。
§ 示例 1:模块 cca 和 ccb 可以访问和更改 globalVariable 的值
§ 示例 2:模块 cca 和 ccb 都可以访问同一个数据库,并且可以读写相同的记录
例子:
1 | while (globalVar == 0) { |
模块可能有副作用
影响可读性:是什么导致这个循环终止?
§ 在一个模块中对全局变量的声明进行更改需要在其他模块中进行相应的更改
§ 公共耦合模块难以重用
§ 一个模块暴露于比必要更多的数据。 这可能导致网络犯罪
控制耦合
一个模块将控制元素传递给另一个
当一个模块通过传递控制信息来控制另一个模块的流量时发生控制耦合
§ 示例1:一个操作码被传递给一个具有逻辑内聚性的模块
§ 示例 2:作为参数传递的控制开关
p调用q
1 | Module p { // has two methods |
为什么这么差?
模块不是独立的
– 模块q(被调用模块)必须知道模块p的内部结构和逻辑
– 这会影响可重用性
标记耦合
§ 一些语言只允许简单的变量作为参数
– partNumber
– satelliteAltitude
– degreeOfMultiprogramming
§ 很多语言也支持数据结构的传递
– partRecord
– satelliteCoordinates
– segmentTable
如果数据结构作为参数传递,则两个模块是标记耦合的,但被调用的模块对数据结构的部分而非全部独立组件进行操作
为什么这么糟糕?
§ 例子:
1 | calculateLibraryFine(StudentRecord student) |
§ 不清晰,不阅读整个模块,记录的哪些字段被访问或更改
§ 难以理解
§ 不太可能重复使用
§ 传递了不必要的数据
§ 注意:如果数据结构的所有组件都被访问和/或更改,则将数据结构作为参数传递是可以的
数据耦合
§ 当方法通过参数定期共享数据时,会发生数据耦合。
§ 数据耦合优于戳耦合,因为模块准确地获取它需要的东西,而无需知道特定数据结构的结构。
§ 例子:
– displayTimeOfArrival(flightNumber);
– computeProduct(firstNumber,secondNumber);
– getJobWithHighestPriority(jobQueue);
1 | int main(){ |
为什么这么好?
§ 不存在内容、通用、控制、标记耦合的困难
§ 维护更简单
§ 减少回归故障。
内聚+耦合
§ 模块的高内聚性是首选,因为它与软件的几个理想特性相关,包括健壮性、可靠性、可重用性和可理解性。
§ 低耦合是结构良好的计算机系统的标志。 当与高内聚性相结合时,支持高可读性和可维护性的一般目标。
因此,高内聚和低耦合是好的设计的关键。
UML的导向性
• 也称为“导航可见性”、“导航”或“可见性”或“知道”。
• 即,要将消息从一个对象发送到另一个对象,它必须是可见的。
• 例子:
• 学生可以“看到”单位,因为它是学生类中的一个属性。
• 用箭头表示:单向可见性(单向)。
• 双向导向增加了复杂性
– 每当发生更改时都需要维护关联的两端
• 学生了解单位,单位了解学生。
• 当一种方式就足够时尽量避免。
伪代码
1 | READ height of rectangle |
W7 软件设计
就是纵向第三步Design workflow
设计模式
TODO:跳过了前面的铺垫
• 模式是一种表示、共享和重用知识的手段。
• 架构模式是对良好设计实践的程式化描述,已经在不同环境中进行了尝试和测试。
• 模式应包括有关何时有用以及何时无用的信息。
• 可以使用表格和图形描述来表示模式。
MVC pattern
名称 | MVC (Model-View-Controller) |
---|---|
介绍 | 将展示和交互与系统数据分开。 该系统由三个相互交互的逻辑组件构成。 模型组件管理系统数据和对该数据的相关操作。 View 组件定义和管理数据如何呈现给用户。 控制器组件管理用户交互(例如,按键、鼠标点击等)并将这些交互传递给视图和模型。 |
适用于 | 当有多种方式查看和交互数据时使用。 当未来对数据交互和呈现的要求未知时也使用。 |
优势 | 允许数据独立于其表示而改变,反之亦然。 支持以不同方式呈现相同数据,并在所有这些方式中以一种表示方式进行更改。 |
劣势 | 当数据模型和交互很简单时,可能会涉及额外的代码和代码复杂性。 |
-
Model模型管理应用程序域的行为和数据
– 响应有关其状态信息的请求(通常来自视图)
– 响应改变状态的指令(通常来自控制器) -
View视图管理信息的显示
-
Controller 控制器解释用户输入
– 通常是鼠标和键盘,
– 通知模型和/或视图进行适当的更改。
• 视图和控制器都依赖于模型,但模型不依赖于它们
– 模型可以独立于视图和控制器进行构建和测试
• 在独立(桌面)应用程序中,视图和控制器之间的分离并不总是很清楚。
– 但总是有益的
• 在基于 Web 的应用程序中,它们被清楚地区分:
– 视图是在浏览器上实现的
– 控制器在服务器端实现:处理 HTTP 请求的组件
Layered architecture pattern 分层架构
• 用于模拟子系统的接口。
• 将系统组织成一组层(或抽象机器),每个层提供一组服务。
• 然而,通常人为地以这种方式构建系统。
名称 | 分层架构 |
---|---|
介绍 | 将系统组织成具有与每一层相关联的相关功能的层。 一个层向它上面的层提供服务,因此最低层代表可能在整个系统中使用的核心服务。 |
适用于 | 在现有系统上建造新设施时使用; 当开发分布在多个团队中,每个团队负责一层功能时; 当需要多级安全时。 |
优势 | 只要保持界面,就允许替换整个层。 可以在每一层提供冗余设施(例如,身份验证)以增加系统的可靠性。 |
劣势 | 在实践中,在层之间提供清晰的分离通常很困难,并且高层可能不得不直接与较低层交互,而不是直接通过它下面的层。 由于在每一层处理服务请求时对服务请求进行多级解释,因此性能可能是一个问题。 |
Repository architecture 存储库架构
子系统必须交换数据。 这可以通过两种方式完成:
– 共享数据保存在中央数据库或存储库中,所有子系统都可以访问;
– 每个子系统维护自己的数据库并将数据明确地传递给其他子系统。
C/S architecture 客户端-服务器架构
• 分布式系统模型,显示数据和处理如何分布在一系列组件中。
• 一组提供特定服务(例如打印、数据管理等)的独立服务器。
• 一组调用这些服务的客户端。
• 允许客户端访问服务器的网络。
名称 | Client-server |
---|---|
介绍 | 在客户端-服务器架构中,系统的功能被组织成服务,每个服务从一个单独的服务器提供。 客户端是这些服务的用户,并访问服务器以使用它们。 |
适用于 | 当必须从一系列位置访问共享数据库中的数据时使用。 因为服务器可以复制,所以也可以在系统负载可变时使用。 |
优势 | 此模型的主要优点是服务器可以分布在网络中。 通用功能(例如打印服务)可以对所有客户端可用,不需要由所有服务实现。 |
劣势 | 每个服务都是单点故障,因此容易受到拒绝服务攻击或服务器故障的影响。 性能可能无法预测,因为它取决于网络和系统。 如果服务器由不同的组织拥有,则可能是管理问题。 |
OO设计
略
- 为您的软件确定设计模式
- 详细类图,将初始类图推进到:
- 添加方法,添加属性
- 确定属性格式
- 分配每个方法(返回类型、参数等)
- 为关联分配可导航性
- 架构设计(例如 .MVC)和设计决策
- 设计原则:信息隐藏、责任驱动设计、可扩展性决定类图的方法。
- 所有软件工程师都能理解的伪代码
W8 道德规范 & 知识产权(IP)
软件工程道德规范(考点,略,加油查表)。
- 使用代码评估场景中的行为
- 建议可能的解决方案或行动。
知识产权是思想的表达,而不是思想本身。
- 软件著作权
- 软件专利
- 开源软件(Copyleft)
W9 实现上的问题
太coding了基本上不会考
这里相当于implement workflow + construction phase,也就是任务最重的地方
- 重用 Reuse
- 配置管理
- Host target development 主机目标开发
- 编程问题:
- 编程语言的选择
- 良好的编程习惯
- 编码标准
- 集成(持续?)
W10 质量和测试
- 质量保证QA(Quality Assurance)
- 质量保证技术
- 人工审核(演练Walkthroughs和检查Inspections,Non-execution-based)
- 证明(正式方式,Non-execution-based)
- 软件测试(Execution-based)
W11 软件测试
必考
- Positive正面测试确定您的应用程序按预期工作。 如果在正面测试期间遇到错误,则测试失败。
- Negative负面测试可确保您的应用程序可以优雅地处理无效输入或意外的用户行为。
- 例如,如果用户尝试在数字字段中键入字母,则在这种情况下的正确行为是显示“不正确,请输入数字”消息。
- 负面测试的目的是检测此类情况并防止应用程序崩溃。 负面测试可帮助您提高应用程序的质量并找到其弱点。
等效性测试
也称为分区测试,您可以在其中识别具有共同特征并应以相同方式处理的输入组。
应该从这些组中的每一个中选择测试。
- 输入数据和输出结果通常属于不同的类,其中一个类的所有成员都是相关的(有效和无效)
- 这些类中的每一个都是一个等效分区或域,其中程序对每个类成员都以等效的方式运行。
- 应从每个分区中选择测试用例。
假设 DBMS 规定产品必须处理 1 到 16,383 (2 14 1) 之间的任意数量的记录。
在伪代码中,它可能是:
1 | if ( |
Range (1…16,383) 构成一个等价类
等价类的任何一个成员与等价类的任何其他成员一样是一个很好的测试用例
Range (1…16,383) 定义了三个不同的等价类:
等价类 1:少于 1 条记录
等价类 2:1 到 16,383 条记录
等价类 3:超过 16,383 条记录
边值分析
在等价类边界的一侧或仅在其一侧选择测试用例 (1 … 16383)
结合边界值和等价类测试的测试用例总数:
- 测试用例 1:0 条记录 等价类 1 的成员且与边界值相邻
- 测试用例 2:1 条记录边界值
- 测试用例 3:2 条记录邻近边界值
- 测试用例 4:723 条记录 等价类 2 的成员
- 测试用例 5:16,382 条记录与边界值相邻
- 测试用例 6:16,383 条记录 边界值
- 测试用例 7:16,384 条记录 等价类 3 的成员且与边界值相邻
这大大增加了检测到故障的概率
黑盒测试
Test to specifications:忽略代码,使用规范选择测试用例
假设sepc包括 20 个因素,每个因素有 4 个值: 4^20 个测试用例
对规范的详尽测试和对代码的详尽测试都不可行
测试艺术:
-
选择一个小的、可管理的测试用例集
-
最大限度地提高检测到故障的机会,同时
-
最大限度地减少浪费测试用例的机会
-
每个测试用例都必须检测到以前未检测到的故障
这样同一故障不会被多个测试用例检测到。 (不会浪费!)
我们需要能够突出尽可能多的错误的方法
Functional Testing 功能性测试
经典软件黑盒测试的另一种形式
将测试数据基于代码工件的功能,每一项功能或功能都被识别
测试数据旨在分别测试每个(较低级别的)功能
然后,测试由这些低级函数组成的高级函数
1 | <higher level functionality>::= |
玻璃盒测试
Test to code:使用代码选择测试用例(不可靠,比如÷0
通过工件的每条路径必须至少执行一次
这也称为Structural结构测试(白盒)
代码流图有助于手动找到正确的覆盖范围
问题:
- Statement 声明的覆盖
- Branch 分支的覆盖
- 路径的覆盖
- 所有定义都使用路径的覆盖
Statement coverage
运行一组测试用例,其中每个语句至少执行一次
需要跟踪的 CASE 工具
弱点:分支声明
两个语句都可以执行而不会出现错误:&& and ||
Branch coverage
运行一组测试用例,其中每个分支至少执行一次(以及所有语句)
这解决了上一张幻灯片上的问题
同样,需要一个 CASE 工具
例如
1 | public void try(int x, int y) { |
单个测试用例 try(0, 1) 足以满足语句覆盖率,但不足以满足分支覆盖率。
为了实现分支覆盖,测试用例:
try(0, 1) 对两个 if 都为 true
try(1, 2) 对两个 if 都为 false
Path Coverage
运行一组测试用例,其中每条路径都执行一次(以及所有语句)
问题:
路径的数量可能非常大,例如。 for 循环
我们想要一个比所有路径都更弱的条件,但这会显示出比分支覆盖更多的错误
All-Definition-Use-Path Coverage 所有定义使用路径覆盖
每次出现的变量,zz,都被标记为
-
变量的定义:
zz = 1
或read(zz)
-
或变量的使用:
y = zz + 3
或if (zz < 9) errorB()
确定从变量定义到使用该定义的所有路径(可以通过自动工具完成)
为每个这样的路径设置一个测试用例
考点
- 在实施工作流程中进行测试
- 测试用例选择
- 黑盒单元测试(功能性):忽略代码,使用规范选择测试用例
- Glass-box玻璃盒单元测试(语句、分支、路径):忽略规范,使用代码选择测试用例
- 等价类划分和边值分析(可以是黑盒或玻璃盒)
- 集成测试(由程序员)
- 交付前的最终测试:
- 产品测试(QA 团队、程序员)
- 验收测试(客户、QA 团队、程序员)
W12 交付后维护
不在考试内容里
Mock Exam
一般来说考试结构和模拟考差不多(老天保佑),所以这里讲下模拟考里知识点分布
每个part的背景内容不一样,但是实际考试说明了会是一样的(so focus on traceability)
考试明说了不会让写代码,顶多伪代码,5个问题(小问题不好说啊,很可能总共10个小问题)
Part 1
只有一题,偏向理论,20‘。
让你结合案例,分析[the Unified Process](#The Unified Process 统一流程),W4内容
这里估计会跑偏,就可能是别的definition
Part 2
分为三题
Q1
画UML use case diagram 10’
简单,不过要注意是use case diagram 用例图
就几个人(Actor),一个系统框,框里一堆椭圆形操作,人和操作连一连。
Q2
补全[use case scenario](#第一步:功能建模 - 用例场景)的 Exception conditions(including reasonable actions to be taken, 即要写系统怎么handle)9‘
比较简单
Q3
第三个是31’的超级大题
画 initial UML class diagram
要求:
包括类的适当属性
使用泛化、关联和聚合来描述类之间的关系
正确指示关系的多样性(基数)。
不包括职责或操作。 此时没有所有信息来执行此操作。
pdf里还有个(1)小题,让找出名词(也就是找class第一步)。
要注意的是问题边界外(即,不在UML里的)的名词也需要写进去
Part 3
[UML sequence diagram](#UML Sequence Diagram 序列图)
Part 4
[UML state chart diagram](#UML state charts 状态表)
和lecture里的相比多了个框和标题,别的就:
起始点->各个state->state之间过程、操作->终止点
Part 5
道德准测,15’
基本上老老实实做ASS的肯定都是熟练工了
- 找到可能存在的问题
- 评价行为(即,最后需要写should do)
都要有code support哦,因为是开卷所以应该是好查的。
Part 6
画the Unified Process的工作流,15‘
表现出(粗略)在该项目期间给予不同工作流的相对工作量。 在 x(时间)轴上大致表示“事件 1”、“事件 2”和“事件 3”发生的时间。
[Part 7]
括号的内容只在PDF(可能只是线下考试)里看见,网上考试没有,疑似被删 (or被精简,毕竟伪代码真的长)
根据分析伪代码来写出 glass box unit testing
Test No & Brief Desc | var a | var b | var c | Expected output/behaviour |
---|---|---|---|---|
和Ass的差不太多,考的明显比Lecture里讲的简单。
和黑盒关注IO参数不一样,玻璃盒就需要关注内部var(需要instr, car.speed, car.status这种原型数据)和if/case导致结果的不同
个人感觉考试还是黑盒,明显黑盒更有考点
- Reason: Pos/Neg? + Desc
- vars …
- Expected Result
- Actual (考试估计不要求,or给定)
- Fail/Pass+comment(就在失败里写写原因
[Part 8]
[Functional requirements](#Functional requirements) 和 [Non-Functional Requirement](#Non-functional requirements)
就帮着修改requirements来达到”精确,明确,完整和一致“
先鉴定是(Non-)Functional,然后再重写。(明显不难
实际考试
差不多
出乎意料测试没怎么考
反倒是use case让写了全部(手断)
initial UML反而简单。state chart 较难(需要配合guard)
第一题的Unified Process画图也比想想的要难,需要对几个workflow都有点理解,每个event的发生点及影响也要熟练。(感觉自己应该是做错了,四个phase都没画齐全)