4.3 软件设计的概念和原理


  • 模块和模块化
    • 模块:软件结构的基础,是软件元素,是能够单独命名、独立完成一定功能的程序语句的集合。
      1. 接口:给出可由其他模块访问的对象 (常量,变量,数据类型、函数)
      2. 实现:接口的实现(模块功能的执行机制)(私有、公有、保护变量,过程描述,源程序代码
    • 模块化:指解决一个复杂问题时自顶向下逐层把软件系统划分成若干模块的过程。
      1. 50多年的历史
      2. 软件的单个属性,使得程序能够被理性的管理
    • 模块化思想:即把软件划分为多个可独立命名和访问的部件,每个部件称为一个模块,当把所有模块组装到一起时则获得满足问题需要的一个解。
    • 为什么要模块化?
      1. 模块化是为了使一个复杂的大型程序能被人的智力所管理;
      2. 如果一个大型程序仅由一个整体组成,它将很难被人所理解。使用多个模块。
    • 模块化的作用:
      1. 采用模块化原理可以使软件结构清晰,不仅容易设计也容易阅读和理解;
      2. 模块化使软件容易测试和调试,因而有助于提高软件的可靠性;
      3. 模块化能够提高软件的可修改性;
      4. 模块化也有助于软件开发工程的组织管理。
    • 模块划分的准则:
      1. 模块可分解性:分解为子问题,降低整个系统的复杂性。注意不能过度分解。
      2. 模块的组装性:设计的模块能够组装到系统中,并能够在其他系统中复用。
      3. 模块的可理解性:模块设计要易于构造和修改。
      4. 模块连续性:系统需求的微小变化只是导致单个模块、而不是整个系统的都要修改。把修改引起的副作用最小化。
      5. 模块保护:模块内部出现异常情况,这种影响只是局限于模块内部,对外部的影响要尽可能小。
  • 抽象
    • 抽象是人类认识自然界中的复杂事物和复杂现象过程中使用的一种思维工具。
    • 抽象:现实世界中一定事物、状态或过程之间总存在某些相似的或者共性的方面,把这些相似的方面集中或概括起来,暂时忽略它们之间的次要因素,这就是抽象。
    • 在模块化问题求解时,可以在不同层次上进行抽象 。
    • 软件工程的抽象过程:
      1. 软件工程过程的每一步都是对软件解法的抽象层次的一次精化。
      2. 在可行性研究阶段,软件作为系统的一个完整部件。
      3. 在需求分析期间,软件解法是使用在问题环境内熟悉的方式描述的;
      4. 当由总体设计向详细设计过渡时,抽象的程度也随之减少了;
      5. 最后,当源程序写出来以后,也就达到了抽象的最底层。
    • 抽象与逐步求精
      1. “逐步求精”是与“抽象”密切相关的一个概念。是一种自顶向下设计的策略。
      2. “逐步求精” 主要思想是,针对某个功能的宏观描述用逐步求精的方法不断地分解,逐步确立过程细节,直至该功能用程序语言描述的算法实现为止。求精的每一步都是用更为详细的描述代替上一层次的抽象描述。
      3. 在软件设计过程中,抽象与逐步求精一般结合起来进行应用。在建立较高层次的抽象模型后,对其进行求精得到更加具体的抽象模型,然后再进行精化,由此一直到达最终的软件实现。
      4. 系统的层次结构的上一层是下一层的抽象,下一层是上一层的求精。
  • 信息隐蔽和局部化
    • 信息隐蔽:应该这样设计和确定模块,使得一个模块内包含的信息(过程和数据)对于不需要这些信息的模块来说,是不能访问的。
    • 局部化:和信息隐蔽的概念密切相关,它是指把一些关系密切的软件元素物理地放的彼此靠近。显然,局部化有助于实现信息隐藏。
    • 信息隐藏优点
      1. 开发活动更加简单:开发人员只需要专注于本模块的开发,需要的数据可以由其他模块得到,处理好的数据也可以通过接口传给其他模块。每个模块的开发人员所要处理的复杂性显著降低。
      2. 支持模块的并行开发:开发人员只需事先约定好接口设计(接口类型、参数、数据传输方向等),就可以按照设计独立开发自己负责的接口。
      3. 减少测试和维护的工作量:因为模块之间信息是隐藏的,可以对每个模块单独测试、维护,测试发现只需在本模块修改
  • 模块独立性及其度量
    • 模块独立的重要性:
      1. 模块的独立性要求:每个模块完成一个相对独立的特定子功能,并且和其他模块之间的关系很简单。
      2. 有效模块化的软件比较容易开发出来。当许多人分工合作开发同一个软件时,这个优点尤其重要。
      3. 独立的模块比较容易测试和维护。这是因为相对说来,修改设计和程序需要的工作量比较小,错误传播范围小,需要扩充功能时能够“插入”模块。
    • 系统模块化设计,尽量使每个模块应相对独立,其功能相对单一,而模块之间的接口应尽可能简单。
    • 模块的独立性可以从两个方面来度量:
      1. 耦合衡量不同模块彼此间相互依赖的紧密程度。耦合要低,即每个模块和其他模块之间的关系要简单。
      2. 内聚衡量一个模块内部各个元素之间彼此结合的紧密程度的度量。 内聚要高,每个模块完成一个相对独立的特定子功能。
      3. 追求高内聚低耦合
    • 耦合
      1. ![[Pasted image 20230618150333.png]]
      2. 耦合的强度所依赖的因素:
        • 一个模块对另一模块的引用
        • 一个模块向另一个模块传递的数据量
        • 一个模块施加到另一个模块的控制的数量
        • 模块之间接口的复杂程度
      3. 耦合等级的划分(由弱到强)
        • 低等级耦合:非直接耦合,两模块中任一个都不依赖对方能独立工作;
        • 中等级耦合
          1. 数据耦合,两个模块间通过参数交换信息,而且信息仅限于数据;
          2. 标记耦合,两个模块之间传递的是数据结构,而且被调用模块不需要作为参数传递过来的整个数据结构,只需要部署结构其中一部分数据元素;
          3. 控制耦合,一个模块向另一模块传递一个控制信号,接受控制信号的模块将依据该信号至进行必要的活动;
          4. 公共耦合,若干模块通过全局的数据环境相互作用;
        • 高级耦合:内容耦合,一个模块使用另一个模块内部的数据或控制信息 ;一个模块直接转移到另一个模块内部等
      • 尽量使用数据耦合,减少控制耦合,限制公共耦合,杜绝内容耦合。
      • 耦合程度的度量
        1. 无直接耦合
          • 如果两个模块中的每一个都能独立地工作而不需要另一个模块的存在,那么它们完全独立。
          • 在一个软件系统中不可能所有模块之间都没有任何连接。
        2. 数据耦合
          • 如果两个模块彼此间通过参数交换信息,而且交换的信息仅仅是数据,那么这种耦合称为数据耦合。
          • 评价:
            1. 系统中至少必须存在这种耦合。一般说来,一个系统内可以只包含数据耦合。
            2. 数据耦合是理想的目标。
            3. 维护更容易,对一个模块的修改不会使另一个模块产生退化错误。
        3. 标记耦合
          • 当把整个数据结构作为参数传递,而被调用的模块只需要使用其中一部分元素时,就出现了特征耦合(标记)。
          • 评价:
            1. 被调用的模块可使用的数据多于它确实需要的数据,这将导致对数据的访问失去控制。
            2. 无论何时把指针作为参数进行传递,都应该仔细检查该耦合。
        4. 控制耦合
        5. 公共环境耦合
          • 当两个或多个模块通过一个公共数据环境相互作用时,它们之间的耦合称为公共环境耦合。公共环境可以是全程变量、共享的通信区、内存的公共覆盖区 、任何存储介质上的文件、物理设备等。
          • 公共环境耦合的类型:
            1. 一个模块往公共环境送数据,另一个模块从公共环境取数据。数据耦合的一种形式,是比较松散的耦合。
            2. 两个模块都既往公共环境送数据又从里面取数据,这种耦合比较紧密,介于数据耦合和控制耦合之间。
            3. ![[Pasted image 20230618152544.png]]
        6. 内容耦合
          • 最高程度的耦合是内容耦合。如果出现下列情况之一,两个模块之间就发生了内容耦合:
          • 一个模块访问另一个模块的内部数据;
          • 一个模块不通过正常入口转到另一个模块的内部
          • 两个模块有一部分程序代码重叠;
          • 一个模块有多个入口
    • 内聚
      1. 内聚:标志一个模块内各个元素彼此结合的紧密程度,它 是信息隐藏和局部化概念的自然扩展。简单地说,理想内 聚的模块只做一件事。
      2. 要求
        • 设计时应该力求做到高内聚,通常中等程度的内聚也可以采用 的,而且效果和高内聚相差不多;但是,低内聚不要使用。
        • 内聚和耦合是密切相关的,模块内的高内聚往往意味着模块间 的松耦合。实践表明内聚更重要,应该把更多注意力集中到提 高模块的内聚程度上。
      3. 好的设计满足:
        • 模块的功能单一
        • 模块的各部分都和模块的功能直接相关
        • 高内聚
      4. 内聚划分为不同等级,内聚度越高越好。(由低到高)
        • 低级内聚:
          1. 偶然内聚:模块内各成分之间没有关系,即使有关系,也很松散
          2. 逻辑内聚:模块完成的任务在逻辑上相关。
          3. 时间内聚:一个模块包含的多个任务必须在同一时间内执行。而这 些功能只是因为时间因素关联在一起
        • 中等级内聚:
          1. 过程内聚:处理成分必须以特定的次序执行
          2. 通信内聚:模块中各成分都将对数据结构的同一区域进行操作
        • 高级内聚:
          1. 顺序内聚:各成分均与同一功能相关,且一个成分的输出作为另一 个成分的输入。
          2. 功能内聚:所有成分形成一个整体,完成单个功能。而且这些成分 对完成该功能而言是充分必要的。最高等级的内聚
        • ![[Pasted image 20230618170205.png]]
        • 内聚
          1. 偶然内聚
            • 如果一个模块完成一组任务,这些任务彼此间即 使有关系,关系也是很松散的,就叫偶然内聚
            • 评价:
              1. 模块内各元素之间没有实质性联系,很可能在一 种应用场合需要修改这个模块,在另一种应用场合又 不允许这种修改,从而陷入困境
              2. 可理解性差,可维护性产生退化。
              3. 模块是不可重用的
            • 解决方案.:将模块分成更小的模块,每个小模块执行一个操作
          2. 逻辑内聚
            • 如果一个模块完成的任务在逻辑上属于相同或类 似的一类,则称为逻辑内聚
            • 评级:
              1. 接口难以理解,造成整体上不易理解。
              2. 完成多个操作的代码互相纠缠在一起,即使局部 功能的修改有时也会影响全局,导致严重的维护问题
              3. 难以重用
            • 解决方案:模块分解
          3. 时间内聚
            • 如果一个模块包含的任务必须在同一段时间内执 行,就叫时间内聚,如初始化模块。
            • 评价:
              1. 时间关系在一定程度上反映了程序某些实质,所 以时间内聚比逻辑内聚好一些。
              2. 模块内操作之间的关系很弱,与其他模块的操作 却有很强的关联
              3. 时间内聚的模块不太可能重用
          4. 通信内聚
            • 如果模块中所有元素都使用同一个输入数据和 (或)产生同一个输出数据,则称为通信内聚,即在 同一个数据结构上操作
            • 评价:
              1. 模块中各操作紧密相连,比过程内聚更好
              2. 不能重用。
            • 解决方案:将模块分成多个模块,每个模块执行一个操作。
          5. 顺序内聚
            • 如果一个模块内的处理元素和同一个功能密切相 关,而且这些处理必须顺序执行,则称为顺序内聚。
            • 评价:
              1. 根据数据流图划分模块时,通常得到顺序内聚的 模块,这种模块彼此间的连接往往比较简单
          6. 功能内聚
            • 如果模块内所有处理元素属于一个整体,完成一 个单一的功能,则称为功能内聚。功能内聚是最高程 度的内聚。
            • 评价:
              1. 模块可重用,应尽可能重用。
              2. 可隔离错误,维护更容易。
              3. 扩充产品功能时更容易
      5. 结论
        1. 在设计软件时,尽可能做到高内聚。
        2. 偶然内聚、逻辑内聚和时间内聚属于低内聚, 通信内聚属于中内聚,顺序内聚和功能内聚属于高内聚

  目录