本资料覆盖全部 16 个 Lecture,包含每讲的核心知识点全面梳理、易错点深度分析和高频考点精讲。
目标是:读完本资料后直接掌握期末考试所需要的全部知识点。
Lecture 1 --- 软件工程导论
快速理解
这是整门课的总纲,回答"什么是软件工程、为什么需要它"。从软件危机出发,引出软件工程的必要性和核心概念。理解本讲是掌握后续所有内容的基础。
核心知识点
1. 软件的定义
- 软件(Software)= 程序(Programs)+ 文档(Documentation)+ 数据(Data)
- 程序:在给定输入下产生预期输出的指令序列
- 文档:描述软件的使用、设计、维护等的人工制品
- 数据:程序处理的信息结构
- 软件不是物理产品,是逻辑产品
- 软件不会"磨损",但会"过时" / "退化"(因环境变化)
2. 软件危机(Software Crisis)
- 发生背景:1960s,随着硬件性能爆炸式增长,软件规模和复杂度远超手动开发能力
- 核心问题:软件复杂度增长速度 > 人类开发能力的增长速度
- 典型表现 :
- 进度和成本失控:项目频繁延期、预算大幅超支
- 质量低下:交付的软件充满缺陷,可靠性差
- 维护困难:软件难以修改和扩展,修改引入新 bug
- 用户满意度低:交付的产品与用户期望不符
3. 软件工程的定义
- IEEE 定义:"将系统化、规范化、可量化的方法应用于软件的开发、运行和维护(即:对软件的工程化应用)"
- 核心关键词:systematic(系统化)、disciplined(规范化)、quantifiable(可量化)
- 软件工程 vs 计算机科学:
| 维度 | 计算机科学 | 软件工程 |
|---|---|---|
| 关注点 | 理论和算法 | 实践和交付 |
| 核心问题 | 如何自动化 / 计算"正确" | 如何在有限资源下构建有用的系统 |
| 产出 | 新算法、新理论 | 高质量软件产品 |
| 时间维度 | 追求"对"的答案 | 在时间 / 预算约束下做出权衡 |
4. 软件四大质量属性
| 属性 | 英文 | 解释 | 反面例子 |
|---|---|---|---|
| 可维护性 | Maintainability | 软件能否被容易地修改以适应新需求或修复缺陷 | 改一行代码要改几十个文件 |
| 可靠性 / 可信性 | Dependability | 软件在给定条件下正确运行的程度,包括安全性、可靠性、可用性等 | 银行系统偶尔算错账 |
| 效率 | Efficiency | 软件对系统资源(CPU、内存、磁盘、网络)的使用是否高效 | 一个简单查询卡死整个服务器 |
| 可用性 | Usability / Acceptability | 用户学习 / 使用软件是否容易,是否被用户接受 | 功能强大但无人会用 |
- 四个属性之间存在权衡:提高安全性可能降低性能,提高可用性可能降低效率。软件工程就是在这些属性之间做权衡(trade-off)。
5. 软件工程面临的挑战
- 异质性(Heterogeneity):软件需要在不同硬件、操作系统、网络环境中运行
- 交付压力(Delivery pressure):业务需求要求快速交付
- 信任和安全性(Trust & Security):用户对软件的信任和隐私保护
- 业务和社会的变革:需求总是在变化
6. 专业伦理(Professional Ethics)
- 应遵循的原则:
- PUBLIC:始终以公众利益为重
- CLIENT & EMPLOYER:在符合公众利益前提下服务客户和雇主
- PRODUCT:确保产品和文档达到最高专业标准
- JUDGMENT:保持判断的独立性和诚实性
- MANAGEMENT:通过道德和专业的管理推动高质量开发
- PROFESSION:提升软件工程专业的声誉和诚信
- COLLEAGUES:公平支持同事
- SELF:持续学习,提升专业能力
易错点
- 把"软件"等同于"程序":考试中常考------软件 = 程序 + 文档 + 数据,三者缺一不可。例如仅仅写好了代码(程序)没有用户手册(文档)和配置数据(数据),不能称为完整的软件。
- 混淆四大质量属性 :
- Maintainability(可维护性)关注"修改容易度"
- Dependability(可靠性)关注"不出故障"和"坏了能恢复"
- Efficiency(效率)关注"资源消耗"
- Usability(可用性)关注"用户友好程度"
- 以为软件工程只是编码:编码只是整个生命周期中很小的一部分。需求、设计、测试、维护等同样重要。
- 计算机科学和软件工程混淆:CS 关心"什么是对的"(理论正确性),SE 关心"什么在现实中可行"(实用权衡)。
高频考点
- ❗ 软件危机的定义、三个典型表现(必考------简答或选择)
- ❗ 软件四大质量属性的中文 + 英文名称及含义(必考------考选择或简答名称)
- ❗ 软件工程的定义(特别是 disciplined approach)(高频考)
- 软件 = 程序 + 文档 + 数据的完整定义
- 软件工程 vs 计算机科学的区别
Lecture 2 --- 软件过程模型
快速理解
"软件开发有哪些组织方式?"本讲从瀑布模型演进到敏捷方法,展示了软件开发过程的发展脉络。核心是理解每种模型的特点、优缺点和适用场景。
核心知识点
1. 软件过程(Software Process)的概念
- 软件过程 = 一系列活动(Activities),目标是开发或演化一个软件系统
- 四个基础过程活动 (所有模型都包含):
- 软件规格(Specification):定义系统的功能和约束
- 开发 / 设计实现(Development & Implementation):产生软件系统
- 验证 / 确认(Validation / Verification):检查软件是否满足需求
- 演化 / 维护(Evolution / Maintenance):适应需求变化
2. 瀑布模型(Waterfall Model)
- 阶段 :需求 → 设计 → 实现 → 测试 → 维护(严格线性顺序)
- 关键特征 :每个阶段有正式评审,评审通过才能进入下一阶段
- 优点:管理简单、阶段清晰、文档完善、易于理解
- 缺点 :
- 不适应需求变化(晚期修改成本极高)
- 客户很晚才能看到可运行的软件(风险高)
- 阻塞问题(一个阶段卡住,后续全部停滞)
- 适用场景:需求稳定、明确、不太可能变化的项目(如嵌入式系统、安全关键系统)
- 不适用场景:需求不确定或快速变化的大型项目
3. 增量开发(Incremental Development)
- 核心思想 :将系统分批次(increment)交付,每个增量增加一些功能
- 增量 1:核心功能(如登录)
- 增量 2:扩展功能(如搜索)
- 增量 3:高级功能(如报表)
- 优点 :
- 用户能早期看到部分功能,提供反馈
- 降低项目失败风险(核心功能首先开发)
- 可逐步交付使用
- 缺点 :
- 需要系统架构支持增量扩展
- 随着增量增加,需求可能偏离原有架构
- 和迭代(Iterative)的区别 :
- 增量:搭积木------每次加一块新功能
- 迭代 :打磨雕塑------每次对整个系统进行精化改进
4. 螺旋模型(Spiral Model)
- 核心驱动力 :风险分析(Risk Analysis)
- 四大象限 (每轮螺旋):
- 确定目标:设定本轮的目标、约束和备选方案
- 风险评估:识别和评估方案中的技术风险和项目管理风险
- 开发和验证:选择最安全的方案,进行开发和验证
- 规划下一轮:评审结果,规划下一轮螺旋
- 优点 :
- 风险管理能力强
- 适用于大型、高风险、复杂项目
- 缺点 :
- 管理复杂,不适合小项目
- 风险分析本身需要专业能力
5. RUP(Rational Unified Process)
- 由 Rational(IBM)公司开发,是一个迭代式、架构优先、用例驱动的过程框架
- 四大阶段:
| 阶段 | 英文 | 主要目标 | 耗时占比参考 |
|---|---|---|---|
| 初始 | Inception | 确定项目范围、识别用例、理解业务需求 | ~10% |
| 细化 | Elaboration | 建立架构基线、解决核心风险、细化用例 | ~30% |
| 构建 | Construction | 大规模开发和测试、构件实现 | ~50% |
| 交付 | Transition | Beta 测试、部署、用户培训 | ~10% |
- 六大核心工程工作流(Disciplines) :
- Business Modeling(业务建模)
- Requirements(需求)
- Analysis & Design(分析与设计)
- Implementation(实现)
- Test(测试)
- Deployment(部署)
- RUP 的核心价值观 :
- 迭代开发(Iterative Development)
- 风险管理(Risk Management)
- 架构优先(Architecture-Centric)
- 用例驱动(Use-Case Driven)
6. 敏捷方法(Agile Methods)
敏捷宣言(Agile Manifesto)四句话:
- 个体和交互 > 流程和工具
- 可工作的软件 > 详尽的文档
- 客户合作 > 合同谈判
- 响应变化 > 遵循计划
- 右边的内容仍然有价值,但左边的优先级更高。
敏捷的核心价值观:
- 快速响应需求变化
- 小版本迭代交付
- 团队成员紧密协作
- 持续改进
XP(极限编程,Extreme Programming):
- 将好的实践做到"极致"
- 核心实践 :
- 测试驱动开发(TDD):先写测试,再写代码
- 结对编程(Pair Programming):两人共用一台机器编码
- 持续集成(Continuous Integration):每天多次集成代码
- 重构(Refactoring):持续改进代码结构
- 简单设计(Simple Design):只做最简单的能工作的设计
- 集体代码所有权:任何人都可以修改任何代码
- 短迭代(1-2周)
Scrum:
- 角色 :
- Product Owner:定义产品 Backlog,确定优先级
- Scrum Master:确保 Scrum 流程执行,清除障碍
- Development Team:自组织团队,负责开发
- 工件(Artifacts) :
- Product Backlog:产品待办列表(所有需求的优先级清单)
- Sprint Backlog:Sprint 待办列表(当前迭代的任务)
- Increment:可交付的增量产品
- 事件(Events) :
- Sprint:1-4周的开发迭代周期
- Sprint Planning:规划本次 Sprint 要完成的任务
- Daily Scrum(每日站会):15分钟,同步进度
- Sprint Review:演示完成的功能
- Sprint Retrospective:回顾改进
7. 模型对比总览
| 维度 | 瀑布 | 增量 | 螺旋 | RUP | 敏捷 |
|---|---|---|---|---|---|
| 需求明确度 | 高 | 中 | 低 | 中 | 低 |
| 风险应对 | 弱 | 中 | 最强 | 强 | 中 |
| 用户参与度 | 低 | 中 | 中 | 中 | 高 |
| 适应变化 | 极差 | 中 | 中 | 中 | 最强 |
| 文档要求 | 多 | 中 | 中 | 多 | 少 |
| 适用规模 | 小-中 | 大 | 大 | 大 | 中-小 |
易错点
- 增量和迭代混淆:增量 = 每次加功能(搭积木),迭代 = 每次改进整体(打磨)。考试中常给出场景让你判断。
- 认为敏捷 = 没有文档:敏捷是"恰到好处的文档(just enough documentation)",不是零文档。
- RUP 阶段的顺序:Inception → Elaboration → Construction → Transition。常见陷阱是混淆 Elaboration(精化)和 Construction(构建)。
- Scrum 角色混淆:Product Owner 决定"做什么"和优先级,Scrum Master 确保流程正确运行,Team 决定"怎么做"。
- 瀑布模型仍有价值:不要以为瀑布已经过时------对安全关键系统(如航空电子、医疗设备),瀑布的严格评审流程仍然必要。
- 螺旋模型的核心不是迭代,是风险分析。螺旋每一轮都是从目标设定→风险分析→开发验证→下一轮规划,风险分析才是灵魂。
高频考点
- ❗ 各种过程模型的特点和适用场景对比(必考------选择题或简答)
- ❗ 瀑布模型的优缺点(高频选择 / 简答)
- ❗ 敏捷宣言四句话(必考------简答或填空)
- ❗ RUP 四大阶段的英文和中文名称及顺序(高频)
- ❗ 增量 vs 迭代的区别(高频选择)
- ❗ Scrum 的核心角色和职责(高频)
- 螺旋模型的风险驱动特点
- XP 的主要实践(尤其是 TDD、结对编程、持续集成)
Lecture 3 --- 需求工程
快速理解
"软件出问题第一原因就是需求出问题。"需求工程回答的是:我们到底要构建什么?本讲涵盖从需求获取到需求验证的全过程。
核心知识点
1. 需求工程(Requirements Engineering, RE)
- 目标:确定系统需要提供什么功能、有什么约束
- 需求 (Requirements) 是系统必须满足的能力或条件
- 需求错误的修复成本随开发阶段指数级增长:需求阶段的 bug 修复成本是 1,实现阶段是 10-100,发布后可能是 1000+
2. 需求工程的四个主要活动
需求获取 (Elicitation)
→ 需求分析与协商 (Analysis & Negotiation)
→ 需求规格说明 (Specification)
→ 需求验证 (Validation)
(1)需求获取(Elicitation)
- 目标:从涉众(Stakeholder)处获取需求信息
- 主要技术:
| 技术 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 访谈(Interview) | 一对一问问题 | 深入,可追问 | 耗时,涉众可能说不清 |
| 观察(Ethnography / Observation) | 观察用户实际工作过程 | 发现"说不出的需求" | 耗时,用户可能因被观察而改变行为 |
| 场景分析(Scenarios) | 描述用户使用系统的具体场景 | 具体形象,用户易理解 | 不一定覆盖所有异常 |
| 原型法(Prototyping) | 快速构建界面原型 | 用户"看到"后才知道要什么 | 客户可能误会原型就是最终产品 |
| 头脑风暴(Brainstorming) | 集体讨论创意 | 激发新想法 | 需要好的主持人 |
| 用例(Use Cases) | 描述 Actor 与系统的交互 | 结构化、标准化 | 学习曲线 |
(2)需求分析与协商
- 识别需求之间的冲突(如:安全 vs 易用性)
- 优先级排序(Must have / Should have / Could have)
- 涉众分析(Stakeholder Analysis):识别关键决策者
(3)需求规格说明(Specification)
- 两种层次的规格说明:
| 层次 | 面向受众 | 形式 | 详细程度 |
|---|---|---|---|
| 用户需求(User Requirements) | 客户 / 用户 | 自然语言 + 图表 | 高层、概述性 |
| 系统需求(System Requirements) | 开发人员 | 结构化文本、模型 | 详细、精确 |
- 好的需求应当清晰、一致、完整、可验证
(4)需求验证(Validation)------五项检查
| 检查项 | 英文 | 解释 |
|---|---|---|
| 有效性 | Validity | 这个需求是否真的反映用户需要? |
| 一致性 | Consistency | 需求之间没有矛盾(如:要求"快"又要"加密"但没考虑性能影响) |
| 完整性 | Completeness | 没有遗漏关键需求和边界情况 |
| 可实现性 | Realism / Feasibility | 在现有技术和预算下能否实现? |
| 可验证性 | Verifiability | 能否通过测试或检查来确认是否满足? |
- "系统应是用户友好的" --- 违反可验证性,因为"友好"无法被客观测试
3. 功能需求(Functional Requirements)vs 非功能需求(Non-functional Requirements)
功能需求:
- 描述系统做什么(具体行为 / 功能)
- 例子:"系统应该允许用户搜索产品"
- 直接来源于用例和场景
非功能需求:
- 描述系统做得怎么样(质量属性和约束)
- 例子:"搜索响应时间应在 500ms 内"
- 通常对应系统的整体质量属性
非功能需求的三分类:
非功能需求
├── 产品需求(Product Requirements)
│ ├── 性能 (Performance):响应时间、吞吐量
│ ├── 可靠性 (Reliability):MTBF、可用性百分比
│ ├── 可用性 (Usability):学习时间、操作效率
│ └── 安全性 (Security):认证、授权、加密
├── 组织需求(Organizational Requirements)
│ ├── 开发标准:使用特定语言 / 框架 / 流程
│ ├── 交付时间:必须何时完成
│ └── 开发工具和环境要求
└── 外部需求(External Requirements)
├── 法规合规:GDPR、HIPAA、行业标准
├── 互操作性:必须与哪些其他系统对接
└── 伦理要求:隐私保护、无障碍访问
非功能需求的重要性:
- 往往决定了系统的架构选择
- 非功能需求的满足比功能需求的满足更难量化
- 非功能需求之间通常是冲突的(trade-off)
4. 需求管理(Requirements Management)
- 需求会变化------这是不可避免的
- 需要管理需求的版本 和变更
- 建立需求追踪矩阵(Requirements Traceability Matrix, RTM) :
- 每个需求 → 对应的设计 → 对应的代码 → 对应的测试
- 当需求变化时,能快速定位影响范围
易错点
- 功能需求 vs 非功能需求判断:考试常给描述让你判断类型。关键技巧------功能需求回答"做什么",非功能需求回答"多快 / 多好 / 多安全 / 多可靠"。
- 把"非功能需求"当作"不重要的需求":事实上非功能需求往往更关键、更难满足。
- 误把 UI 界面设计当作需求:界面是设计方案的体现,不是原始需求。
- 需求验证五项检查的名称混淆:Validity(有效性)、Consistency(一致性)、Completeness(完整性)、Realism(可实现性)、Verifiability(可验证性)------五个 V / C 开头的英文词。
- 以为需求一次获取完毕:需求是逐步发现的(Iterative Elicitation),这是共识。
- "系统应是用户友好的" 是典型的不良需求------无法验证。好的需求应可量化、可测试。
高频考点
- ❗ 功能需求 vs 非功能需求的区分(必考------选择题,给出描述判断类型)
- ❗ 需求验证的五项检查(高频考------简答或选择)
- ❗ 需求获取技术的名称和适用场景(高频考)
- ❗ 非功能需求的三分类(中频------简答)
- 需求工程的四个主要活动(步骤顺序)
- 用户需求 vs 系统需求的区别
Lecture 4 --- 系统建模
快速理解
"不用图形描述系统,沟通就会出问题。"系统建模是用图形化的方式抽象和描述系统,帮助理解、沟通和分析。本讲介绍四种模型的用途和各自的核心图。
核心知识点
1. 系统建模的目的
- 帮助理解系统的结构和行为
- 便于团队成员之间沟通
- 提供设计文档
- 支持系统分析和验证
2. 系统建模的四大类型
| 模型类型 | 回答的问题 | 代表性图表 | 建模阶段 |
|---|---|---|---|
| 上下文模型 | 系统边界在哪?与谁交互? | 上下文图 (Context Diagram) | 早期需求分析 |
| 交互模型 | 系统内各部分如何协作? | 序列图、用例图 | 分析 & 设计 |
| 结构模型 | 系统由什么组成?各部分关系? | 类图、组件图、部署图 | 分析 & 设计 |
| 行为模型 | 系统如何响应事件和数据? | 状态图、活动图、DFD | 分析 |
3. 上下文模型(Context Models)
- 上下文图 :将系统视为一个黑盒,显示系统与外部实体(Actor)的关系
- 作用:确定系统边界------"什么在系统内,什么在系统外"
- 特点:最高层的抽象,不展示内部细节
- 例子:图书馆系统上下文图 → 外部有 Librarian、Member、Book Supplier 等 Actor
4. 交互模型(Interaction Models)
- 用例图(Use Case Diagram) :展示系统功能(用例)与外部参与者(Actor)的关系
- 重点关注:哪些人为系统做什么
- 关联关系:Actor 和 Use Case 之间用线连接
- 关系:
<<include>>(必须包含)、<<extend>>(可选扩展)、泛化(父子用例 / 参与者)
- 序列图(Sequence Diagram) :展示对象间按时间顺序的消息传递
- 生命线(Lifeline) :
对象名:类名的矩形框 + 虚线 - 激活框(Activation Box):矩形条,表示对象在做什么
- 消息类型 :
- 同步消息(实心箭头 + 实线):发送方等待返回
- 异步消息(空心箭头 + 实线):发送方不等待
- 返回消息(虚线箭头):方法返回值
- 生命线(Lifeline) :
5. 结构模型(Structural Models)
- 类图(Class Diagram) :展示系统的静态结构
- 类 = 名称 + 属性 + 方法(操作)
- 属性的可见性:
+public,-private,#protected - 关系类型(重点 ⭐):
| 关系 | 图形 | 说明 | 例子 |
|---|---|---|---|
| 关联 (Association) | 实线 ------------ |
类之间有联系 | 学生 ------ 课程 |
| 聚合 (Aggregation) | 空心菱形 ◇------------ |
整体-部分,部分可独立存在 | 学校 ◇------------ 老师(老师可换学校) |
| 组合 (Composition) | 实心菱形 ◆------------ |
整体-部分,部分随整体消亡 | 页面 ◆------------ 按钮(按钮随页面销毁) |
| 泛化 (Generalization) | 空心三角 ------▷ |
继承关系 | 猫 ------▷ 动物 |
| 实现 (Realization) | 虚线空心三角 - - ▷ |
接口实现 | Circle - - ▷ Drawable |
| 依赖 (Dependency) | 虚线箭头 - - -> |
一个类使用另一个类(临时) | Printer - - -> Paper |
- 多重性(Multiplicity) :
1:恰好一个0..1:零或一个0..*或*:零或多个1..*:一或多个
6. 行为模型(Behavioral Models)
- 数据驱动建模:DFD(Data Flow Diagram)
| DFD 元素 | 图形 | 说明 |
|---|---|---|
| 进程 (Process) | 圆形 / 椭圆 | 对数据做变换操作 |
| 数据流 (Data Flow) | 箭头 | 数据在进程间流动 |
| 数据存储 (Data Store) | 平行线 / 开口矩形 | 数据持久存放 |
| 外部实体 (External Entity) | 矩形 | 系统外的数据源或目的地 |
-
DFD 的三个抽象级别:
- Level 0(上下文 DFD):整个系统视为一个进程
- Level 1(0 层 DFD):系统分解为多个主要进程
- Level 2(1 层 DFD):对主要进程进一步分解
-
事件驱动建模:状态机图(State Machine Diagram)
| 要素 | 说明 | 例子 |
|---|---|---|
| State(状态) | 对象在特定条件下的情况 | "等待输入"、"已登录" |
| Initial State | 初始状态(实心圆) | ● |
| Final State | 终止状态(实心圆+圈) | ◎ |
| Transition(转移) | 状态间的变化 | 从"待处理"→"处理中" |
| Event(事件) | 触发转移的事件 | "用户点击提交" |
| Action(动作) | 转移时的操作 | "验证输入、保存数据" |
- 状态图适合描述单个对象的生命周期行为
- 活动图(Activity Diagram)适合描述业务流程或算法步骤
- 要素:开始节点、动作节点、决策 / 分支(菱形)、分叉 / 汇合(粗线)、泳道(Swimlane,按角色划分责任区域)
7. 模型之间的关系
- 不同的模型从不同角度描述同一个系统
- 用例图 驱动后续的序列图 和类图
- 状态图 和活动图补充行为描述
- 上下文图定义系统边界
易错点
- 聚合 vs 组合 :菱形是空心的还是实心的?考试常给一个场景(如"教室和椅子"------如果椅子可以搬到别的教室就是聚合,如果椅子是教室不可分割的一部分就是组合)。记住:聚合(空心◇)的部分可独立存在;组合(实心◆)的部分随整体一起销毁。
- DFD 不是流程图:DFD 关注的是"数据如何流动和变换",不是"程序的控制流(先做什么后做什么)"。活动图 / 流程图才关注控制流。
- DFD 和活动图的混淆:两者看起来都有数据 / 控制流动,但 DFD 展示数据变换和存储,活动图展示业务流程步骤序列。
- 多重性方向 :
1 --------- 0..*要注意哪个方向。Member 1 --------- 0..* Loan表示一个 Member 对应零或多个 Loan。 - 状态图只适合单个对象:状态机图描述的是"一个对象在其生命周期中的状态变化",不是业务流程。
- 用例图中 actor 是人还是系统?:Actor 可以是人、其他系统、硬件设备------只要与当前系统有交互。
高频考点
- ❗ 类图中六种关系的识别和绘制(必考------给出关系类型描述,选 / 画出正确图形)
- ❗ 聚合 vs 组合的区分(绝对高频)
- ❗ 状态图中的要素(State、Transition、Event、Action)(高频简答)
- ❗ DFD 的四个基本元素及图形表示(高频选择)
- ❗ 用例图中 <> 和 <> 的区分(高频考)
- ❗ 四种模型类型各自回答什么问题(选择 / 简答)
- 序列图的元素(生命线、激活框、消息类型)
- 上下文模型的作用(确定系统边界)
Lecture 5 --- 面向对象基础
快速理解
"面向对象的五个核心概念。"本讲为理解 OO 分析和设计打下基础,是后续 UML、OOA、OOD 的理论前提。
核心知识点
1. 面向对象的四大基本特征 + 类与对象
(1)抽象(Abstraction)
- 定义:关注本质特征,忽略无关细节
- 例子:将"一辆汽车"抽象为"交通工具",忽略颜色、型号等非本质属性
- 软件中的应用:接口 / 抽象类 = 对外只暴露必要的操作,隐藏内部细节
- 关键词:关注"是什么"而不是"如何实现的"
(2)封装(Encapsulation)
- 定义:数据和操作数据的方法绑定在一起,隐藏内部实现细节,仅暴露公开接口
- 核心思想:Information Hiding(信息隐藏)
- 实现方式:private 属性 + public getter / setter 方法
- 好处:
- 降低复杂度(外部不需要知道内部实现)
- 提高可维护性(内部修改不影响外部)
- 提高安全性(防止外部非法修改内部状态)
- 关键词:保护内部状态,暴露操作接口
(3)继承(Inheritance)
- 定义:子类(Subclass)继承父类(Superclass)的属性和方法,实现代码复用
- 关系语义:"is-a"(猫是 动物,轿车是车辆)
- 子类可以:完全继承、重写(Override)、扩展新的属性和方法
- 类继承 vs 接口继承:
- 类继承:继承实现(代码复用)
- 接口继承:继承类型规范(多态)
- 虚拟方法(Virtual Method):子类可以重写的父类方法
- 抽象类(Abstract Class):不能实例化,必须被子类继承才可使用
- 注意:过多继承层次会使系统难以维护
(4)多态(Polymorphism)
- 定义:同一接口可以被不同类以不同方式实现
- 核心机制:动态绑定(Dynamic Binding)------运行时决定调用哪个方法
- 两种形式:
- 编译时多态(Compile-time Polymorphism) = 方法重载(Overloading):同名方法不同参数列表(在 Java / C++ 中编译期确定)
- 运行时多态(Runtime Polymorphism) = 方法重写(Overriding):子类重写父类方法(运行期确定)
- 多态的好处:代码灵活、可扩展性强
(5)类(Class)与对象(Object)
- 类 = 模板 / 蓝图(定义属性和方法),不占用运行时内存
- 对象 = 类的实例(实例化后占用内存)
- 一个类可以有多个对象实例
2. 消息传递(Message Passing)
- OO 系统中,对象之间通过发送消息来交互
- 消息 = 对象名 + 方法名 + 参数
- 例子:
student1.getGrade("CS101")
3. 继承 vs 组合(Inheritance vs Composition)
- 继承("is-a") :子类是父类的一种
- 优点:代码复用直接
- 缺点:破坏封装(子类依赖父类实现),继承层次过深难以维护
- 组合("has-a") :一个类包含另一个类的对象作为成员
- 优点:松耦合、灵活(运行时可替换)
- 缺点:需要额外的代码委托
- 原则:优先使用组合而非继承(Favor composition over inheritance)
4. 对象之间的关系总结
| 关系 | 语义 | 表示 |
|---|---|---|
| 继承 (Generalization) | is-a | 类继承 |
| 关联 (Association) | knows-a / has-a | 双向或单向引用 |
| 聚合 (Aggregation) | has-a (弱) | 部分可独立 |
| 组合 (Composition) | has-a (强) | 部分随整体 |
| 依赖 (Dependency) | uses-a | 方法参数 / 返回值 |
易错点
- 抽象 vs 封装混淆:抽象是"从外部观察对象时看到的本质特征"(从复杂中提取关键),封装是"把内部细节藏起来"(信息隐藏)。抽象回答"这个对象是什么?",封装回答"内部实现不让外界看到"。
- 多态 ≠ 方法重载(Overloading):Overloading 是"同名不同参"(编译期决定),多态更核心的是动态绑定 / 重写(运行期决定)。考试常考多态的两种形式。
- "is-a" vs "has-a"判断错误:给定两个类,判断是继承还是组合。如 "Student" 和 "Course" → has-a(组合 / 关联),"GraduateStudent" 和 "Student" → is-a(继承)。
- 抽象类 vs 接口混淆:抽象类可以有部分实现,接口只有方法签名(在 Java 8+ 之前)。但很多考试中保持传统定义。
- 消息传递的写法 :
object.method(args)不是简单的函数调用,而是"向对象发送一条消息"。
高频考点
- ❗ 面向对象五大概念的定义和区分(必考------选择或简答)
- ❗ 封装的好处 / 信息隐藏的作用(高频简答)
- ❗ 继承 vs 组合的对比和选择(高频简答)
- ❗ 多态的两种形式:Overriding 和 Overloading(高频选择)
- 抽象类和接口
Lecture 6 --- UML 入门
快速理解
UML(统一建模语言)是 OO 分析与设计的标准建模语言。本讲详细介绍了最重要的五种 UML 图及其使用方法。
核心知识点
1. UML 概述
- UML = Unified Modeling Language(统一建模语言)
- 作用:为 OO 分析和设计提供标准的图形化表示方法
- 用于:可视化、规格说明、构造和文档化系统的产出物
- UML 2.0 包含 13 种图,本讲重点覆盖 5 种核心图
2. 用例图(Use Case Diagram)
- 用途 :捕捉功能需求,展示系统对外提供的功能
- 要素 :
- 参与者(Actor):与系统交互的人、系统或设备(用小人图标表示)
- 用例(Use Case):系统提供的一个完整功能(椭圆 / 矩形 + 文本表示)
- 系统边界(System Boundary):矩形框,内部是系统,外部是 Actor
- 关联(Association):连接 Actor 和 Use Case(实线)
- 关系 :
<<include>>(包含):一个用例总是会执行 另一个用例。箭头从"调用方"指向"被调用方"(虚线箭头 +<<include>>标注)。语义:A 总是包含 B,没有 B 则 A 不完整。<<extend>>(扩展):一个用例在某些条件下 会执行另一个用例。箭头从"扩展用例"指向"被扩展用例"。语义:A 在特定条件下才扩展 B,B 本身是完整的。- 泛化(Generalization):子用例 / 子 Actor 继承父用例 / 父 Actor 的行为(空心三角箭头)。
3. 类图(Class Diagram)
-
用途 :展示系统的静态结构(类及其之间的关系)
-
类的表示:
┌─────────────────────┐
│ ClassName │ ← 类名(粗体居中)
├─────────────────────┤
│ - attribute1: Type │ ← 属性(可见性 名称: 类型)
│ + attribute2: Type │
├─────────────────────┤
│ + method1(): Type │ ← 方法(可见性 名称(参数): 返回类型)
│ - method2(p: Type) │
└─────────────────────┘ -
可见性 :
+(public)、-(private)、#(protected)、~(package) -
六种关系(详细已在 Lecture 4 介绍,这里是核心总结):
| 关系 | 图形 | 弱→强 |
|---|---|---|
| 依赖 | - - - -> |
最弱 |
| 关联 | ------------ |
|
| 聚合 | ◇------------ |
|
| 组合 | ◆------------ |
|
| 泛化 | ------▷ |
|
| 实现 | - - -▷ |
最强(类型层面) |
4. 序列图(Sequence Diagram)
- 用途:按时间顺序展示对象之间的消息交互
- 要素 :
- 生命线(Lifeline) :
对象名:类名的矩形 + 向下的虚线 - 激活条(Activation Bar):对象执行操作的时间段(矩形条)
- 消息(Message):对象间传递的通信
- 生命线(Lifeline) :
- 消息类型 :
→同步消息(实心箭头):如方法调用,调用方等待返回→异步消息(普通箭头):发送后不等待↘返回消息(虚线箭头):返回值- 自调用消息:对象调用自己的方法
- 交互片段(Interaction Fragment) :
alt:可选 / 替代片段(类似 if-else)loop:循环片段opt:可选片段
- 用途 :详细描述一个用例的实现过程
5. 状态图(State Machine Diagram)
- 用途 :描述一个对象在其生命周期中的状态变化
- 要素 :
- 初始状态 (实心圆
●) - 状态(圆角矩形):对象的状态名称
- 转移 (箭头):
事件(参数) [守卫条件] / 动作 - 自身转移:事件触发后回到同一状态
- 最终状态 (实心圆 + 外圈
◎)
- 初始状态 (实心圆
- 例子:电梯控制系统的状态图可能包括:Idle → Moving → Idle 等
- 适用场景:一个对象有多个不同状态,且不同状态行为不同(如 Order 状态:Pending → Paid → Shipped → Delivered → Cancelled)
6. 活动图(Activity Diagram)
- 用途:展示业务流程或工作流中的步骤序列
- 要素 :
- 开始节点 (实心圆
●) - 动作节点(圆角矩形):执行的一个步骤
- 决策 / 分支 (菱形
◇):条件判断,有多个出口 - 分叉(Fork):粗线,将一条控制流分为多条并发流
- 汇合(Join):粗线,将多条并发流合并为一条
- 泳道(Swimlane):垂直区域,按角色 / 部门来组织活动
- 最终节点 (实心圆+圈
◎)
- 开始节点 (实心圆
- 状态图 vs 活动图 :
- 状态图:关注一个对象的状态变化(状态 -> 事件 -> 状态)
- 活动图:关注流程步骤(动作 -> 动作 -> 动作)
- 活动图更接近传统流程图,但增加了并发和角色划分的能力
7. UML 图表分类总结
| 类型 | 图表名称 | 动态 / 静态 |
|---|---|---|
| 结构图 (Structure) | 类图、对象图、组件图、部署图、包图、组合结构图 | 静态 |
| 行为图 (Behavior) | 用例图、活动图、状态图 | 动态 |
| 交互图 (Interaction) | 序列图、通信图、时序图、交互概览图 | 动态 |
易错点
- 聚合(空心菱形) vs 组合(实心菱形)看错:最简单记忆法------"空心"意味着"部分可以独立空出来"(可分离);"实心"意味着"部分被牢牢焊在整体上"(不可分离)。
<<include>>和<<extend>>箭头方向混淆 :<<include>>:A<<include>>B → A 一定会调用 B。箭头从 A 指向 B。<<extend>>:A<<extend>>B → A 在特定条件下扩展 B。箭头从 A(扩展)指向 B(被扩展)。
- 用例图中的 Actor 必须是 "人":错!Actor 可以是其他系统、硬件设备。
- 序列图中的消息箭头方向错误:同步消息用实心箭头,异步消息用普通空心箭头。混淆后含义完全不同。
- 状态图 vs 活动图混用:描述单个对象的行为 → 状态图;描述业务流程 → 活动图。不能搞反。
- 假设 UML 只用于 OO 开发:UML 可以与其他开发范式配合使用,但确实最常用于 OO 开发。
- 泛化(继承)箭头的方向 :子类指向父类,箭头在父类端。人总是想画反。
高频考点
- ❗ 类图中六种关系的图形识别(必考------选择题给图判断类型)
- ❗ 聚合 vs 组合的区分(必考)
- ❗ 用例图中 <> 和 <> 的语义区分(必考)
- ❗ 序列图的要素识别(生命线、激活框、消息类型)(高频选择)
- ❗ 给定场景选择正确的 UML 图(高频选择)
- ❗ 状态图的元素(State、Transition、Event、Action)(高频)
- 类图的多重性含义
- 活动图的泳道 / 分叉 / 汇合的作用
- 结构图 vs 行为图的分类
Lecture 7 --- 面向对象分析 (OOA) 实例
快速理解
OOA 是将用户需求转化为分析模型的过程。"分析"意味着理解问题域------识别出系统中有哪些类、它们之间如何交互。本讲通过一个完整案例(如图书馆系统)演示了 OOA 的实操流程。
核心知识点
1. OOA 的基本流程
需求描述 → 理解问题域 → 识别候选类 → 筛选类 → 确定类间关系 → 建立用例模型 → 构建分析类图
2. 理解问题域(Understanding the Problem Domain)
- 通过与用户 / 客户交流,理解业务逻辑
- 识别关键的业务实体(Business Entities)
- 确定系统的范围 和限制
3. 识别候选类(Identifying Candidate Classes)
- 经典方法:名词提取法(Noun Extraction)
- 从需求文档 / 用户描述中提取所有名词 和名词短语
- 这些名词就是候选类
- 筛选标准 (排除法则):
- 冗余类:表达同一概念的多个名词 → 保留一个
- 模糊类:含义不清晰 → 排除或重新定义
- 属性冒充类:实际上是另一个类的属性 → 排除(如"书名"不是类,而是 Book 的属性)
- 实现细节类:在分析阶段不需考虑 → 推迟到设计阶段
- 保留标准 :
- 有明确的职责和行为的实体
- 系统需要存储和管理其信息
4. 确定类之间的关系
- 关联 :
A 关联 B(如:Student 关联 Course) - 聚合 :
A 包含 B 的集合(弱整体-部分) - 组合 :
A 由 B 构成(强整体-部分) - 泛化 :
A 是 B 的一种(继承) - 在类图上标注多重性 (1, 0..., 1... 等)
5. 构建用例模型(Use Case Modeling)
- 识别 Actor :谁 / 什么会与系统交互?
- 主要 Actor:使用系统主要功能的人(如借书人)
- 次要 Actor:管理和维护系统的人(如图书管理员)
- 外部系统 Actor:与系统交互的其他系统(如支付系统)
- 识别用例 :Actor 与系统的每次交互就是一个用例
- 用例命名格式:动词 + 名词(如"借阅图书""归还图书""查询图书")
6. 构建分析类图(Analysis Class Diagram)
- 不同于设计类图------分析类图是概念层面的
- 不需要写方法签名和具体实现细节
- 三种分析类(Stereotypes) :
<<boundary>>边界类:Actor 与系统的交互界面(如:借书界面)<<control>>控制类:协调和控制系统操作(如:借书控制器)<<entity>>实体类:存储和管理的业务数据(如:Book、Member)
7. OOA 与 OOD 的区别(考试中非常非常重要 ⭐)
| 维度 | OOA(分析) | OOD(设计) |
|---|---|---|
| 关注 | "做什么"(问题域) | "怎么做"(解决方案域) |
| 抽象层次 | 高层概念模型 | 具体实现细节 |
| 产出 | 分析类图、用例图 | 设计类图、序列图 |
| 与实现的关系 | 独立于实现 | 贴近特定实现技术 |
| 关注重点 | 业务逻辑、实体 | 架构模式、设计模式 |
- 简单记忆:OOA 问"What",OOD 问"How"
易错点
- 把所有名词都当成类:需求中的名词不一定都是类。"书名"是 Book 的属性,"学生"既是 Actor 也可能是类。需要仔细筛选。
- OOA 阶段过度关注实现细节:分析阶段只做"概念建模",不要考虑用什么数据库、什么框架、怎么存储数据。
- OOA 和 OOD 混淆 :有时在分析和设计之间来回跳。分析没有完成之前不要进入设计。记住:分析 = 理解问题,设计 = 解决问题。
- 边界类、控制类、实体类放错位置:例如把"数据库连接"当成实体类------数据库是设计问题,不是分析问题。
- 用例建模时遗漏 Actor:除了直接用户,还要考虑管理员、外部系统等。
高频考点
- ❗ OOA 的基本步骤和核心目标(高频简答)
- ❗ OOA 与 OOD 的区别(必考------简答或选择)
- ❗ 候选类识别的名词提取法和筛选标准(高频简答 / 应用题)
- ❗ 三种分析类(边界、控制、实体)的职责(中频选择)
- 用例建模的步骤
- 分析类图 vs 设计类图的区别
Lecture 8 --- 架构设计
快速理解
"建房子要先搭骨架还是直接砌砖?"架构设计就是软件系统的"骨架"设计。本讲介绍主流的架构模式和设计决策。
核心知识点
1. 架构设计(Architectural Design)的重要性
- 架构 :软件系统的整体组织结构和组件之间的关系
- 为什么重要 :
- 影响系统的非功能属性(性能、安全性、可靠性、可维护性等)
- 是涉众沟通的桥梁(不同角色从架构中看到不同的内容)
- 促进复用(架构级别的模式复用)
- 早期架构决策一旦做出,后期很难改变
2. 架构设计的主要决策
- 系统的整体风格 和架构模式是什么?
- 系统如何分解为组件?
- 组件之间如何通信?
- 系统的数据组织方式是什么?
- 如何满足关键的非功能需求?
3. 六大通用架构模式
(1)MVC(Model-View-Controller)
| 组件 | 职责 |
|---|---|
| Model(模型) | 数据和业务逻辑 |
| View(视图) | 数据展示 / 用户界面 |
| Controller(控制器) | 处理用户输入、协调 Model 和 View |
- 核心优点:关注点分离(Separation of Concerns)
- 缺点:对于简单应用来说可能过度设计
- 典型应用:Web 框架(Spring MVC、Ruby on Rails)、GUI 应用
(2)分层架构(Layered Architecture)
- 每一层只调用下一层的接口
- 典型层次:UI 层 → 业务逻辑层 → 数据访问层 → 数据库层
- 封闭架构(Closed Architecture):每层只能调用直接下层(最常见)
- 开放架构(Open Architecture):层可以跳过下层调用更底层(性能更好但耦合增加)
- 优点:逐步抽象、可替换性(换数据库只需改数据访问层)
- 缺点:每层增加开销,层数难确定
(3)客户端-服务器(Client-Server)
- Client:请求服务(发起通信)
- Server:提供服务(等待请求)
- 变体:多层架构(Thin Client + Application Server + Database Server)
- 优点:集中管理、易于扩展
- 缺点:服务器成为潜在瓶颈和单点故障
(4)管道-过滤器(Pipe-and-Filter)
- Filter(过滤器):对数据进行处理 / 变换的单元
- Pipe(管道):连接过滤器的数据通道
- 例子 :Unix 管道命令
cat file | grep pattern | sort - 优点 :
- 松耦合(过滤器独立运行)
- 可组合、可复用
- 支持并发
- 缺点:不适合交互式应用,不适合需要全局状态的处理
(5)仓库模式 / 中央数据(Repository / Shared Data)
- 所有组件共享一个中央数据结构
- 组件独立运行、通过中央数据通信
- 例子:IDE(编辑器、编译器、调试器共享 AST 仓库)、AI / 编译器中的黑板系统(Blackboard)
- 优点:数据一致性好
- 缺点:中央仓库成为性能瓶颈和单点故障
(6)事件驱动(Event-driven Architecture)
- 组件通过事件发布 / 订阅(Publish-Subscribe) 通信
- 组件不直接调用对方------通过事件总线(Event Bus)解耦
- 优点:极度松耦合和灵活
- 缺点:调试困难、全局顺序难以保证、复杂度高
4. 架构模式对比
| 模式 | 组件通信方式 | 适用场景 | 性能特点 |
|---|---|---|---|
| MVC | Controller ↔ Model ↔ View | Web 应用、GUI | 可接受 |
| 分层 | 相邻层间单向调用 | 企业应用 | 层数越深越慢 |
| Client-Server | 请求-响应 / 网络 | Web 系统 | 受限于网络 |
| Pipe-and-Filter | 数据流驱动 | 数据处理、编译器 | 可并行化 |
| Repository | 共享数据 | 数据密集型应用 | 数据访问瓶颈 |
| Event-driven | 事件发布 / 订阅 | GUI、实时系统 | 异步、高吞吐 |
5. 架构设计文档
- 架构视图(Architectural Views) :从不同角度描述架构
- Kruchten 4 + 1 视图模型:
- 逻辑视图(Logical View):功能需求
- 进程视图(Process View):并发和性能
- 开发视图(Development View):模块组织
- 物理视图(Physical View):部署
- 场景(Scenarios / Use Cases):将四个视图串联
- Kruchten 4 + 1 视图模型:
易错点
- MVC ≠ 三层架构:MVC 是表现层的设计模式,三层架构是宏观的分层(表现层 → 业务逻辑层 → 数据访问层)。一个系统可以有三层架构,其中的表现层内部用 MVC。
- 认为一种架构模式走天下:实际项目中往往是多种模式的组合。比如:整体用分层,分层中的某一层内部用 MVC,组件间通信用事件驱动。
- 架构设计 ≠ 详细设计:架构是"骨架"(宏观),详细设计是"血肉"(微观)。考试常考区别。
- 管道-过滤器适合交互式应用:错,它适合批处理 / 数据流应用,不适合需要频繁用户交互的场景。
- 事件驱动架构的调试困难常被忽略。
高频考点
- ❗ 六大架构模式的名称、特点和适用场景(必考------选择 / 简答 / 应用题选择模式)
- ❗ MVC 三层各自的职责(必考选择或简答)
- ❗ 分层架构的优缺点(高频简答)
- ❗ 给定系统场景,选择最合适的架构模式(必考------综合应用)
- 架构设计的主要决策(4个问题)
- 管道-过滤器的适用场景(编译器、数据处理)
Lecture 9 --- 面向对象设计 (OOD)
快速理解
OOA 回答"系统要做什么",OOD 回答"系统怎么用代码实现"。本讲介绍 OOD 的核心活动、设计模式和责任分配原则。
核心知识点
1. OOA → OOD 的过渡
OOA产出:分析类图、用例图 (概念层,关注"做什么")
↓
OOD输入:分析模型 + 非功能需求 + 技术约束
↓
OOD产出:设计类图(含方法签名)、序列图、接口定义 (实现层,关注"怎么做")
2. OOD 的主要活动
- 类设计:为每个类分配职责,设计属性和方法
- 协作设计:设计类之间的交互方式(用序列图建模)
- 设计模式应用:用成熟的设计方案解决常见的设计问题
- 接口设计:定义系统的对外接口
- 组件打包:将类组织为包 / 模块
- 非功能需求处理:满足性能、安全性等要求
3. 设计模式(Design Patterns)总览
设计模式 = 针对常见设计问题的可复用解决方案
(1)创建型模式(Creational)------ 处理对象创建的机制
| 模式 | 目的 | 例子 |
|---|---|---|
| Singleton | 确保类只有一个实例 | 日志管理器、配置管理器 |
| Factory Method | 在父类中定义创建对象的接口,子类决定实例化哪个类 | 数据库连接工厂 |
| Abstract Factory | 创建相关或依赖对象的家族,而不指定具体类 | UI 组件工厂 |
| Builder | 分步骤构建复杂对象 | 构建复杂文档 |
| Prototype | 通过复制已有对象创建新对象 | 克隆图形对象 |
(2)结构型模式(Structural)------ 处理类 / 对象的组合
| 模式 | 目的 | 例子 |
|---|---|---|
| Adapter | 将一个接口转换成客户希望的另一个接口 | 电源适配器、新旧系统对接 |
| Bridge | 将抽象部分与实现部分分离,使它们可以独立变化 | 跨平台图形渲染 |
| Composite | 以树形结构表示"整体-部分"关系,使客户端统一对待单个对象和组合对象 | 文件系统(文件 / 目录) |
| Decorator | 动态地给对象添加额外的职责 | Java I/O Stream |
| Facade | 为子系统提供统一的高层接口,简化使用 | 库的简单 API |
| Proxy | 为另一个对象提供替身或占位符以控制对其的访问 | 延迟加载、访问控制 |
(3)行为型模式(Behavioral)------ 处理对象间的通信和责任分配
| 模式 | 目的 | 例子 |
|---|---|---|
| Observer | 定义一对多的依赖,当一个对象状态改变时所有依赖者自动收到通知 | 事件监听、推送通知 |
| Strategy | 定义一系列算法,封装每个算法,并使它们可以互换 | 排序策略、支付方式 |
| Command | 将请求封装为对象,支持参数化、排队和撤销 | 撤销 / 重做 |
| State | 允许对象在其内部状态改变时改变其行为 | 电梯状态 |
| Template Method | 在父类中定义算法骨架,将一些步骤延迟到子类实现 | 框架中的钩子方法 |
| Iterator | 提供顺序访问聚合对象元素的方法而不暴露内部表示 | 遍历集合 |
4. GRASP 原则(General Responsibility Assignment Software Patterns)
GRASP 是 责任分配(谁该做什么) 的指导原则:
| 原则 | 解释 | 例子 |
|---|---|---|
| Information Expert | 谁拥有完成职责所需的信息,谁负责完成 | 订单类负责计算总价(它拥有商品信息) |
| Creator | 谁负责创建对象?通常由包含或紧密使用该对象的类创建 | Member 创建 Loan 实例 |
| Controller | 谁处理系统事件?通常用一个控制类处理外部输入 | 控制器接收 GUI 请求并委派给 Model |
| Low Coupling(低耦合) | 尽量减少类之间的依赖 | 通过接口而非具体类引用 |
| High Cohesion(高内聚) | 类内部的职责应高度相关 | 不要将"打印报表"和"管理用户"放在同一个类 |
| Polymorphism(多态) | 用多态替代条件判断(if-else / switch) | 用接口+不同实现替代类型判断 |
| Pure Fabrication | 当 Expert 不适用时,创建一个新的类来承担职责 | Controller 类、Service 类 |
| Indirection | 通过中间对象来解耦两个组件 | 适配器模式 |
| Protected Variations | 在不稳定点周围设置保护接口 | 接口隔离、抽象封装变化 |
易错点
- OOA 和 OOD 混为一谈:再次强调------OOA 是"分析问题",OOD 是"设计方案"。考试最爱考这俩的区别。
- 设计模式用错场景:例如,需要在运行时切换算法 → Strategy;需要对象状态变化时通知其他对象 → Observer;需要确保只有一个实例 → Singleton。需要根据场景匹配模式。
- GRASP 的 Information Expert 和 Controller 混淆:Expert 关注"谁能做",Controller 关注"谁接收外部事件"。
- Low Coupling 和 High Cohesion 的区别 :
- Low Coupling = 类之间的依赖少(松散)
- High Cohesion = 类内部职责集中(专注)
- 设计模式过度使用:不是任何问题都需要设计模式。保持简单。
高频考点
- ❗ OOA 与 OOD 的区别(必考------简答或选择)
- ❗ 设计模式的三大分类及每类的典型模式(高频简答)
- ❗ 给定场景选择合适的设计模式(高频应用)
- ❗ GRASP 原则的名称和含义(特别是 Information Expert、Low Coupling、High Cohesion)(必考)
- ❗ 低耦合和高内聚的概念(必考)
Lecture 10 --- 面向对象设计实例
快速理解
通过一个完整案例(如 ATM / POS / 图书馆系统)从头到尾走一遍 OOD 流程,把 Lecture 9 的理论付诸实践。
核心知识点
1. OOD 实例的完整流程(从需求到设计)
需求描述 → 用例分析(用例图) → 识别领域类(领域模型) →
序列图(用例实现) → 提取方法签名(设计类图) → 设计优化(应用设计模式)
2. 第一步:从需求到用例
- 阅读需求描述,标注Actor 和Use Case
- 画出用例图,显示 Actor 与 Use Case 的关系
- 用
<<include>>和<<extend>>关系捕捉用例间的依赖
3. 第二步:构建领域模型(Domain Model)
- 识别业务实体类(Entity Classes)
- 识别边界类(Boundary Classes,如 UI 界面)
- 识别控制类(Control Classes,协调工作流)
- 建立类之间的初步关系(关联、聚合、泛化)
4. 第三步:构建序列图(Sequence Diagrams)
- 每个主要用例 → 至少一张序列图
- 画出对象生命周期、消息交互顺序
- 识别对象间传递的消息 → 这些消息将成为目标类的方法
- 序列图驱动类设计------从序列图中"提取"类的方法签名
5. 第四步:设计类图(Design Class Diagram)
- 从序列图中提取方法,赋予具体的类
- 添加可见性修饰符(public / private / protected)
- 确定参数和返回类型
- 标注多重性和关系方向
- 应用设计模式重构
6. 第五步:设计优化
- 应用GRASP来评估责任分配是否合理
- 应用设计模式来解决常见问题
- 考虑非功能需求(性能约束可能影响设计决策)
7. 实例中的关键教训
- OO 设计是一个迭代过程:先有一个初步设计,然后逐步细化优化
- 分析和设计的分界线是模糊的:在实践中来回穿梭,但考试中要清楚区分
- 好的设计 = 低耦合 + 高内聚 + 合适的模式
易错点
- 序列图中缺少返回消息:序列图中不仅要画调用消息,也要画返回消息。很多初学者的序列图中只有调用没有返回。
- 把领域模型当作数据库设计:领域模型是概念层次的,不需要考虑外键、主键、索引等数据库细节。
- 设计时过早优化:先让设计正确(功能正确),再考虑性能优化。
- 一个序列图试图展示所有情况:每个序列图对应一个具体场景,而不是"所有可能的场景"。
- 忽略 Actor → 生命线的映射:每个 Actor 在序列图中会变成一条生命线。
高频考点
- ❗ 根据需求描述画序列图(必考------综合应用题)
- ❗ 从用例中识别出设计类(高频应用)
- ❗ 解释设计决策的合理性(如为什么用某个设计模式、为什么某个类承担某个职责)(高频简答)
- 序列图中方法的提取 → 类图方法的添加
- 类图的关系标注
Lecture 11 --- 面向对象详细设计
快速理解
在架构和 OOD 完成之后,深入到方法和属性级别的设计决策。SOLID 原则是本讲的核心。
核心知识点
1. SOLID 原则(五条面向对象设计核心原则)
S --- 单一职责原则(Single Responsibility Principle, SRP)
- 定义:一个类只能有一个改变的理由,即一个类只承担一种职责
- 违反 SRP 的表现:"上帝类(God Class)"------一个类什么都能干
- 例子:不应把"报表生成"和"用户管理"放在同一个类中
- 好处:类更容易理解、测试、维护
O --- 开闭原则(Open / Closed Principle, OCP)
- 定义:对扩展开放(Open for Extension),对修改封闭(Closed for Modification)
- 核心思想:新增功能时,应当通过添加新代码 (扩展)而不是修改已有代码(修改)来实现
- 实现方式:抽象(接口、抽象类)+ 多态
- 例子:Shape 接口 + Circle / Rectangle / Triangle 实现类。新增 Star 类不需要修改已有代码
- 好处:降低新功能引入 bug 的风险
L --- 里氏替换原则(Liskov Substitution Principle, LSP)
-
定义:子类对象应该能够替换父类对象,且不改变程序的正确性
-
违反 LSP 的经典例子:Square 继承 Rectangle
javaclass Rectangle { void setWidth(int w) { this.width = w; } void setHeight(int h) { this.height = h; } } class Square extends Rectangle { @Override void setWidth(int w) { super.setWidth(w); super.setHeight(w); // 保证宽高相等 } }问题:调用
rect.setWidth(5); rect.setHeight(3);时,如果是 Square 实例,高度变成了5,违反了用户预期。 -
LSP 的核心:子类不能违反父类定义的契约(方法的前置条件、后置条件、不变式)
-
检测方法:"is-a" 测试------如果 B 替换 A 后系统出问题,就不满足 LSP
I --- 接口隔离原则(Interface Segregation Principle, ISP)
-
定义:不应该强迫客户依赖它们不使用的方法
-
核心思想:接口要小而专注(Role Interface / Pluggable Interface)
-
违反 ISP:"胖接口(Fat Interface)"------一个接口包含太多不相关的方法
-
例子:
java// 违反 ISP interface Worker { void work(); void eat(); void sleep(); } // 符合 ISP interface Workable { void work(); } interface Eatable { void eat(); } interface Sleepable { void sleep(); } // Robot 只需实现 Workable,无需实现 eat() 和 sleep() -
ISP vs SRP:SRP 关注类 的职责单一,ISP 关注接口的粒度小
D --- 依赖反转原则(Dependency Inversion Principle, DIP)
-
定义:高层模块不应依赖低层模块,两者都应依赖于抽象。抽象不应依赖细节,细节应依赖抽象。
-
核心思想:面向接口编程(Program to an Interface, not an Implementation)
-
违反 DIP 的例子:高层类
OrderService直接依赖低层类MySQLDatabase -
符合 DIP 的例子:
OrderService → Database Interface ← MySQLDatabase / PostgreSQLDatabase -
依赖反转(DIP)≠ 依赖注入(DI) :
- DIP 是设计原则("要依赖抽象")
- DI 是实现技术(构造器注入、setter 注入、接口注入)
- DI 是实现 DIP 的一种方式
2. 设计契约(Design by Contract, DbC)
- 提出者:Bertrand Meyer(Eiffel 语言)
- 三要素:
| 元素 | 时机 | 解释 | 例子 |
|---|---|---|---|
| 前置条件(Precondition) | 方法调用前 | 调用方必须满足的条件 | num >= 0(计算平方根) |
| 后置条件(Postcondition) | 方法调用后 | 方法执行后保证的条件 | 返回值的平方等于输入 |
| 不变式(Invariant) | 始终成立 | 对象在其整个生命周期中始终保持的条件 | 余额 = 收入 - 支出 |
- 好处:明确契约、简化调试、文档化
3. 接口设计(Interface Design)
- 接口 = 一组操作的集合签名
- 设计原则:
- 接口应当小而专注(ISP)
- 接口方法应当清晰、明确、无歧义
- 每个方法说明:前置条件、后置条件、参数类型、返回类型
- 避免"标记参数"(如 boolean flag 改变行为)
4. 详细设计的产出物
- 详细类图:含所有属性类型、方法签名、关系细节
- 序列图:含具体方法的调用过程和参数传递
- 状态图:对状态复杂的类(如 Order、Document)
- 接口规格说明:每个接口的详细定义
易错点
- SOLID 原则之间的混淆 :
- SRP(类只做一件事) vs ISP(接口不要太宽泛)------ SRP 是类层面的,ISP 是接口层面的
- OCP(对扩展开放) vs DIP(依赖抽象)------ OCP 是目标(不改已有代码),DIP 是手段(依赖抽象)
- 里氏替换原则(LSP)只与继承相关:考试中给"正方形继承长方形"的经典反例让你分析为什么违反 LSP。
- DIP 与 DI 混淆:DIP 是原则(依赖抽象),DI 是实现(把依赖注入进来)。
- 设计契约三要素的顺序:前置条件(调用前必须满足)→ 方法执行 → 后置条件(执行后保证),不变式始终成立。
- 开闭原则不是"完全不修改":系统核心的抽象需要维护和扩展(这就是"开放"的部分),但稳定部分的实现代码不因功能扩展而修改。
高频考点
- ❗ SOLID 五个原则的英文缩写、全称和解释(必考------简答 / 选择)
- ❗ 里氏替换原则的经典反例(正方形 / 长方形问题)(必考)
- ❗ 开闭原则的解释和实现方式(高频简答)
- ❗ 依赖反转与依赖注入的区别(高频选择)
- ❗ 设计契约三要素(前置条件、后置条件、不变式)(高频)
- SRP vs ISP 的区别
Lecture 12 --- 结构化设计
快速理解
在面向对象之前,结构化设计是主流的软件设计方法。它以 功能(Function) 和 数据流(Data Flow) 为中心。本讲的核心是:DFD → 结构图,以及耦合 / 内聚评估。
核心知识点
1. 结构化设计 vs 面向对象设计
| 维度 | 结构化设计 | 面向对象设计 |
|---|---|---|
| 核心 | 功能 / 过程驱动 | 对象 / 数据驱动 |
| 基本单位 | 模块(Module)、函数 | 类(Class)、对象 |
| 主要工具 | DFD、结构图 (Structure Chart) | UML 类图、序列图 |
| 设计过程 | 功能分解(顶层→底层) | 职责分配 + 协作 |
| 数据与操作 | 分离(数据仓库 + 功能模块) | 封装(数据和操作在一起) |
| 评估标准 | 耦合度 + 内聚度 | 耦合度 + 内聚度(同样重要) |
2. 结构化设计的主要流程
需求 / 规格 → DFD(数据流建模) → 变换 / 事务分析 → 结构图 → 模块设计 → 耦合/内聚评估
3. 数据流图(Data Flow Diagram, DFD)
四个核心元素:
| 元素 | 图形 | 说明 | 例子 |
|---|---|---|---|
| 进程 (Process / Bubble) | 圆形 / 椭圆 | 对数据进行变换 / 处理 | "计算工资"、"验证订单" |
| 数据流 (Data Flow) | 箭头 | 数据在进程间流动,标注数据名称 | "会员信息"、"订单数据" |
| 数据存储 (Data Store) | 两条平行线 / 开口矩形 | 存储数据的仓库 | "会员数据库"、"库存文件" |
| 外部实体 (External Entity) | 矩形 | 系统的数据源或数据目标 | "顾客"、"银行系统" |
DFD 的分层抽象:
- 上下文图(Level 0):系统作为一个整体,显示与外部实体的数据流
- Level 1:将系统分解为 3-7 个主要进程
- Level 2:对 Level 1 中的每个进程进一步分解
DFD 规则:
- 进程必须有输入流和输出流(不能只有输入或只有输出)
- 数据流不能直接从外部实体到外部实体
- 数据流有方向(标注名称)
- 系统内部进程必须命名
4. 变换分析(Transform Analysis)
-
适用场景 :DFD 中有一个明显的变换中心(中心转换 / 核心处理逻辑)
-
识别方法:
- 追踪输入数据流,找到"传入流"到达"变换中心"的边界
- 追踪输出数据流,找到"变换中心"产生"传出流"的边界
- 中间的进程就是变换中心
-
映射为结构图:
结构图层次: ┌──────────────┐ │ 主控模块 │ ← 顶层 ├──────┬───────┤ │ 传入 │ 变换 │ 传出 │ ← 中层 │ 控制 │ 控制 │ 控制 │ ├──┬───┼──┬───┼──┬──┤ │ │ │ │ │ │ │ ← 底层具体功能模块 -
步骤:
- 在 DFD 中识别传入流、变换中心、传出流
- 设计顶层主控模块
- 将传入流映射为传入分支(输入处理模块)
- 将变换中心映射为变换分支(核心处理模块)
- 将传出流映射为传出分支(输出处理模块)
- 逐层分解,直到每个模块只执行一个功能
5. 事务分析(Transaction Analysis)
-
适用场景 :DFD 中有一个事务中心,根据输入类型分派到不同的处理路径
-
识别方法:寻找"根据输入类型选择不同处理路径"的模式
-
映射为结构图 :
┌────────────────────┐ │ 总控模块 │ ├────────────────────┤ │ 事务分派器 │ ├──┬──┬──┬──┬──┬──┤ │A │B │C │D │E │F │ ← 各事务处理模块 │型 │型 │型 │型 │型 │型│ │处 │处 │处 │处 │处 │处│ │理 │理 │理 │理 │理 │理│ -
步骤 :
- 识别事务中心和数据输入路径
- 设计总控模块和事务分派器
- 将每个事务类型映射为一个处理分支
- 为每个分支设计详细模块
6. 结构图(Structure Chart)
- 用途:展示模块间的层次结构、调用关系和数据传递
- 基本元素 :
- 模块(Module):矩形框,标注模块名
- 调用关系:箭头连接(从调用模块指向被调用模块)
- 数据耦合:带空心圆的小箭头(表示数据传递)
- 控制耦合:带实心圆的小箭头(表示控制标志传递)
7. 耦合度(Coupling)------从低到高,好的到差的
| 耦合类型 | 英文 | 说明 | 好坏 | 例子 |
|---|---|---|---|---|
| 数据耦合 | Data Coupling | 模块之间只传递简单数据 | ✅ 最好 | calcTax(amount) |
| 印记耦合 | Stamp Coupling | 传递结构体 / 记录,但只用其中一部分 | ⚠️ 一般 | calcPrice(Order order) 但只用了 order.date |
| 控制耦合 | Control Coupling | 一个模块传递控制标志影响另一个模块的逻辑 | ⚠️ 差 | process(type, data) 其中 type 控制内部 if-else |
| 公共耦合 | Common Coupling | 多个模块共享同一全局数据 | ❌ 很差 | 使用全局变量 config |
| 内容耦合 | Content Coupling | 一个模块直接访问另一个模块的内部 | ❌ 极差 | 直接访问另一个模块的私有数据 |
8. 内聚度(Cohesion)------从高到低,好的到差的
| 内聚类型 | 英文 | 说明 | 好坏 |
|---|---|---|---|
| 功能内聚 | Functional Cohesion | 模块所有元素为完成单一功能 | ✅ 最好 |
| 顺序内聚 | Sequential Cohesion | 模块的输出是下一个元素的输入 | ✅ 好 |
| 通信内聚 | Communication Cohesion | 模块内元素操作同一数据 | ⚠️ 可接受 |
| 过程内聚 | Procedural Cohesion | 模块内元素按特定执行顺序组合 | ⚠️ 可接受 |
| 时间内聚 | Temporal Cohesion | 模块内元素仅在同一时间执行(如初始化) | ❌ 差 |
| 逻辑内聚 | Logical Cohesion | 功能相关但逻辑不同(如一个函数同时处理多种打印) | ❌ 差 |
| 偶然内聚 | Coincidental Cohesion | 元素之间无任何有意义的关系 | ❌ 极差 |
好设计的目标 :高内聚 + 低耦合(High Cohesion, Low Coupling)
易错点
- DFD 不是流程图 / 活动图:DFD 展示"数据如何流动和转换",不是"控制流"(先做什么后做什么)。
- 变换分析 vs 事务分析混淆:变换分析 = 数据处理(输入→处理→输出),事务分析 = 分派(根据类型选择不同路径)。
- 耦合度排序从好到差:数据 < 印记 < 控制 < 公共 < 内容。考试中常给场景让你判断类型。
- 内聚度排序从好到差:功能 > 顺序 > 通信 > 过程 > 时间 > 逻辑 > 偶然。
- 结构图没有表达执行顺序 :结构图展示调用层次关系,不是时间序列。
- DFD 中进程必须同时有输入和输出:没有输入的进程不能存在(不可能凭空产生数据),没有输出的进程也是无效的。
高频考点
- ❗ 耦合度的五种类型(从好到差排序)(必考------选择 / 简答)
- ❗ 内聚度的七种类型(从好到差排序)(必考------选择 / 简答)
- ❗ 变换分析和事务分析的区别(高频简答)
- ❗ DFD 的四个基本元素和图形(高频选择)
- ❗ 结构化设计 vs 面向对象设计对比(高频简答)
- 结构图的模块表示和调用关系
- DFD 的分层抽象(上下文图 → Level 1 → Level 2)
Lecture 13 --- 软件测试(一)
快速理解
测试是保证软件质量的核心手段。本讲介绍测试的基础理论和黑盒测试方法。
核心知识点
1. 测试的基本概念
- 测试 ≠ 调试
- 测试:发现缺陷(找到 bug)
- 调试:定位和修复缺陷(修 bug)
- 测试的目标:以最小成本发现尽可能多的重要缺陷
- 测试无法穷尽:不可能测试所有可能的输入组合 → 必须使用有效策略(等价类划分、边界值分析等)
2. 验证(Verification)vs 确认(Validation)
| 概念 | 回答的问题 | 解释 | 关注 |
|---|---|---|---|
| Verification(验证) | "Are we building the product right?" | 产品是否按规格正确构建? | 过程正确性 |
| Validation(确认) | "Are we building the right product?" | 是否构建了客户真正需要的产品? | 需求满足度 |
- 记忆技巧:Verification = "Verify the spec"(验证规格),Validation = "Validate the user needs"(确认用户需求)
- V&V 贯穿软件生命周期
3. V-Model(V 模型)
需求分析 → ← 验收测试
设计 → ← 系统测试
详细设计 → ← 集成测试
编码 → 单元测试
- V 模型的本质:每个开发阶段都有对应的测试阶段
- 左半边是"开发向下",右半边是"测试向上"
4. 测试层级(Testing Levels)
(1)单元测试(Unit Testing)
- 测试对象:单个函数、方法、类
- 执行者:开发者(最常做的测试)
- 目标:验证代码逻辑的正确性
- 方法:白盒测试为主
- 工具:JUnit, pytest, Jest 等
(2)集成测试(Integration Testing)
- 测试对象:组件之间的接口和交互
- 执行者:开发者 + 测试团队
- 目标:验证组件能否正确协作
- 关注点:接口参数传递是否正确、数据是否一致、错误处理是否顺畅
(3)系统测试(System Testing)
- 测试对象:整个完整系统
- 执行者:独立测试团队(没有偏见,更客观)
- 目标:验证整个系统是否满足功能需求和非功能需求
- 测试类型:功能测试、性能测试、安全性测试、可用性测试
(4)验收测试(Acceptance Testing)
- 测试对象:客户场景下的系统
- 执行者:客户 / 最终用户
- 目标:确定系统是否满足客户需求、是否可接受
- 类型:
- Alpha 测试:在开发环境下由用户代表执行
- Beta 测试:在真实用户环境中执行
5. 黑盒测试(Black-box Testing)
定义 :不查看内部代码结构,仅基于需求规格设计测试用例
(1)等价类划分(Equivalence Partitioning)
- 原理:将输入数据划分为若干等价类,同一个类中所有值的测试效果等价
- 每个等价类只测一个代表值
| 等价类类型 | 定义 | 例子(年龄输入 0-150) |
|---|---|---|
| 有效等价类 | 合法输入 | 0 ≤ age ≤ 150 的整数 |
| 无效等价类 | 非法输入 | age < 0, age > 150, 非整数 |
- 步骤 :
- 分析输入条件
- 划分有效等价类和无效等价类
- 从每个类中选择一个代表值作为测试输入
- 关键 :不要忽略无效等价类------系统对非法输入也要正确处理
(2)边界值分析(Boundary Value Analysis, BVA)
- 原理:大量 bug 发生在输入的边界附近而不是中间值
- 方法:测试边界值及其两侧的值
- 例子:年龄 0-150 → 测试 -1, 0, 1, 149, 150, 151
- 一般边界值:最小值、最小值-1、最小值+1、最大值、最大值-1、最大值+1
- 边界值分析 vs 等价类划分 :等价类划分取代表值,边界值分析取边界及附近的特殊值。两者通常配合使用。
6. 测试用例设计原则
- 好的测试用例是可复现的(相同输入 → 相同输出)
- 每个测试用例独立(不依赖其他测试的结果)
- 每个测试用例应当发现一类错误而不是全部错误
- 测试用例 = 输入 + 前置条件 + 预期输出
易错点
- Verification vs Validation 考英文选择题 :最经典的考法。记住------Verification = 验证实现正确 (building product right),Validation = 验证需求正确(building right product)。
- 等价类划分时遗漏无效等价类:很多考生只划分了有效等价类,忘记了无效等价类(如负数、非数字等非法输入)。
- 边界值分析是否包含边界本身 :标准的 BVA 包含边界值。如 0-150,测试 -1, 0, 1, 149, 150, 151。
- 测试四个层级的具体测试内容搞混:单元测试(最小单元)→ 集成测试(接口)→ 系统测试(全系统)→ 验收测试(用户验收)。
- 系统测试和验收测试的执行者搞反:系统测试由独立测试团队做,验收测试由客户 / 用户做。
高频考点
- ❗ Verification vs Validation 的区分(必考------给英文定义选择题或简答)
- ❗ 测试的四个层级及其定义(高频选择)
- ❗ 等价类划分的应用(必考------综合应用题,给场景划分类并设计测试)
- ❗ 边界值分析的原理和应用(必考------综合应用题)
- ❗ 黑盒测试的定义和特点(高频简答)
- 测试用例的基本构成(输入+前置条件+预期输出)
Lecture 14 --- 软件测试(二)
快速理解
测试(二)延续测试(一),重点讲白盒测试(覆盖度分析、圈复杂度)和集成测试策略。这部分偏向技术实现层面。
核心知识点
1. 白盒测试(White-box Testing)
- 定义:基于代码的内部结构和逻辑设计测试用例
- 也称为:结构测试(Structural Testing)、Glass-box Testing
- 适用场景:单元测试阶段
- 目标:覆盖代码的不同执行路径
2. 代码覆盖度(Code Coverage)------四种主要标准
(1)语句覆盖(Statement Coverage)
- 要求:每个语句至少执行一次
- 最弱的覆盖标准,但是基础要求
- 局限性:即使语句都执行了,分支条件可能没覆盖到
(2)分支覆盖(Branch Coverage)
- 要求:每个判断的真假分支至少各执行一次
- 也称为:决断覆盖(Decision Coverage)
- 比语句覆盖强(但可能漏掉某些条件组合)
(3)条件覆盖(Condition Coverage)
- 要求:每个布尔子条件的 true 和 false 至少各取一次
- 例子:
if (A || B)→ 需要 A=true, A=false, B=true, B=false 各至少一次 - 注意:条件覆盖不一定覆盖所有分支(条件覆盖的分支覆盖)
(4)路径覆盖(Path Coverage)
- 要求:所有可能的执行路径至少执行一次
- 最强的覆盖标准
- 局限性:路径数量可能指数级增长("路径爆炸"),实际中不可能完全覆盖
覆盖度强弱关系:
路径覆盖 ⊃ 分支覆盖 ⊃ 语句覆盖
条件覆盖 ⊃ 语句覆盖(但条件覆盖不保证分支覆盖)
3. 圈复杂度(Cyclomatic Complexity)
-
定义 :衡量程序模块的复杂程度
-
三种等效计算方法:
CC = E - N + 2P- E = 流程图中边的数量
- N = 节点的数量
- P = 连通分量数(通常 = 1)
CC = 判定节点数 + 1- 判定节点:if, while, for, case 等分支点
- 最简单的计算方法
CC = 封闭区域数 + 1- 在流程图中数封闭区域的个数
-
用途:
- 指导需要多少测试用例(CC = 最少需要的路劲数)
- 判断模块是否需要重构(一般认为 CC > 10 时复杂度过高)
-
例子:
javavoid method(boolean a, boolean b) { if (a && b) { // 判定节点 1 // 路径 1 } else { // 路径 2 } if (a || b) { // 判定节点 2 // 路径 3 } else { // 路径 4 } }- 判定节点数 = 2(两个if)
- CC = 2 + 1 = 3
- 最少需要 3 个测试用例才能覆盖所有分支
4. 集成测试策略
(1)大爆炸集成(Big Bang Integration)
- 做法:所有组件开发完成后一次性集成测试
- 优点:简单,没有额外开销
- 缺点:错误定位极其困难(不知道是哪个组件的问题)
- 适用:小项目
(2)自顶向下集成(Top-down Integration)
- 做法:从顶层主控模块开始,按控制层次逐步向下集成
- 需要的辅助性模块:Stub(桩) ------模拟未实现的下层模块
- 优点:
- 最早验证系统架构和顶层逻辑
- 早期可看到系统原型
- 缺点:
- 底层关键功能测试滞后
- 需要大量 Stub 的开发
(3)自底向上集成(Bottom-up Integration)
- 做法:从底层模块开始,按调用层次逐步向上集成
- 需要的辅助性模块:Driver(驱动) ------模拟上层调用模块
- 优点:
- 底层功能测试充分
- 不需要 Stub
- 缺点:
- 很晚才能验证系统整体架构
- 需要开发 Driver
(4)三明治 / 混合集成(Sandwich / Hybrid Integration)
- 做法:同时从顶层和底层向中间进行集成
- 本质:Top-down + Bottom-up 组合
- 优点:较快验证系统功能
- 缺点:较复杂
(5)持续集成(Continuous Integration, CI)
- 做法:每次代码变更后立即进行自动化构建和集成测试
- 是敏捷开发的核心实践之一
- 要求:自动化构建系统、自动化测试套件
5. Stub vs Driver ------ 常考对比 ⭐
| Stub(桩) | Driver(驱动) |
|---|---|
| 模拟下层被调模块 | 模拟上层调用模块 |
| 自顶向下测试使用 | 自底向上测试使用 |
| 就是一个"假装的下层函数" | 就是一个"假装的上层调用器" |
| 返回预设的数据 | 调用被测试模块的方法 |
记忆技巧:
- S tub = Subordinate(下属 / 下层)
- D river = Director(导演 / 上层)
6. 回归测试(Regression Testing)
- 定义 :对修改后的代码重新运行已有测试,确保修改没有破坏已有的功能
- 为什么会引入回归 bug:代码之间的耦合------修改 A 不小心破坏了依赖 A 的 B
- 自动化:回归测试应尽量自动化,否则人工重复执行成本极高
- 时机:每次代码变更后(bug fix、功能添加、重构)
易错点
- 覆盖度标准之间的包含关系 :
- 路径覆盖 ⊃ 分支覆盖 ⊃ 语句覆盖(是对的)
- 条件覆盖 ⊃ 分支覆盖(不一定! 条件覆盖可能不满足分支覆盖的要求)
- 圈复杂度计算公式记混:最实用的是"判定节点数 + 1"。不要记错了。
- Stub 和 Driver 的方向搞反 :Stub 在下层 (被调用者),Driver 在上层(调用者)。自顶向下需要 Stub;自底向上需要 Driver。
- 语句覆盖 = 100% = 程序没有 bug:错!即使语句覆盖 100%,仍然可能有逻辑 bug。
- 大爆炸集成仅适合小项目:对于大型项目,大爆炸集成几乎不可能定位错误。
高频考点
- ❗ 四种覆盖标准的定义和强弱对比(必考------选择 / 简答)
- ❗ 圈复杂度的计算(三种方法)(必考------综合应用)
- ❗ 集成测试策略对比以及 Stub / Driver 的区分(必考------选择 / 简答)
- ❗ 回归测试的定义和目的(高频简答)
- 持续集成的概念
Lecture 15 --- 配置管理
快速理解
"多人协作开发时,谁改了什么?谁改了哪个版本?怎么合并?"配置管理解决的就是这些问题。本讲介绍配置管理的四大活动、核心概念和版本控制基础。
核心知识点
1. 配置管理(Configuration Management, CM)的定义
- CM 是一套管理软件变更的系统化方法
- 目标:确保软件产品在其生命周期中的完整性、一致性和可追溯性
- 核心问题:谁、什么时候、为什么、做了什么修改
2. 配置管理的四大活动
(1)配置识别(Configuration Identification)
- 确定哪些软件元素需要受控管理 → 这就是配置项(Configuration Item, CI)
- 配置项包括:代码文件、需求文档、设计文档、测试用例、构建脚本、配置文件、用户手册等
- 每个 CI 有唯一的标识符和版本号
- 配置项的选择是 CM 中最基础的活动
(2)配置控制(Configuration Control)
-
管理对配置项的变更(变更管理流程)
-
变更控制委员会(Change Control Board, CCB):评审变更请求的决策机构
-
标准变更流程:
提交变更请求 (CR) → 影响分析 → CCB 评审 → 批准 → 实施变更 → 验证变更 → 更新基线 拒绝 → 终止 -
关键点:不是所有变更都会被批准------不必要或不合理的变更应被拒绝
(3)配置状态报告(Configuration Status Accounting)
- 记录和报告每个配置项的当前状态 和变更历史
- 回答的问题:
- 这个 CI 当前是什么版本?
- 谁正在处理这个 CI?
- 哪些变更请求在等待中?
- 从某个基线开始已经做了多少变更?
(4)配置审计(Configuration Auditing)
- 验证:实际的产品和文档是否与记录的配置一致
- 确保"说的 = 做的"(实际产品 = 配置记录)
- 两种审计:
- 功能配置审计(FCA):确认功能是否满足需求
- 物理配置审计(PCA):确认产品文档是否完整,配置是否一致
3. 核心概念
| 概念 | 定义 | 要点 |
|---|---|---|
| 配置项 (CI) | 被配置管理控制的基本单元 | 代码、文档、脚本、数据均在范围内 |
| 基线 (Baseline) | 经过正式评审和批准的配置快照,是后续变更的参照起点 | 里程碑性质、不可随意修改 |
| 版本 (Version) | 配置项在特定时间点的状态 | 每个版本都是 CI 的特定实例 |
| 分支 (Branch) | 从主线分离的独立开发线 | 支持并行开发 |
| 合并 (Merge) | 将不同分支的变更整合 | 可能产生冲突,需要人工解决 |
| 标签 / 标记 (Tag / Label) | 给特定版本打上标识,便于后续引用 | 通常用于标记 Release 版本 |
基线的重要性:
- 基线 = 变更控制的基准
- 一旦确立基线,对基线的任何变更都必须走正式的变更流程
- 典型的基线:需求基线、设计基线、产品发布基线
4. 版本控制系统(VCS)
集中式(Centralized VCS)------ 如 SVN、CVS
| 优点 | 缺点 |
|---|---|
| 管理集中,权限控制简单 | 服务器单点故障 |
| 易于管理 | 网络中断无法提交 |
| 学习曲线平缓 | 分支操作较慢且存储冗余 |
分布式(Distributed VCS)------ 如 Git、Mercurial
| 优点 | 缺点 |
|---|---|
| 每个开发者都有完整仓库副本 | 学习曲线较陡 |
| 支持离线操作 | 仓库容量大 |
| 分支 / 合并操作快速高效 | 权限管理相对复杂 |
| 强大的社区支持(GitHub / GitLab) |
5. 分支策略(Branching Strategy)
(1)主干开发(Trunk-based Development)
- 只有一个长期分支(trunk / main),所有开发都在主干上
- 短生命周期功能分支,频繁合并(每日至少一次)
- 适合 CI / CD、自动化测试成熟的项目
- 减少合并冲突
(2)GitFlow
- 两种长期分支:master (生产发布)和 develop(开发主线)
- 临时分支:feature (功能开发)、release (发布准备)、hotfix (紧急修复)
- 从 develop 创建 feature 分支开发新功能
- 功能完成后合并回 develop
- 从 develop 创建 release 分支做发布准备
- release 分支合并到 master 和 develop
- 从 master 创建 hotfix 分支做紧急修复
- hotfix 合并回 master 和 develop
- 适合有正式发布周期的项目
(3)GitHub Flow / Feature Branch
- 简化版:master 分支始终可发布
- 从 master 创建 feature 分支 → 开发 → PR 评审 → 合并回 master
- 简洁,适合持续部署
易错点
- 配置项(CI)不仅是代码:很多考生误以为只有代码需要配置管理。实际上,需求文档、设计文档、测试用例、部署脚本等所有软件元素都是 CI。
- 基线 vs 版本混淆 :每个版本是 CI 的一个快照,但基线是经过正式评审的特殊版本,具有里程碑意义。不是每个版本都能称为基线。
- 版本控制 ≠ 配置管理:版本控制是配置管理的一个部分(主要对应配置识别和变更控制)。配置管理还包括配置审计、状态报告等。
- 变更不是都该批准的:不要以为所有变更请求都应该被 CCB 批准。变更需要评估影响,不合理的不批。
- SVN = 集中式,Git = 分布式:考试中常考这两种类型的特点对比。
- GitFlow 各分支的命名和用途容易混淆:master 是生产发布;develop 是集成开发;feature 是功能开发;release 是发布准备;hotfix 是紧急修复。
高频考点
- ❗ 配置管理四大活动的名称和内容(必考------简答)
- ❗ 配置项(CI)的定义和范围(高频选择)
- ❗ 基线的定义和作用(高频简答 / 选择)
- ❗ 集中式 vs 分布式版本控制系统对比(高频简答)
- ❗ 变更管理的标准流程(高频简答)
- 分支策略(GitFlow)
- 配置审计的 FCA 和 PCA 两种类型
Lecture 16 --- 成本估算
快速理解
"这个项目需要多少人做多久?要花多少钱?"成本估算是项目启动前最关键也最困难的活动之一。本讲介绍几种常见的估算方法,核心是 COCOMO 模型。
核心知识点
1. 为什么软件成本估算这么难?
- 软件是无形产品------没有物理制造过程
- 需求的变化 和不确定性
- 新技术的引入使历史数据参考价值降低
- 团队能力、经验也有显著影响
- 开发效率不是线性的(沟通成本随团队规模增加)
2. 主要估算方法总览
| 方法 | 原理 | 适用阶段 | 准确性 | 客观性 |
|---|---|---|---|---|
| 专家判断 (Expert Judgment) | 请有经验的专家来估算 | 任何阶段 | 取决于专家水平 | 主观 |
| 类比估算 (Estimation by Analogy) | 参考类似项目的实际数据 | 早期 | 中 | 中 |
| 算法模型 (Algorithmic Model) | 使用数学公式(如 COCOMO) | 有规模估算后 | 较高 | 客观 |
| 功能点分析 (Function Points) | 基于功能需求估算规模 | 需求完成后 | 较高 | 客观 |
| LOC 估算 (Lines of Code) | 基于代码行数估算 | 设计 / 实现阶段 | 随阶段推进提高 | 客观 |
| Pricing to Win | 按客户出价确定"成本" | 任何阶段 | 🚫 不推荐 | 操控 |
3. COCOMO 模型(Constructive Cost Model)
COCOMO 81:由 Barry Boehm 在 1981 年提出,有三个层次:
- 基本 COCOMO(Basic) :仅基于**规模(KLOC)**估算
- 不精确,但快速
- 中级 COCOMO(Intermediate) :规模 + 15 个成本驱动因子
- 更精确
- 详细 COCOMO(Advanced / Detailed) :分阶段应用成本驱动因子
- 最精确但也最复杂
三种项目类型
| 类型 | 英文 | 说明 | a (Effort) | b | c (Time) | d | 例子 |
|---|---|---|---|---|---|---|---|
| 组织型 | Organic | 小团队、熟悉领域、开发环境好、低复杂度 | 2.4 | 1.05 | 2.5 | 0.38 | 简单的 MIS 系统 |
| 半分离型 | Semi-detached | 中等规模、团队成员经验混合、中等复杂度 | 3.0 | 1.12 | 2.5 | 0.35 | 数据库系统、OS 实用程序 |
| 嵌入型 | Embedded | 强约束(硬件、实时、安全)、高复杂度 | 3.6 | 1.20 | 2.5 | 0.32 | 飞行控制系统、ATM 系统 |
基本 COCOMO 公式:
工作量:Effort = a × (KLOC)^b (单位:人月,Person-Months)
工期: Time = c × (Effort)^d (单位:月,Months)
人数: Staff = Effort / Time (约数)
例子:估计为 10 KLOC 的组织型项目:
- Effort = 2.4 × (10)^1.05 ≈ 2.4 × 11.22 ≈ 26.93 人月
- Time = 2.5 × (26.93)^0.38 ≈ 2.5 × 3.53 ≈ 8.83 月
- Staff ≈ 27 / 9 ≈ 3 人
中级 COCOMO 的 15 个成本驱动因子(只需知道有这些类别):
- 产品属性:RELY(可靠性)、DATA(数据库大小)、CPLX(复杂度)
- 硬件属性:TIME(时间约束)、STOR(内存约束)、TURN(周转时间)
- 人员属性:ACAP(分析师能力)、AEXP(应用经验)、PCAP(程序员能力)等
- 项目属性:MODP(现代编程实践)、TOOL(工具使用)、SCED(进度约束)
中级 COCOMO:Effort = a × (KLOC)^b × EAF
- EAF(Effort Adjustment Factor)= 15 个成本驱动因子的等级值的乘积
- 每个因子取值为:很低、低、正常、高、很高、极高
COCOMO II(1995 年更新):
- 适应现代开发实践(重用、增量开发、快速原型)
- 使用功能点 或对象点替代 KLOC 作为输入
- 三个阶段模型(Early Design → Post-Architecture → Post-Development)
4. 功能点分析(Function Point Analysis)
- 由 Allan Albrecht(IBM)在 1970s 提出
- 基于功能需求估算软件规模,与实现技术无关
五个功能维度:
| 维度 | 英文缩写 | 说明 | 例子 |
|---|---|---|---|
| 外部输入 | EI | 外部实体向系统输入数据 | 新增订单、修改客户信息 |
| 外部输出 | EO | 系统向外部输出数据(含计算 / 处理) | 生成报表、打印发票 |
| 外部查询 | EQ | 查询请求 + 返回结果(不含计算) | 查询订单状态 |
| 内部逻辑文件 | ILF | 系统内部维护的逻辑数据集合 | 客户信息表、产品目录 |
| 外部接口文件 | EIF | 系统引用的外部数据集合 | 外部汇率表 |
- 每个维度按复杂度(低 / 中 / 高)赋予权重
- 总 FP = 各维度加权和 → 可转换为代码行数(按语言转换率)
- 功能点 vs LOC :
- 功能点:与语言无关、可在早期估算、主观看程度高
- LOC:依赖语言、难以早期估算、客观
5. 三条重要的估算法则
| 法则 | 内容 | 管理含义 |
|---|---|---|
| Parkinson's Law | 工作会膨胀以填满可用时间 | 设置合理的截止期限很重要 |
| Brooks' Law | 给延期的项目加人会使其更延期 | 人月不是可互换的;加人增加沟通成本而非降低工作量 |
| Pricing to Win | 按客户预算倒推成本 | ⚠️ 不道德------可能牺牲质量和团队成员利益 |
Brooks' Law 的深层原因(记住常考 ⭐):
- 新人需要学习时间(ramp-up time),不仅不贡献还在消耗团队精力
- 沟通成本非线性增长 :n 个人的沟通链路数 = n(n−1)/2n(n-1)/2n(n−1)/2
- 工作不可分解:有些任务没法分配给多人并行做
易错点
- COCOMO 三种项目类型的特征混淆:Organic(小 / 熟悉 / 简单)→ Semi-detached(中等)→ Embedded(强约束 / 高复杂度)。考试中常给场景让你判断类型。
- 功能点五个维度的名称记忆顺序混乱:记住 EI(输入)、EO(输出)、EQ(查询)、ILF(内部文件)、EIF(外部文件)。
- LOC 估算 vs 功能点估算:LOC 依赖编程语言、可量化;功能点与技术无关、可早期估算。不是同一个东西!
- Brooks' Law 的精髓 :人月不可交换------给一个延期的项目加人只会让它更延期。不是"加人没用",而是"加人短期会拖慢进度"。
- Pricing to Win 不是合理的估算方法:这是按客户预算报价,而不是按实际工作量估算,可能导致项目亏本或质量下降。
- 基本 vs 中级 COCOMO 的区别:中级多了 15 个成本驱动因子(EAF 调整因子)。
高频考点
- ❗ COCOMO 三种项目类型和各自的特征(必考------选择或简答)
- ❗ 基本 COCOMO 公式的工作量 / 工期关系(必考------简答或计算)
- ❗ 功能点五个维度的名称(高频简答 / 选择)
- ❗ Brooks' Law 的解释和原因(必考------简答)
- ❗ Parkinson's Law(高频简答-概念)
- ❗ 各种估算方法的优缺点对比(高频选择 / 简答)
- COCOMO 的三个层次(基本→中级→详细)
- 中级 COCOMO 的 EAF 调整
必背速查表
1. SOLID 原则速查
| 缩写 | 全称 | 一句话 |
|---|---|---|
| S | Single Responsibility Principle | 一个类一个职责 |
| O | Open / Closed Principle | 对扩展开放,对修改封闭 |
| L | Liskov Substitution Principle | 子类可替换父类 |
| I | Interface Segregation Principle | 接口要小 |
| D | Dependency Inversion Principle | 依赖抽象不依赖细节 |
2. 耦合度从好到差
数据耦合 → 印记耦合 → 控制耦合 → 公共耦合 → 内容耦合
3. 内聚度从好到差
功能内聚 → 顺序内聚 → 通信内聚 → 过程内聚 → 时间内聚 → 逻辑内聚 → 偶然内聚
4. 测试覆盖标准从弱到强
语句覆盖 → 分支覆盖 → 条件覆盖 → 路径覆盖
(注意:条件覆盖不一定满足分支覆盖)
5. 圈复杂度口诀
判定节点数 + 1 (最简单版本)
6. Stub vs Driver
| Stub | Driver |
|---|---|
| 模拟下层(被调)模块 | 模拟上层(调用)模块 |
| 用于自顶向下集成测试 | 用于自底向上集成测试 |
7. Verification vs Validation
| Verification | Validation |
|---|---|
| "Building the product right" | "Building the right product" |
| 验证规格正确性 | 确认需求满足度 |
8. 聚合 vs 组合
| 聚合 (Aggregation) | 组合 (Composition) |
|---|---|
| 空心菱形 ◇ | 实心菱形 ◆ |
| 部分可独立存在 | 部分随整体消灭 |
| 弱整体-部分 | 强整体-部分 |
9. 功能点五个维度
EI (外部输入)→ EO (外部输出)→ EQ (外部查询)→ ILF (内部逻辑文件)→ EIF(外部接口文件)
10. COCOMO 三种项目类型
Organic(组织型) < Semi-detached(半分离型) < Embedded(嵌入型)
(越往右规模越大、复杂度越高、约束越强)
整理完成,祝你复习顺利,考试高分!
声明:以上内容均由 Claude Code 根据课程课件进行整理,用于期末复习