软件工程与计算II

SE-II-ch13-详细设计中的模块化与信息隐藏

软件工程与计算II
目录

# 13-详细设计中的模块化与信息隐藏

# Outline

不同的编程范式都可以谈模块化和信息隐藏,本节介绍结构化的模块化和信息隐藏,下两节介绍面向对象的模块化和信息隐藏。这也是历史的发展顺序

# 1. 内聚和耦合 概念(重要)

  1. 内聚:内聚表达的是一个模块内部的联系的紧密性:包括信息内聚、功能内聚、通信内聚、过程内聚、时间内聚、逻辑内聚和偶然内聚。
public class Rous{
    public static int findPattern(String text,String pattern)
    public static int average(Vector numbers)
    public static OutputStream openFile(String fileName)
}
  1. 耦合:耦合描述的是两个模块之间关系的复杂程度:包括内容耦合,公共耦合,重复耦合,控制耦合,印记耦合,数据耦合
public class Employee{
    public String name,emailID;
}
public class Emailer{
    public void sendEmail(Employee e,String text)
}

# 2. 模块化与信息隐藏思想

# 2.1. 设计好的软件

  1. 什么是好的软件所具有的?

# 2.1.1. Parnas 1972

首先,Parnas提出了信息隐蔽原则:在概要设计时列出将来可能发生变化的因素,并在模块划分时将这些因素放到个别模块的内部。这样,在将来由于这些因素变化而需修改软件时,只需修改这些个别的模块,其它模块不受影响。

  1. 管理人员(可管理性)
  2. 产品灵活性(灵活性)
  3. 可理解性(可理解性)
  4. 特征
    1. 允许编写一个模块,而几乎不了解另一个模块中的代码
    2. 允许在不重新组装整个系统的情况下重新组装和更换模块。

# 2.1.2. Stevens 1974

  1. 简洁性(Simplicity)
    1. 易于调试
    2. 易于分解
  2. 可观察性(Observability)
    1. 易于修改

# 2.1.3. Beohm 1976

  1. 可维护性
  2. 可扩展性
  3. 可理解性
  4. 可重用性

# 2.2. 动机

  1. 模块化是关于如何将软件程序划分(分解)为不同模块的思想
  2. 信息隐藏是更多地从模块的外部(抽象)来进行思考

# 2.3. 模块化与信息隐藏思想的发展

# 2.3.1. 背景

  1. 1960s
    1. 软件和硬件不同
    2. 软件制作
  2. 1970s
    1. 软件是数据 + 算法
    2. 瀑布模型
    3. 形式化方法
  3. 1980s
    1. 重用
    2. 对象
    3. 人件 peopleware

# 2.3.2. 历史发展

  1. 萌芽:Wirth1971; Parnas1972;
  2. 形成:Stevens1974; Parnas1978; Parnas1985;
  3. 发展 [从面向对象角度]:Eder1992; Hitz1995;
  4. 反思:McConnell1996; Demarco2002

# 2.3.3. Wirth1971 - 萌芽

  1. 软件通过逐步求精发展
  2. 核心思想:逐步求精 Stepwise Refinement
  3. 程序是在一系列优化步骤中逐步开发的
    1. 尽可能分解决策
    2. 解开看似相互依赖的方面
    3. 尽可能推迟有关展示细节的决策
  4. 对程序和数据结构的描述的修饰应并行进行。
  5. 以这种方式获得的模块化程度将决定程序可以适应目的变化或扩展或环境变化的难易程度。
  6. 每个优化都基于一组设计标准隐含了许多设计决策。
  7. 仔细编程并不是一件容易的事。

# 2.3.4. Parnas 1972 - 萌芽

  1. 关于将系统分解为模块的标准
  2. 什么是模块化?
    1. 模块:成为职责分配而不是子程序
    2. 模块化:包括在独立模块上的工作开始之前必须做出的设计决策("系统级"决策)
  3. 分解标准
    1. 信息隐藏
    2. 分解中的每个模块都以其对设计决策的了解为特征,而对其他所有决策则都不了解。
  4. 层次结构
    1. 如果可以在模块或程序之间定义某种关系
    2. 该关系是偏序的
    3. 我们关注的关系是"使用"或"依赖"
  5. 数据结构,其内部链接,访问过程和修改过程是单个模块的一部分(封装的思想)
  6. 操作系统和类似程序的队列中使用的控制块格式必须在"控制块模块"中隐藏

在此之前都是想法,而不是方法,之后才提出了真正可以实施的方法

# 2.3.5. Stevens 1974 - 形成

  1. “Structured design” 结构化设计
  2. 模块:一组一个或多个连续的程序语句,其名称具有系统其他部分可以调用的名称,并且最好具有其自己独特的变量名称集
  3. 耦合:通过一个模块到另一个模块的连接建立的关联强度的度量
  4. 内聚:巧合;逻辑;沟通;顺序;功能性
  5. 模块的控制范围
  6. 决策的效力范围
  7. 当决策的影响范围在包含决策的模块的控制范围内时,系统会更简单

# 2.3.6. Stevens 1974: Designing the structure 设计结构

  • 步骤1:勾画出问题的功能图

  • 步骤2:确定外部概念性数据流。
  • 步骤3:确定问题中的主要外部概念数据流

  • 步骤4:使用源模块针对每个概念输入流设计源结构,该模块存在于大多数抽象输入数据的位置;简单的下沉模块

  • 步骤5:对于每个模块,确定产生该模块返回的表单所需的最后一次转换

  • 担心的是一个地方的变更会导致其他部分的变更

# 2.3.7. Parnas 1978 - 形成

  1. 文章:“设计易于拓展的软件”
  2. 迈向更好结构的步骤
    1. 需求定义:首先确定子集
    2. 信息隐藏:接口和模块定义
    3. 虚拟机概念
    4. 设计"用途"结构
  3. "依赖"关系
    1. 如果A可能需要正确执行B才能完成A规范中描述的任务,则A使用B
    2. 满足以下所有条件时,允许A"使用"B:
      1. A本质上更简单,因为它使用B
      2. B实际上不复杂,因为它不允许使用A
      3. A 有一个有用的子集,包含B,不需要A
      4. 没有可以想象的有用的子集包含A但不包含B

# 2.3.8. Parnas 1985 - 形成

  1. 文章:“复杂系统的模块化结构”
  2. 模块说明
    1. 主要隐藏
    2. 角色
    3. 分配模块特定职责的标准
  3. 模块层次结构
    1. 顶层分解
    2. 二级分解
    3. 第三级
  4. 隐藏
    1. 主要隐藏:模块中包含的隐藏信息。
    2. 次要隐藏: 用于实施模块的实施决策。
  5. 顶层分解
    1. 硬件隐藏模块
    2. 行为隐藏模块
    3. 软件决策模块

# 2.3.9. Eder 1992 - 发展

  1. 文章:“面向对象系统中的耦合和内聚”
  2. "良好"设计准则
  3. 面向对象的耦合:相互作用耦合;组件耦合;继承耦合
  4. 面向对象的内聚:方法内聚;类内聚;继承内聚

# 2.3.10. Hitz 1995 - 发展

  1. 文章:“在面向对象的系统中测量耦合和内聚”
  2. 类级耦合
  3. 对象级耦合
  4. 耦合和内聚综合指标的框架
  5. 定量分析面向对象系统

# 2.3.11. McConnell 1996 - 反思

  1. 文章:“缺少行动:信息隐藏”
  2. 信息隐藏
    1. 是软件工程研讨会的设计思路之一
    2. 不需要或不依赖任何特定的设计方法
    3. 其特点是"隐藏"
    4. 最常见的隐藏是您认为可能会更改的设计决策
    5. 询问需要隐藏哪些内容,可以支持所有级别的良好设计决策

# 2.3.12. Demarco 2002 - 反思

  1. 内容:描述了75年自己写的文章的10条内容,其中有几条到现在(2002)仍然是对的

  1. 第五条:接口隔离原则

# 3. 模块化

  1. 计算机系统不是单体的:它们通常由多个交互模块组成。
  2. 长期以来,模块化一直被视为廉价,高质量软件的关键。
  3. 系统设计的目标是决定:
    1. 什么是模块;
    2. 模块应该是什么?
    3. 模块之间如何交互。

# 3.1. 什么是模块?

  1. 通用视图:一段代码。有局限性。
  2. 编译单元,包括相关的声明和接口
  3. 大卫·帕纳斯 Parnas:一个工作单元。
  4. 编程单元(程序,类等)的集合
    1. 在整个系统中具有明确定义的界面和目的,
    2. 可以独立分配给开发人员
  5. 课本总结:模块是一个词汇上邻接的程序语句序列,由边界元素限制范围,有一个聚合标识符。

# 3.2. 为什么要将系统进行模块化?

  1. 管理:分而治之
  2. 演进:分离系统的各个部分,以便将一个部分的更改与其他部分的更改隔离开
    1. 直接性原则(将需求明确分配到模块,理想情况下一个需求(或多个)映射到一个模块)
    2. 连续性/局部性原则(需求的微小变化仅触发对一个模块的更改)
  3. 理解:促使我们的系统更加容易被理解
    1. 作为思维大小的块的组成,例如7±2规则
    2. 一部分只有一个问题,例如本地性原则,封装,关注点分离
  4. 关键问题:模块化使用什么标准?->信息隐藏

# 3.3. 结构化设计中的耦合

# 3.3.1. 模块化

  1. 两种思路

  1. 可以根据数据总线进行切割
  2. 可以根据门电路进行划分

# 3.3.2. 模块之间

  1. 连接:连接是对其他地方定义的某些标签或地址的引用
  2. 联系(两个模块之间)的复杂度
    1. 数量
    2. 程度

# 3.3.3. 结构化的耦合

  1. 耦合是对从一个模块到另一个模块的连接所建立的关联强度的度量。
    1. 连接有多复杂
    2. 连接是指模块本身还是模块内部的东西
    3. 什么是正在发送或接收的内容

# 3.3.4. 耦合的强度 1 - 连接有多复杂

  1. 连接模块:对常见的环境:全局变量,范围
  2. 对于其他模块

# 3.3.5. 原则一:全局变量是被认为是有害的

# 3.3.6. 连接到全局环境

  1. 假设N1 + N2 = N,M1 + M2 = M
    1. 如果M个模块共享N个元素【全局变量
      1. 有潜在的N * M个连接
      2. (N1 + N2) * (M1 + M2)= N1 * M1 + N2 * M2 + N1 * M2 + N2 * M1
    2. 如果M1模块有N1个共享元素,而M2模块有N2个共享元素(也就是N1 对应 M1,而N2 对应M2):存在潜在的(N1 * M1 + N2 * M2)连接
  2. 封装减少耦合:抽象和分解
    1. 将潜在的共享元素细分成组
    2. 将每个组的访问权限限制为最小的模块子集

# 3.3.7. 常见环境的缺陷

  1. 连接将每个模块共享给每个其他这样的模块
    1. 一个模块中的错误和更改可以传播到其他模块
    2. 理解一个模块需要其他人的帮助
    3. 更难于重用
  2. 通用环境中的每个元素都会增加整个系统的复杂性

# 3.3.8. 原则二:如果没有特殊要求,让代码清晰一点 to be explicit

  1. 字典:可修改的

  1. 属性:明确的

# 3.3.9. 明确的和可修改的

  1. 属性【明确的】和字典【可修改】
  2. 明确的调用和事件
  3. 显式子类和数据驱动的代码

如果没有特殊要求,那么选择属性这样的明确的代码

# 3.3.10. 原则三:不要重复

  • 如何消除重复呢?先写接口

  • 也就是我们只需要new一个printer就行

# 3.3.11. 耦合的强度 2 - 连接是指模块本身还是模块内部的东西

  1. Connections that address or refer to a module as a whole by its name yield lower coupling than connections referring to the internal elements of another module 以其名称寻址或整体引用一个模块的连接产生的耦合比引用另一个模块内部元素的连接产生的耦合低(整体连接的耦合程度低于模块内部元素连接的耦合)

  1. 关联到内部是不合适的,应该是关联到接口
  2. 接口耦合度低,而如果是具体关联耦合度高

# 3.3.12. 原则四:面向接口编程

# 3.3.13. 结构良好的系统

通过已定义的接口通过传递的参数进行通讯

# 3.3.14. 耦合的强度 3 - 应该对外提供什么和接收什么

下面的是控制耦合的一个例子:

上层将控制信息传递给下层,下层根据控制信息来switch

  1. 在上层已经处理好需要处理啥,把switch放在上层
  2. 不需要传递控制信号,直接调用要进行处理的下层

# 3.3.15. 结构化方法中的耦合强度

从上向下耦合度降低

  1. 内容耦合:①另一个模块修改一个模块的代码【在以前的语言有的可以,现在不行】②一个模块依赖另一个模块的内容
  2. 公共耦合:全局变量
  3. 重复耦合:两个模块有相同的重复代码
  4. 控制耦合:传递的人和接收的人都需要管理控制信号
  5. 印记耦合:传递数据结构,但只用了其中的一部分
  6. 数据耦合:传递必要数据的连接,发送数据正好
  7. 没有耦合是最好的:但是是做不到的,完全独立不能协作
  8. 几乎没有没有耦合的系统,只能降低耦合
  9. 重复耦合及以上不允许存在
  10. 控制耦合和印记耦合都要尽量消除

注:内容耦合、重复耦合和公共耦合是不能接受的。数据耦合是最好的,而控制耦合和印记耦合是可以接受的

# 3.4. 结构化设计中的内聚

从上到下内聚性增加

  1. 偶然内聚:没什么关系(避免)
  2. 逻辑内聚:将一系列相关操作放在一起,由其他模块进行调用
  3. 时间内聚:操作和时间有关系
  4. 过程内聚:更强调按照一定步骤进行调用
  5. 通信内聚:在过程内聚的基础上,对相同的数据进行操作
  6. 功能内聚:只执行一个单一操作或实现一个单一的功能
  7. 信息内聚:多个操作,相互相对独立,并且都在相同的数据结构上完成,比如ArrayList类
  8. 时间、过程、通信尽量做
  9. 最好能做到功能、信息内聚

我们分离控制过程与具体功能的执行过程:

负责控制过程(控制器对象)的模块可以是通信内聚、过程内聚、时间内聚的,因为它们需要控制任务的分配和执行的流程

执行具体功能的模块应该是功能内聚或信息内聚的。

# 3.4.1. 内聚

  1. 实现独立模块的方式
    1. 减少不在同一模块中的元素之间的关系
    2. 增加同一模块中元素之间的关系

# 4. KWIC案例

  1. 一共有4种Java的实现方式,注意复习

# 4.1. KWIC

  1. 简称KWIC,⼜称上下⽂关键词索引,由IBM的卢恩首创,是最早出现的机编索引, 1960年首次用于美国化学文摘社出版的《化学题录》(Chemical Titles)。
  2. KWlC索引的的编制特点是:
    1. 使用禁用词表选择标题中具有检索意义的词为关键词,并将其作为确定索引条目的依据;
    2. 关键词的排检点设于标题的中部,所有索引条目按关键词的字顺竖向排列;
    3. 保留文献篇名中关键词前后的上下文,如⽂献名称过长,可以以轮排的形式移至条目的前部或后部;款目后跟随该信息资源的位置。
  3. 上述条目均按关键词的字顺排列在相应位置,检索时先在检索入口处查找与检索课题有关的关键词,再通过阅读上下⽂寻找符合检索要求的文献。可以按排检点为中心对同⼀关键词有关的资源集中检索查找,是这⼀索引的优点;不足是将索引的排检点设置在中部不符合⽤户使⽤习惯。

  1. 对很多的关键词进行关键词排序

# 4.2. KWIC来实现系统的模块化

  1. KWIC索引系统接受:
    1. 一组有序的线
    2. 每行是一组有序的单词
    3. 每个单词都是一组有序的字符
  2. 每行都"循环移位"并通过以下方式复制:
    1. 反复删除第一个单词
    2. 将其附加在行的末尾
  3. 输出按字母顺序排列的所有行的所有循环移位的列表

  1. 通过下标就可以排序数组,下标是title的中间

# 4.3. 根据功能进行设计

# 4.4. 循环位移算法的实现

# 4.5. 信息隐藏

  1. 基本思想:每个模块都隐藏了重要设计决策的实现,因此只有该模块的组成部分才知道详细信息:特别是如果存在所有可能的设计更改的列表-隐藏假设列表
  2. 所有设计决策彼此独立
  3. 两种常见的信息隐藏:
    1. 一是根据需求分配的职责,因为实践表明,需求是经常变化的,频率和幅度都很大;
    2. 二是内部实现机制,常见的变化主题包括硬件依赖,输入输出形式,非标准语言特征和库,负责的设计和实现,复杂的数据结构,复杂的逻辑,全局变量。数据大小限制等。

# 4.6. 根据设计决策进行设计

  1. 模块之间的交互是通过接口实现的

# 4.7. CircularShifter的定义

  1. 有无更改调用的方法:不用
  2. 有无更改调用的方法之中的实现:可能更改

# 4.8. 分解标准

  1. 第一种模块化:处理过程中的每个主要步骤都是一个模块
  2. 第二种模块化:
    1. 信息隐藏:每个模块都有一个或多个"隐藏"
      1. 行:字符/行的存储方式
      2. 循环位移:旋转算法,旋转存储
      3. 字符表化:alpha的算法,alpha的惰性
      4. 每个模块都以其对设计决策的了解为特征,而对其他所有决策则都不了解。
  3. 预期不到的变更是无法进行规避的

# 4.9. 可修改性比较

按照算法分解比按照决策分解的修改范围大的多

两种方案类似主程序子程序和面向对象风格,但这是体系结构设计的风格,这里是详细设计角度上讲

# 4.10. 独立的发展

  1. 模块化 1
    1. 必须先设计所有数据结构,然后才能进行并行工作
    2. 需要复杂的描述
  2. 模块化 2
    1. 必须先设计接口,然后才能开始并行工作
    2. 仅简单描述

# 4.11. 可理解性

  1. 第二种模块化是更好的
    1. 主观性判断
    2. 更低的模块之间的耦合:面向接口编程

# 4.12. 模块化

  1. 一个职责设计而不是一个子程序
  2. 由特定于自己的设计决策表示,而其他模块则未知
  3. 在实现上支持灵活性
  4. 不要代表过程中的步骤
  5. 低耦合,高内聚

# 4.13. 结论

  1. 模块
    • 可以被放置到一起的去形成一个完成系统的模块
    • 职责分配,子过程,内存加载,功能部分
  2. 模块化:设计独立的模块
  3. 封装
    • 语言便利性
    • 面向对象三大基本思想:数据和行为在一起,体现共同职责
  4. 信息隐藏
    • 设计原则
    • 信息隐藏是更高的设计原则

# 5. 结构化的模块化

  1. 1-2道大题是关于设计模式的

# 6. MSCS中的模块化思想的应用

# 6.1. 低耦合处理

  1. 软件体系结构的分层设计中:
    • 不同层的模块之间仅能通过程序调⽤与数据传递实现交互,不能共享数据(例如 Model 层建⽴⼀个数据对象并将引⽤传递给 Logic 层使⽤)否则会导致公共耦合。
  2. 软件体系结构的逻辑包设计中:
    • 依据功能的特点将三个层次进⼀步划分为更⼩的包,⽽不是只使⽤ Presentation、Logic和 Model 三个包,可以通过包分割实现接⼝最⼩化,这能去除不必要的耦合。

# 6.2. 软件体系结构的物理包设计中

  1. 将不同包的重复内容独⽴为单独的包以消除重复,避免产生隐式的重复耦合;

# 6.3. 详细设计中对象创建者的选择:

  1. 如果两个对象 A、B 间已有比较高的耦合度了, 那么使⽤ A 创建 B 或者反之就不会带来额外的耦合度。这就是表 12-4 内容的核⼼思想——不增加新的耦合

# 6.4. 详细设计中选择控制风格:

  1. 解除界面与逻辑对象的直接耦合。

# 6.5. 高内聚处理

  1. 软件体系结构的分层设计中:
    • 三个层次都是⾼内聚的,⼀个处理交互任务, ⼀个处理业务逻辑,⼀个处理数据持久化。
  2. 软件体系结构的逻辑包设计中:
    • 将三个层次进⼀步划分为更⼩的包,可以实现每个更⼩的包都是⾼内聚的。
  3. 详细设计中抽象类的职责:
    • 要求状态与⽅法紧密联系就是为了达到⾼内聚 (信息内聚)。
  4. 详细设计中使⽤控制⻛格:
    • 控制⻛格分离了控制逻辑,可以实现业务逻辑对象的⾼内聚(功能内聚)。因为封装了控制逻辑,所以控制器对象承载了不可避免的顺序内聚、通信内聚和逻辑内聚,这就要求控制器对象必须是受控的,也是它们为什么倾向于对外委托⽽不是⾃⼰进⾏业务计算的原因。

# 7. 结构化的信息隐藏

信息隐藏其实就是利用了抽象的方法。抽象出每个类的关键细节,也就是模块的职责(什么是公开给其他人的,什么是隐藏在自己模块中的)。换句话说,抽象出来的就是接口, 隐藏的就是实现,它们共同体现了模块的职责。

通过分别关注实现和接口,抽象可以使得面向对象方法拥有更好的效率和更多的灵活性。

# 7.1. Information 信息

  1. 信息隐藏
  2. 什么需要隐藏?变更
    1. 数据表示
    2. 设备的属性(必需属性除外)
    3. 实施世界模型
    4. 支持政策的机制
  3. 信息隐藏的核心是将每个模块都隐藏一个重要的设计决策

# 7.1.1. 最有可能修改的设计领域

  1. 硬件依赖:外部软件系统
  2. 输入输出格式:DB, Internet, UI, …
  3. 非标准语言特性和依赖路径:Platform: os, middleware, framework…
  4. 困难的设计和实现部分
    1. 通常是被设计的很不好的部分或者是需要被重新设计和重新实现的部分
    2. Complex…, monitor, exception, log, …
  5. 复杂的数据结构,被多个类使用的数据结构,或者是不让你满意的数据结构
    1. Separate model from logic 逻辑分块
  6. 复杂逻辑,他们被认为就像数据结构一样复杂。
  7. 算法,时间表,时间紧迫,实现紧迫
  8. 全局变量:常常并没有并真正使用,但是常常受益于权限访问的隐藏:Data Access Routines
  9. 数组声明和循环限制。
  10. 以及业务规则:比如法律、规则、正则和被转换成计算机系统的过程。

# 7.2. 隐藏

  1. 尝试本地化未来的变化 try to localize future change
    1. 隐藏可能会独立更改的系统详细信息
    2. 分开可能具有不同变化频率的独立部分
    3. 暴露接口一般不太可能改变
  2. 容易变化的隐藏,不容易变化的展示

# 7.3. 信息隐藏

这里“信息”到底指的是什么?其实这里的信息就是指模块的秘密,就是对模块来说容易变化的地方 [McConnell l 996b] 。模块的秘密主要分为两类 [Parnas 1985] :

  1. 一是根据需求分配的职责,因为实践表明需求是经常变化的,频率和幅度都很大(参见第 21 章)
  2. 二是内部实现机制

而“隐藏”就是希望把未来的改变限制在本地,只暴露出不容易变化的接口

# 7.3.1. 接口与实现

  1. 模块的用户和实施者对此有不同的看法。
  2. 界面:用户对模块的看法。
  3. 仅描述用户使用模块需要知道的内容
  4. 使其更易于理解和使用
  5. 接口描述了该模块提供的服务,但没有描述如何提供这些服务

# 7.3.2. 什么是接口

  1. 作为合同的接口:模块发布的任何内容
    • 提供的接口:模块的客户端可以依赖于
    • 所需的接口:该模块可以依赖于其他模块
  2. 语法接口
    1. 如何调用操作
      1. 操作签名列表
      2. 有时也是有效的呼叫操作命令
  3. 语义接口
    1. 这些操作是做什么的,例如
      1. 前置条件和后置条件
      2. 用例

# 7.3.3. 进一步原则

  1. 显式接口:使模块之间的所有依赖关系明确(无隐藏的耦合)
  2. 低耦合-接口少:最小化模块之间的依赖性
  3. 小接口
    1. 保障小接口
      1. 将许多参数组合到结构/对象中
      2. 将大型接口分为几个接口
    2. 面向接口编程
  4. 高内聚:一个模块应该封装一些定义良好的,连贯的功能(稍后会详细介绍)

# 7.3.4. 耦合与内聚

  1. 内聚力是模块之间的一致性的度量。
  2. 耦合是模块之间的交互程度。
  3. 需要高内聚和低耦合。

# 7.4. Module Guide

# 7.4.1. 模块的主要秘密

  1. 主要秘密描述的是这个模块所要实现的用户需求。是设计者对⽤户需求的实现的⼀次职责分配。有了这个描述以后,我们可以利⽤它检查我们是否完成所有的⽤户需求,还可以利⽤它和需求优先级来决定开发的次序。

# 7.4.2. 模块的次要秘密

  1. 次要秘密描述的是这个模块在实现职责时候所涉及的具体的实现细节。包括数据结构,算法,硬件平台等信息。

# 7.4.3. 模块的角色

  1. 描述了独⽴的模块在整个系统中所承担的⻆⾊,所起的作⽤。以及与哪些模块有相关联的关系。

# 7.4.4. 模块的对外接⼝

  1. 模块提供给别的模块的接⼝。

# 7.4.5. 循环位移模块的模块说明

# 7.4.6. 复杂系统的示例

  1. A-7E飞机
  2. 极其复杂的机上飞行计划
  3. 内存有限
  4. 实时约束

# 7.4.7. Parnas’s Experience

  1. “当我们试图在没有指南的情况下工作时,……责任要么在两个模块要么没有。 有了模块指南,设计的进一步进展显示出相对较少的疏忽。”
  2. 集成测试仅用了一周
  3. 仅发现了9个错误。
  4. 错误隔离到单个模块的位置。
  5. 所有的bug都被很快的修复了

# 7.5. 思想的应用(信息隐藏处理)

# 7.5.1. 在软件体系结构设计的分层设计中:

  1. 经验表明软件系统的界⾯是最经常变化的, 其次是业务逻辑,最稳定的是业务数据。这就是分层⻛格建⽴ Prensentation、 Logic 和 Model 三个层次的原因,它们体现了决策变化的划分类型,它们之间的依赖关系符合各⾃的稳定性。

# 7.5.2. 在软件体系结构设计的物理包设计中:

  1. 消除重复可以避免重复耦合,同时可以避免同⼀个设计决策出现在多个地⽅—— 这意味着该决策没有被真正地隐藏 (这也是控制耦合【两个模块都知道了秘密】⽐数据耦合差的原因)。

# 7.5.3. 在软件体系结构设计的物理包设计中:

  1. 建⽴独⽴的安全包、通信包和数据库连接包,是为了封装各⾃的设计决策——安全处理、 ⽹络通信与数据库处理。

# 7.5.4. 在软件体系结构设计与详细设计中:

  1. 严格要求定义模块与类的接⼝,可以便利开发,更是为了实现信息隐藏。

# 7.5.5. 在详细设计中使用控制风格:

  1. 专⻔⽤控制器对象封装关于业务逻辑的设计决策, ⽽不是将其拆散分布到整个对象⽹络中去。

# 8. 内聚的例子

  1. 内聚程度从低到高如下

# 8.1. 偶然内聚

  1. 模块执行多个完全不相关的操作

# 8.2. 逻辑内聚

  1. 模块执行一系列相关操作,每个操作的调用由其他模块来决定

# 8.3. 时间内聚

  1. 模块执行一系列与时间有关的操作

# 8.4. 过程内聚

  1. 模块执行一些与步骤顺序有关的操作

# 8.5. 通信内聚

  1. 模块执行一系列与步骤有关的操作,并且这些操作在相同的数据上进行
  2. 和过程内聚的唯一不同:是否在同一块数据结构上操作

# 8.6. 功能内聚

  1. 模块只执行一个操作或达到一个单一目的

# 8.7. 信息内聚

  1. 模块进行许多操作,各个都有各自的入口点
  2. 每个操作的代码都相互独立
  3. 所有操作都在相同的数据结构上完成

# 9. 耦合的例子

  1. 耦合程度从低到高如下

# 9.1. 数据耦合

  1. 两个模块的所有参数是同类型的数据项

# 9.2. 印记耦合

  1. 共享一个数据结构,但是却只用了其中一部分
  • 下面传递d.month就行

  • 传递i.date.month就行

  • 其实只需要emailID即可

# 9.3. 控制耦合

  1. 一个模块给另一个模块传递控制信息

  1. 一个方法调用下面的方法,并且传递了控制信息,正确的应该是调用者直接自己根据command判断

# 9.4. 重复耦合

  1. 模块之间有同样(逻辑上同样)的重复代码

# 9.5. 公共耦合

  1. 模块之间共享全局的数据(全局变量,全局数据结构)

# 9.6. 内容耦合

  1. 一个模块直接修改或者依赖于另一个模块的内容
  • 直接修改了date部分的month的值

  • 直接修改了另一个模块对象的成员变量的值

# 10. KWIC的四种不同实现

  1. 复习本地的四种实现的Java代码

# 10.1. 主程序和子程序(MS)

  • 使用全局变量,减少过多的参数传递

# 10.2. 面向对象(OO)

  1. 中间有一些对象进行存储
  2. 对象写作

# 10.3. Pipe Filter

  1. 数据进来通过过滤器
  2. 通过管道串联过滤器
  3. 好处:
    1. 如果有一个过滤器无法使用了直接替换就行
    2. 增加是容易的
  4. 各自过滤器是独立工作的,不在意其他人怎么样
  5. 管道不一定是方法调用(独立进程),要不就和结构化没有区别
  6. 4个线程各自跑起来,用pipe连接起来

# 10.4. 基于事件

  1. 有一个事件机制,有订阅者和分发者
  2. GUI:点击Button之后,触发事件
  3. 框架帮你完成事件机制
  4. 明确什么监听什么
  5. 观察者模式