Lecture 4:软件设计与实现
Chinese-language Computer Science 408 note: Lecture 4:软件设计与实现. Original source available where applicable.
On this page
This note is currently available in Chinese. The original wording, formulas and diagrams are preserved to avoid an unverified technical translation.
描述:本节整理模块化、抽象、信息隐藏和模块独立性,完整转写耦合与内聚的等级次序;进一步说明 UML 顺序图的生命线、消息和激活,并按课件借书示例还原 18 条交互,最后总结类图、类关系及边界类/控制类/实体类识别方法。
4.1 模块与设计原则
4.1.1 模块
模块是具有独立名称、明确输入输出和特定功能的程序实体。它可以表现为函数、类、包、组件、服务或子系统。
良好模块应具有:
- 清晰而单一的职责。
- 最小且稳定的接口。
- 隐藏的内部实现。
- 可独立理解、测试、修改和复用的边界。
4.1.2 软件模块化
把复杂系统分解为若干职责明确、通过接口协作的模块,以控制复杂度并支持并行开发、测试和维护。
分解并非越细越好:模块过大难以理解,过小则接口和通信成本增加。应按职责、变化原因和信息边界划分。
4.1.3 抽象
抽象关注对象或模块“是什么、能做什么”,暂时忽略不影响当前层次的实现细节。
- 数据抽象:规定数据及其允许操作。
- 过程抽象:用一个名称代表一组步骤。
- 控制抽象:隐藏控制流程细节,如迭代器或事件机制。
4.1.4 信息隐藏
模块只公开完成协作所需的接口,把容易变化的设计决策、数据表示和算法封装在内部。其他模块不能依赖这些秘密,从而减少修改传播。
4.1.5 模块独立性
模块独立性表示一个模块只完成相对独立的功能,并尽量少依赖其他模块。衡量的两个核心指标是:
- 耦合:模块之间连接和依赖的紧密程度,越低越好。
- 内聚:一个模块内部各元素为同一职责协作的紧密程度,越高越好。
目标是:
$$ \text{高内聚}+\text{低耦合}\Rightarrow\text{较强模块独立性}. $$
4.2 耦合等级
课件图从左到右耦合逐渐增强、模块独立性逐渐减弱:
无直接耦合 < 数据耦合 < 标记耦合 < 控制耦合 < 外部耦合 < 公共耦合 < 内容耦合
独立性强 ------------------------------------------------------> 独立性弱
| 类型 | 含义 | 例子或问题 |
|---|---|---|
| 无直接耦合 | 两模块没有直接关系,通过上层协调或完全独立 | 最弱耦合 |
| 数据耦合 | 只通过简单参数传递必要数据 | 传入订单号而非整个全局对象 |
| 标记耦合 | 传递复合结构,但接收方只使用其中一部分 | 传整个记录只读取一个字段 |
| 控制耦合 | 传递控制标志,决定另一个模块内部逻辑 | mode=true 决定执行分支 |
| 外部耦合 | 共同依赖外部规定的协议、设备接口或文件格式 | 两模块都依赖特定通信协议 |
| 公共耦合 | 多个模块访问同一全局数据区 | 共享全局变量,修改影响难追踪 |
| 内容耦合 | 一个模块直接访问或修改另一模块内部实现 | 跳入内部代码、修改私有数据 |
NOTE 有些教材会因定义口径调整外部耦合与控制耦合的相对位置。考试应优先按题目所用教材分类;本课件给出的明确顺序如上。
降低耦合的方法:缩小接口、只传必要数据、消除全局状态、面向稳定抽象编程、通过事件或依赖注入隔离具体实现。
4.3 内聚等级
经典结构化设计教材通常把内聚分为七级;从左到右内聚逐渐降低、模块独立性逐渐减弱:
功能内聚 > 顺序内聚 > 通信内聚 > 过程内聚 > 时间内聚 > 逻辑内聚 > 偶然内聚
独立性强 -----------------------------------------------------> 独立性弱
功能单一 -----------------------------------------------------> 功能分散
| 类型 | 含义 | 例子 |
|---|---|---|
| 功能内聚 | 所有元素共同完成一个明确功能 | 计算订单总额 |
| 顺序内聚 | 一个元素的输出直接作为下一个元素的输入 | 读取原始数据、清洗后再计算统计量 |
| 通信内聚 | 多个操作使用或产生相同数据 | 读取一条记录并生成多个相关结果 |
| 过程内聚 | 元素必须按指定控制次序执行 | 先校验、再记录、再通知,但处理数据不一定相同 |
| 时间内聚 | 因在同一时间执行而组合 | 系统初始化或关闭时的一组不同操作 |
| 逻辑内聚 | 一组逻辑相似操作由控制参数选择其一 | 一个函数按标志执行多种输入操作 |
| 偶然内聚 | 元素之间几乎没有有意义联系 | 把杂项工具和无关处理放在同一模块 |
NOTE · 信息内聚的教材口径 有些教材在经典七级之外另列信息内聚:多个操作围绕同一数据结构组织,但通过各自独立的入口调用,例如抽象数据类型对同一对象提供增、删、改、查。它常被评价为较强内聚,但是否高于功能内聚、以及是否纳入统一排序,取决于教材体系;作答时应以题目给定口径为准,不能把它替换成经典七级中的“顺序内聚”。
提升内聚的关键是让模块围绕单一职责和单一变化原因组织,而不是只把代码放进同一文件。
4.4 顺序图
4.4.1 定义
顺序图(Sequence Diagram)是 UML 交互图,详细表示参与者实例、对象实例之间按时间先后发送消息的过程。
- 横向表示不同参与者或对象。
- 时间通常自上而下推进。
- 重点是“谁在什么时候向谁发送什么消息”。
4.4.2 参与者、对象和系统边界
参与者位于系统边界外,代表与系统交互的角色。系统边界内放置边界对象、控制对象、实体对象或其他协作对象。
对象头通常写成 对象名:类名,也可以只写对象名或匿名类实例。
4.4.3 生命线与激活
- 生命线:对象头下方的垂直虚线,表示该实例在一段时间内存在。
- 激活条:生命线上的细长矩形,表示对象正在执行操作或等待嵌套调用返回。
- 对象被创建时生命线从创建消息处开始;销毁时以
X结束。
WARNING 课件说对象应“错落有致”并不严谨。通常参与者和已有对象放在图顶端横向排列;只有较晚创建的对象,其对象头和生命线才从较低位置开始。位置首先表达生命周期,不是为了美观随意错开。
4.4.4 消息
消息是对象之间通信的规格,既可传递调用等控制信息,也可携带参数和返回数据。
常见消息:
| 消息 | 含义 | 常见画法 |
|---|---|---|
| 同步调用 | 发送者等待被调用操作返回 | 实线加实心箭头 |
| 异步消息 | 发送后不等待接收者完成 | 实线加开放箭头 |
| 返回消息 | 返回结果或控制 | 虚线箭头 |
| 自消息 | 对象调用自身操作 | 回折到同一生命线 |
| 创建消息 | 创建新对象 | 箭头指向新对象头 |
| 销毁消息 | 终止对象生命线 | 箭头指向 X |
复杂控制可用组合片段表达:alt 分支、opt 可选、loop 循环、par 并行。
4.4.5 课件“借书”顺序图完整转写
参与对象:
- 图书管理员。
借书界面类,边界类。借书控制类,控制类。读者类,实体类。借阅信息类,实体类。图书类,实体类。预约信息类,实体类。
图中的 18 条消息按时间顺序为:
- 图书管理员向借书界面录入读者信息。
- 借书界面请求借书控制类验证读者信息。
- 借书控制类调用读者类验证读者信息。
- 读者类返回验证成功。
- 借书控制类向借阅信息类查询读者借阅信息。
- 借阅信息类返回读者借阅信息。
- 借书控制类自调用,验证是否符合借阅条件。
- 借书控制类向借书界面返回并显示读者信息和借阅信息。
- 图书管理员向借书界面录入图书信息。
- 借书界面请求借书控制类验证图书信息。
- 借书控制类调用图书类,验证图书是否可借。
- 图书类返回“图书可借”。
- 借书控制类向借书界面返回并显示图书信息。
- 图书管理员在借书界面确认借阅。
- 借书界面请求借书控制类保存借阅信息。
- 借书控制类调用借阅信息类保存借阅信息。
- 借书控制类调用图书类修改图书状态。
- 借书控制类调用预约信息类删除相应预约信息。
该图体现典型分层协作:参与者只与界面交互,界面把业务请求交给控制类,控制类协调实体类完成校验和状态更新。
TIP 第 16 至 18 步共同构成一次借阅业务更新。真实系统还要考虑事务边界:保存借阅、修改图书状态、删除预约应保持一致,要么全部成功,要么整体回滚。
4.5 UML 与类图
4.5.1 UML
UML(Unified Modeling Language,统一建模语言)是一种标准化的可视化建模语言,用于说明、可视化、构造和记录软件密集系统的制品。
UML 是建模语言,不是完整的软件开发方法,也不规定团队必须采用哪一种过程模型。
常见 UML 图:
- 用例图、活动图、状态机图。
- 顺序图、通信图。
- 类图、对象图、构件图、部署图、包图。
UML 模型是系统语义信息的整体;UML 图只是从某个视角呈现模型信息。一个模型可以由多张图表达,删除某一张图不一定等于删除模型中的全部语义。
4.5.2 类图
类图描述类、接口、属性、操作以及它们之间的静态关系,是系统逻辑视图和静态结构建模的重要组成部分。
一个类通常分三格:
- 类名。
- 属性。
- 操作或方法。
可见性符号:
+:public。-:private。#:protected。~:package。
4.5.3 类之间的关系
| 关系 | 含义 | 记法要点 |
|---|---|---|
| 依赖 Dependency | 临时使用,供应者变化可能影响客户 | 虚线箭头 |
| 关联 Association | 对象间较稳定的结构联系 | 实线,可写角色、导航性和多重性 |
| 聚合 Aggregation | 较弱的整体-部分,部分可独立存在 | 整体端空心菱形 |
| 组合 Composition | 强整体-部分,部分生命周期依赖整体 | 整体端实心菱形 |
| 泛化 Generalization | 类之间的继承,is-a | 实线空心三角指向父类 |
| 实现 Realization | 类实现接口规定的契约 | 虚线空心三角指向接口 |
4.5.4 识别类的步骤
课件给出两步主线:
- 发现领域对象,从具体对象抽象出概念类。
- 在用例明确后,识别完成用例所需的对象,把具有相似属性和操作的对象归为类。
目标系统中的类常分为:
- 边界类:处理参与者与系统之间的界面,如页面、API 适配器。
- 控制类:协调某个用例的业务流程和对象协作。
- 实体类:保存领域概念、业务数据和规则。
识别后还要分配职责、确定属性与操作、补充类关系和多重性,并检查是否存在上帝类、重复职责或不必要依赖。
4.6 顺序图与类图的配合
- 类图描述系统有哪些类及其静态关系。
- 顺序图描述某个场景中对象如何按时间协作。
- 顺序图中的消息可帮助发现类所需的操作。
- 类图中的关联和依赖应能支撑顺序图中的合法通信。
二者应相互校验:若顺序图要求对象发送某消息,而类图中接收类没有相应职责或关系,设计可能不一致。
4.7 常考辨析
4.7.1 高内聚是否必然导致低耦合
二者通常正相关但不是逻辑必然。应分别检查模块内部职责与模块之间依赖,目标同时是高内聚和低耦合。
4.7.2 公共耦合与外部耦合
- 公共耦合是多个模块共享同一全局数据区。
- 外部耦合是模块共同依赖外部接口、协议、设备或数据格式。
4.7.3 顺序图与活动图
- 顺序图强调对象之间的消息时序。
- 活动图强调业务或控制流程,可突出分支、并发和责任泳道。
4.7.4 UML 是否是一种开发过程
不是。UML 只提供建模符号和语义,可配合瀑布、迭代、敏捷等不同过程使用。
相关知识
- 科目导航:复试常考科目导航
- 阅读顺序:上一篇:软件需求工程
- 关联专题:数据库设计
本节复盘
- 模块化通过抽象和信息隐藏控制复杂度,模块独立性由耦合与内聚共同衡量。
- 耦合由弱到强为无直接、数据、标记、控制、外部、公共、内容耦合。
- 经典七级内聚由强到弱为功能、顺序、通信、过程、时间、逻辑、偶然内聚;信息内聚属于另一种扩展口径。
- 顺序图自上而下表达消息时序,核心元素包括参与者、对象、生命线、激活条和不同类型消息。
- 借书顺序图通过边界类、控制类和实体类完成读者校验、图书校验及三项一致性更新。
- 类图描述类、接口和静态关系;UML 是建模语言,不是软件开发过程。