结构化系统设计——解决软件“做什么”问题
在上一篇文章中我们了解到,结构化系统分析得到系统的逻辑模型,解决软件“做什么”的问题。而在我们接下来的这篇文章中,将讲解结构化系统设计,那么结构化系统设计可以做什么呢?结构化系统设计是为了得到目标系统的物理模型,解决软件“怎么做”的问题。先用一张图来了解从逻辑模型到物理模型的过渡。
了解完之后,我们来开始讲解结构化系统设计。
一、🖌️设计的目标和任务
1、目标
得到目标系统的物理模型 —— 解决软件”怎么做“问题。📑
2、任务
结构化系统设计中的任务,指使用信息域来表示的软件需求、功能和性能需求,进行:
- 数据设计——侧重于数据结构的定义。
- 系统结构设计——定义软件系统各主要成份之间的关系。
- 接口设计——描述了软件内部、软件和协作系统之间以及软件和人之间如何通信。
- 过程设计——把结构成份地转换成软件的过程性描述,且在编码阶段,根据这种过程性描述,生成源程序代码,然后通过测试最终得到完整有效的软件。
了解完软件设计的目标和任务,相信大家对其有了一个基本的了解。我们再来看看这四个任务在开发阶段的信息流向。
3、开发阶段的信息流
从上图可知,从设计到编码阶段,软件要经过数据设计、系统结构设计、接口设计和过程设计。
4、软件设计的重要性
所以,软件设计是后续开发步骤和软件维护工作的基础。如果一个软件没有设计,那么会整个系统结构将会是非常不稳定的。
大家可以看到,左图是一个有经过软件设计的系统,看起来条理相对清晰,系统相对稳固一点。而右图是一个没有经过软件设计的系统,像冰淇淋似的摇摇晃晃的,很是不稳固。所以,对于一个软件来说,软件设计是非常重要的。
5、软件设计的技术观点和管理观点
从上图中可以看到,在一个软件中,从管理层面来看,管理层的人一般负责概要设计和详细设计。而从技术层面来看,技术人员主要负责数据设计、系统结构设计、接口设计和过程设计。
二、📐设计基础
1、结构图(体系结构图、模块结构图)
结构图反映模块之间的层次调用关系和联系:它以特定的符号表示模块、模块间的调用关系和模块间信息的传递。
(1)分析结构图三者间的关系
1)模块: 模块用矩形框表示,并用模块的名字标记它。
如图所示:
2)模块间的调用关系: 模块之间用单向箭头连接,箭头从调用模块指向被调用模块。
如图所示:
3)模块间信息的传递: 当一个模块调用另一个模块时,调用模块把数据或控制信息传送给被调用模块,以使得被调用模块能够运行。而被调用模块在执行过程中又把它产生的数据或控制信息回送给调用模块。
如图所示:
下图所表示的是,在模块A的箭头尾部有一个菱形符号,表示 模块 A
有条件地调用另一个 模块 B
。如果一个箭头尾部以一个弧形符号表示,表示反复调用。如下图所示, 模块A
反复调用 模块C
和 模块D
。
(2)结构图图例
结合模块、模块间的调用信息、模块间信息的传递三者,生成一张完成的结构图。如下图所示:
2、模块化(思想)
(1)模块化是什么: 指把一个大的系统划分成若干个小模块(东西),即被称为模块化。
(2)模块化想要达到的目的: 使复杂问题简单化处理。
3、抽象化(“开门”)
(1)抽象化是什么
- 软件系统进行模块设计时,可以有不同的抽象层次。
- 在最高的抽象层次上,可以使用问题所处环境的语言概括地描述问题的解法。
- 在较低的抽象层次上,则采用过程化的方法。
(2)抽象化的类型
1)过程抽象(开)
从系统定义到实现,每进展一步都可以看做是对软件解决方法的抽象化过程的一次细化。
比如:
- 在软件需求分析阶段,用“问题所处环境中大家所熟悉的术语”来描述软件的解决方法。
- 在从概要设计到详细设计的过程中,抽象化的层次逐次降低。当产生源程序时到达最低抽象层次。
2)数据抽象(门)
在不同层次上描述数据对象的细节,定义与该数据对象相关的操作。
(3)抽象化举例
1)过程抽象
Question: 开发一个 CAD
软件的三层抽象。
Answer:
抽象层次Ⅰ: 用问题所处环境的术语来描述这个软件:
该软件包括一个计算机绘图界面,向绘图员显示图形,以及一个数字化仪界面,用以代替绘图板和丁字尺。所有直线、折线、矩形、圆及曲线的描画、所有的几何计算、所有的剖面图和辅助视图都可以用这个 CAD
软件实现……
抽象层次Ⅱ: 任务需求的描述。
CAD SOFTWARE TASKS user interaction task; 2-D drawing creation task; graphics display task; drawing file management task; end. 复制代码
在这个抽象层次上,未给出“怎样做”的信息,不能直接实现。
抽象层次Ⅲ: 程序过程表示。
以 2-D
(二维)绘图生成任务为例:
PROCEDURE:2-D drawing creation REPEAT UNTIL (drawing creation task terminates) DO WHILE (digitizer interaction occurs) digitizer interface task; DETERMINE drawing request CASE; line:line drawing task; rectangle:rectangle drawing task; circle:circle drawing task; …… 复制代码
2)数据抽象
Question: 在 CAD
软件中,定义一个叫做 drawing
的数据对象。
Answer:
可将 drawing
规定为一个抽象数据类型,定义它的内部细节为:
TYPE drawing IS STRUCTURE DEFIND number IS STRING LENGTH(12); geometry DEFIND …… notes IS STRING LENGTH(256); BOM DEFIND END drawing TYPE; 复制代码
数据抽象drawing本身由另外一些数据抽象,如 geometry
、 BOM (bill of materials)
构成。
定义drawing的抽象数据类型之后,可引用它来定义其它数据对象,而不必涉及 drawing
的内部细节。
例如,定义:blue-print IS INSTANCE OF drawing 或 schematic IS INSTANCE OF drawing。
4、信息隐藏
信息隐蔽,于1972年由 D.Parnas
提出,是最早的软件开发方法。信息隐蔽指的是每个模块的实现细节对于其它模块来说是隐蔽的。也就是说,模块中所包含的信息(包括数据和过程)不允许其它不需要这些信息的模块使用。
三、🔥模块的独立性
1、模块
(1)属性
“模块”,又称为“组件”,它一般具有如下三个基本属性:
- 功能: 描述该模块实现什么功能。
- 逻辑: 描述模块内部怎么做。
- 状态: 描述该模块使用时的环境和条件。
(2)特性
- 外部特性: 模块的模块名、参数表、参数表其中的输入参数和输出参数,以及对程序和整个系统所造成的影响。
- 内部特性: 完成其功能的程序代码和仅提供给该模块内部使用的数据,即程序和数据。
2、模块的独立性
(1)模块的独立性是什么
模块独立性,是指软件系统中每个模块只涉及软件要求的具体子功能,且和软件系统中其它模块相对应的接口是简单的。
例如: 若一个模块只具有单一的功能且与其它模块没有太多的联系,则称此模块具有模块独立性。
(2)两个准则
一般采用两个准则来度量模块的独立性。即模块间耦合和模块内聚。如以下两点:
- 耦合是模块之间的互相连接的紧密程度的度量。
- 内聚是模块内各功能部分之间的度量。
注意:独立性强的模块,说明这个模块高内聚、低耦合。
(3)模块独立性的14字经
14字经 {耦合:七字经——非数标控外公内,耦合度越高,独立性越低内聚:七字经——功信通过时逻巧,内聚度越低,独立性越低\begin{cases} 耦合:七字经 —— 非数标控外公内,耦合度越高,独立性越低 \\ 内聚:七字经 —— 功信通过时逻巧,内聚度越低,独立性越低 \\ \end{cases}{耦合:七字经——非数标控外公内,耦合度越高,独立性越低内聚:七字经——功信通过时逻巧,内聚度越低,独立性越低
下面围绕七种耦合类型和七种内聚类型进行讲解。
3、耦合的七种类型
模块间的耦合主要包含以下七种类型。
(1)非直接耦合(Non-direct Coupling)
两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。非直接耦合的模块独立性最强。
如下图所示:
(2)数据耦合
一个模块访问另一个模块时,彼此之间是通过简单数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的。
如下图所示:
(3)标记耦合
一组模块通过参数表来传递记录信息,就是标记耦合。这个记录是某一数据结构的子结构,而不是简单变量。
如下图所示:
(4)控制耦合
如果一个模块通过传送开关、标志、名字等控制信息,明显地控制和选择另一模块的功能,就是控制耦合。
如下图所示:
(5)外部耦合
一组模块都访问同一个全局简单变量而不是同一全局数据结构,同时不是通过参数表传递该全局变量的信息,则称之为外部耦合。
(6)公共耦合
若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等。
公共耦合的复杂程度随耦合模块的个数增加而显著增加。若只是两模块间有公共数据环境,则公共耦合有两种情况,分别是松散公共耦合和紧密公共耦合。
如下图所示:
(7)内容耦合
如果发生下列情形,即表示两个模块之间发生了内容耦合:
(1) 一个模块直接访问另一个模块的内部数据。
(2)一个模块不通过正常入口转到另一模块内部。
(3)两个模块有一部分程序代码重迭。
(4)一个模块有多个入口。
注: 满足以上其中一个条件即算内容耦合。
如下图所示:
了解完以上其模块间的耦合后,我们对以下四种耦合方式进行一个归纳总结。
模块间耦合形式 | 可读性 | 错误扩散能力 | 可修改性 | 通用性 |
数据耦合 | 好 | 弱 | 好 | 好 |
控制耦合 | 中 | 中 | 中 | 中 |
公共耦合 | 不好 | 强 | 不好 | 交差 |
内容耦合 | 最差 | 最强 | 最差 | 差 |
4、内聚的七种类型
模块内各功能部分之间的内聚主要包含以下七种类型。
(1)功能内聚
一个模块中的各个部分都是完成某一项具体功能时必不可少的组成部分,或者说该模块中所有部分都是为了完成一项具体功能而协同工作,紧密联系,不可分割的,则称该模块为功能内聚模块。
(2)信息内聚
这种模块完成多个功能,各个功能都在同一个数据结构上操作,每一项功能有一个唯一的入口点。这个模块将根据不同的要求,确定该执行哪一个功能。由于这个模块的所有功能都是基于同一个数据结构(符号表),因此,它是一个信息内聚的模块。
如下图所示:
信息内聚模块可以看成是多个功能内聚模块的组合,并且达到信息的隐蔽。即把某个数据结构、资源或设备隐蔽在一个模块内,不被别的模块所知晓。
(3)通信内聚
如果一个模块内各功能部分都使用了相同的输入数据,或产生了相同的输出数据,则称之为通信内聚模块。通常,通信内聚模块是通过数据流图来定义的。
如下图所示:
(4)过程内聚
使用流程图作为工具设计程序时,把流程图中的某一部分划分出组成模块,就得到了过程内聚模块。例如,把流程图中的循环部分、判定部分、计算部分分成三个模块,这三个模块都是过程内聚模块。所有的处理元素必须按按照指定顺序来执行。
如下图所示:
(5)时间内聚
时间内聚又称为经典内聚。这种模块大多数为多功能模块,但模块中各个功能的执行与时间有关,通常要求所有功能必须在同一时间段内执行。例如初始化模块和终止模块。
(6)逻辑内聚
这种模块把几种相关的功能组合在一起,每次被调用时,由传送给模块的判定参数来确定该模块应执行哪一种功能。
如下图所示:
(7)巧合内聚
巧合内聚,也称为偶然内聚。指当模块内各部分之间没有联系,或者即使有联系,这种联系也很松散,则称这种模块为巧合内聚模块,它是内聚程度最低的模块。
了解完以上模块内各功能部分之间的内聚后,我们对这七种内聚方式进行一个归纳总结。
内聚形式 | 连接形式 | 可修改性 | 可读性 | 通用性 | 联系程度 |
功能内聚 | 好 | 好 | 好 | 好 | 高 |
信息内聚 | 好 | 好 | 好 | 中 | ↑ |
通信内聚 | 中 | 中 | 中 | 不好 | ↑ |
过程内聚 | 中 | 中 | 中 | 不好 | ↑ |
时间内聚 | 不好 | 不好 | 中 | 最坏 | ↑ |
逻辑内聚 | 最坏 | 最坏 | 不好 | 最坏 | ↑ |
偶然内聚 | 最坏 | 最坏 | 最坏 | 最坏 | 低 |
四、🏸软件的体系结构设计
1、体系结构风格
(1)以数据为中心的体系结构
以数据为中心的体系结构风格是一种共享数据源的信息传递方式,例如:典型应用、搜索引擎。
(2)数据流体系结构
1)构造子:filter
;
2)连接子: pipe
;
3)特点:
- 设计者能够理解整个系统对输入输出的影响,输入输出是过滤器的组成部分。
- 任意两个过滤器可以连接在一起,它们可以很容易地应用于其它系统。
- 系统发展演变很容易进行,因为新的过滤器可以很容易地加进来,而旧的过滤器也可以很容易地删减下去。
- 由于过滤器的独立性,设计者可以模拟系统行为和分析系统特性,比如吞吐量。
- 允许过滤器的并行执行。
4)缺点:
- 使用批量处理,不适合处理交互的应用程序。
- 当两个数据流是相关的时候,系统必须维护它们之间的对应关系。
- 过滤器的独立性意味着一些过滤器必须要复制一些由其他过滤器完成的准备功能,这样会影响性能,并使代码变得十分复杂。
5)图例
(3)调用和返回体系结构
1)特点:
- 设计模型是事件驱动的,基于广播的概念。它不同于直接调用一个过程,而是由一个组件宣布一个或多个事件要发生了。然后,其他组件将一个过程与这些事件联系起来(称为程序注册),再由系统调用这些注册程序。
- 数据交换是通过储存库中的共享数据完成的。
2)适用范围:
- 经常用于分组交换网络中或是基于活动者的系统中。
- 用于保持数据库中的一致性。
- 用于用户界面中以便分开数据和管理数据的应用程序。
3)优点:
- 对复用其它系统的组件特别有用。
4)缺点:
- 不能保证某个组件会响应一个事件。
- 测试和检查正确性很困难。
(4)面向对象体系结构
1)构造子:objects
、classes
;
2)连接子: methods
;
3)特点:
- 一个对象必须保持数据类型的完整性,还有就是数据表示对于其他对象是隐藏的。
- 与管道-过滤器系统不同的是,一个对象必须知道其他对象特征,以便于它们之间进行交互,而过滤器是完全独立的。对象间的这种依赖意味着一个对象的改变将影响到调用该对象的组件。
(5)层次式体系结构
1)构造子: 过程、集合;
2)连接子: 调用;
3)图例:
4)特点:
- 层次是分级的,每一层都为它外面的一层提供服务,而且对于它里面的一层来说是一个客户。
5)优点:
- 抽象概念——每一个层次都被认为是一个可扩展的抽象级,设计者可以用这些层次将一个问题分解成一系列更为抽象的步骤。
- 由于各层次间通讯的没有限制,当需求改变的时候,很容易增加或修改一个层次。通常,这样的改变只影响到相邻的两个层次。
- 重用一个层次也是非常简单的,只需要在相邻层次间做一些改变。
2、衡量体系结构复杂度
Card
和 Glass
系统复杂度的度量方法:
- C=S+DC=S+DC=S+D
- S=1n∑f2(i)S=\frac{1}{n}∑ f^2(i)S=n1∑f2(i)
- D=V(i)[f(i)+1]D = \frac {V(i)}{[f(i)+1]}D=[f(i)+1]V(i)
其中, S
是组件间的结构复杂度, D
是组件内部的数据复杂度, f(i)
是组件 i
的扇出, V(i)
是组件 i
的输入输出变量个数, n
是组件个数。