ADD 属性驱动设计方法 — 软件架构课程 Lecture-12

来源:周立新《软件体系结构》课程 Lecture-12 原文:Attribute-Driven Design Method.pdf,49页,文字版 PDF 关联:质量属性与构架商业周期-软件架构课程Lecture-9(上一讲)

一、ADD 方法要解决的四大问题

  1. 质量属性需求的精确规约 — 如何把模糊的”要快、要稳”变成可度量、可验证的场景
  2. 架构决策的枚举 — 有哪些架构手段(tactics / patterns)可用来达成质量需求
  3. 质量需求与架构决策的关联 — 每一条质量场景对应哪些架构手段
  4. 架构决策的组合 — 多个决策如何拼合成一个完整设计

ADD = Attribute-Driven Design,属性驱动设计法。核心思想:用质量属性来驱动架构分解,而不是先分模块再考虑质量。

二、软件设计的概念与原理(基础回顾)

2.1 三个”适合”的质量观

  • 设计质量:软件设计规格说明 ↔ 用户要求 的适合程度
  • 程序质量:程序 ↔ 内部规格说明 的适合程度
  • 用户满意:最终质量 = 用户的满意程度

2.2 概要设计的主要任务

  • 确定系统的整体模块结构
  • 把功能需求分配给软件结构,形成模块结构图
  • 矩形 = 功能单元/模块,线段 = 调用关系

2.3 概要设计的过程

  1. 选取最佳方案(设想供选择 → 选取合理 → 推荐最佳)
  2. 功能分解
  3. 设计软件结构
  4. 数据库设计(E-R 图、概念模型与规范化)
  5. 制定测试计划
  6. 书写文档(系统说明、实现计划、用户手册、测试计划、数据库设计结果)
  7. 复查和复审

2.4 模块化

  • 分而治之:C(P1) > C(P2) → E(P1) > E(P2)
  • C(P1+P2) > C(P1) + C(P2)(复杂度可分解)
  • 但 E(P1+P2) > E(P1) + E(P2)(接口成本存在,不是分越多越好)
  • 模块数与成本的关系:模块数↑ → 单模块成本↓ + 接口成本↑ → 总成本有一个最小成本区

2.5 抽象、信息隐蔽、模块独立

  • 抽象:数据抽象 + 过程抽象
  • 信息隐蔽和局部化:模块内部的实现细节对外不可见
  • 模块独立:高内聚 + 低耦合

2.6 耦合(从低到高,前两个推荐)

耦合类型说明使用建议
数据耦合模块间通过参数传递基本类型数据推荐,最多用
控制耦合传递控制信号(如开关量)少用
公共环境耦合共享全局数据区限制范围
内容耦合一个模块访问另一模块内部数据 / 不通过正常入口转入 / 代码重叠 / 多入口坚决避免

2.7 内聚(从高到低,前三个推荐)

评分:(10, 9, 7, 5, 3, 1, 0)

  1. 功能内聚(10分):模块内所有元素完成一个单一功能
  2. 顺序内聚(9分):处理元素密切相关且必须顺序执行
  3. 通信内聚(7分):所有元素使用同一输入数据 / 产生同一输出数据
  4. 过程内聚(5分):处理元素相关且必须按特定次序执行
  5. 时间内聚(3分):任务必须在同一段时间内执行(如初始化)
  6. 逻辑内聚(1分):任务在逻辑上属于相同或相似的一类
  7. 偶然内聚(0分):任务关系松散,碰巧放一起

2.8 启发式设计规则(7 条)

  1. 改进软件结构,提高模块独立性
  2. 模块规模要适中
  3. 深度、宽度、扇入、扇出都应适当
  4. 模块的作用域应该在控制域之内
  5. 力争降低模块接口的复杂程度
  6. 设计单入口单出口的模块
  7. 模块功能应该可以预测

三、架构在生命周期中的位置

3.1 什么时候可以开始架构设计?

  • 架构由功能需求 + 质量需求 + 业务需求共同塑造 → 这些叫 架构驱动力(architecture drivers)
  • 识别最高优先级的业务目标(<10 个)
  • 通过 质量场景、用例、效用树(utility tree) 来明确驱动力
  • 一旦驱动力明确,架构设计就可以开始(不需要等所有需求都清楚)
  • 反向:架构设计中发现问题,也可以回推需求澄清

3.2 ADD 方法概述

ADD takes as input a set of quality attribute scenarios and employs knowledge about the relation between quality attribute achievement and architecture in order to design the architecture.

  • 输入:一组质量属性场景 + 功能需求 + 约束
  • 核心机制:把质量属性实现 ↔ 架构决策 的关联知识应用到设计中
  • 定位:可以作为 RUP 等大多数开发方法的扩展
  • 本质:一个递归分解过程,每一层:
    1. 满足一组质量场景 → 选择 tactics 和 patterns
    2. 把功能分配到 pattern 提供的模块类型中
  • 输出:架构的前几层模块分解视图 + 其他必要视图

四、ADD 方法详细步骤

总览:三大步

I.  选择要分解的模块
II. 按以下5小步细化该模块:
    2a. 选择架构驱动力
    2b. 选择架构模式
    2c. 实例化模块并分配功能,用多视图表示
    2d. 定义子模块的接口
    2e. 验证并细化用例和质量场景,作为子模块的约束
III. 对每个需要继续分解的模块重复以上步骤

4.1 第 I 步:选择要分解的模块

  • 从系统开始,然后是子系统,再是子模块
  • 车库门例子:从”整个车库门开启器系统”开始分解
  • 约束示例:必须与家庭信息系统(HIM)互操作

4.2 第 2a 步:选择架构驱动力

针对当前模块,确定最重要的质量属性驱动力。车库门例子的驱动力:

  1. 可修改性:支持产品线架构(设备与控制器、处理器都可变)
  2. 实时性能:障碍物检测必须在 0.1 秒内停止
  3. 可诊断性/可管理性:可通过家庭信息系统进行诊断和管理

4.3 第 2b 步:选择架构模式

  • 每个 tactic 设计用来实现一个或多个质量属性
  • 但 tactic 所在的 pattern 会对其他质量属性产生副作用(如解释器模式影响性能)
  • 选择 tactic 的两个主要因素:
    • 驱动力本身
    • 实现该 tactic 的 pattern 对其他质量的副作用

主要质量属性的 tactic 方向:

质量属性核心 tactic 方向
可修改性局部化变化、防止涟漪效应、推迟绑定时间;语义一致性、信息隐藏、为受影响区域定义虚拟机
性能资源需求管理、资源仲裁;提高计算效率、选择调度策略

车库门例子的架构模式(综合可修改性 + 性能 + 可诊断性):

┌─────────────────────────────────────────────────┐
│                   User Interface                 │  ← 非性能关键计算 + 用户交互
├─────────────────────────────────────────────────┤
│            Non-Performance-Critical              │  ← 门的常规升降、诊断功能
│             Computation Section                  │
├─────────────────────────────────────────────────┤
│                 Virtual Machine                  │  ← 抽象硬件差异(产品线可修改性)
│          (Communication / Sensor/Actuator)       │
├─────────────────────────────────────────────────┤
│            Performance-Critical                  │  ← 障碍物检测(0.1s 硬实时)
│             Computation Section                  │
├─────────────────────────────────────────────────┤
│        Scheduler That Guarantees Deadline        │  ← 保证截止期的调度器
└─────────────────────────────────────────────────┘

4.4 第 2c 步:实例化模块并分配功能,用多视图表示

实例化模块(车库门例子)

  • 性能关键区 → 障碍物检测与停止功能(因为有截止期)
  • 非性能关键区 → 门的常规升降 + 诊断功能
  • 虚拟机层 → 通信、传感器读取、执行器控制(封装硬件差异,支持产品线)

功能分配的要点

  • 把父模块的用例分配到子模块,验证功能完整性
  • 分配过程中可能发现需要新增或删除子模块
  • 发现必要的信息交换关系
  • 某些 tactic 会引入特定的模块交互模式

三种架构视图

视图作用
模块分解视图静态结构,模块职责划分
并发视图动态并行活动、同步、资源竞争、死锁、数据一致性;发现新职责(如资源管理器)
部署视图多处理器/专用硬件时的部署;决定哪些模块需要多实例(可靠性需求);不是随意推导的

并发视图要点:

  • 组件 = 模块的实例;连接器 = 虚拟线程的载体
  • 连接器类型:synchronize with、starts、cancel、communicates with
  • 用例驱动:两用户同时操作、一用户多活动、启动、关闭

4.5 第 2d 步:定义子模块的接口

  • 接口 = 模块提供和要求的服务与属性(不等于函数签名)
  • 从三视图中推导交互假设,记录为接口
视图接口含义
模块视图信息的生产者/消费者;服务提供/使用的交互模式
并发视图线程间交互 → 服务的提供/使用;组件是否主动(有自己的线程);同步、顺序化、阻塞调用
部署视图硬件要求(如专用硬件);时序要求(如计算速度 ≥10 MIPS);通信要求(如信息每秒更新不超过一次)

4.6 第 2e 步:验证并细化用例和质量场景,作为子模块的约束

三类需求分配给子模块:

  1. 功能需求 → 分配给具体子模块
  2. 约束 → 三种情况:
    • 组合满足约束(如定义操作系统为子模块)
    • 单个子模块满足(如封装协议的模块)
    • 多个子模块协作满足(如 Web 需要 client+server)
  3. 质量场景 → 四种情况:
    • 分解后已完全满足,无额外影响
    • 当前分解满足,但对子模块有约束
    • 分解对其是中性的(不影响)
    • 当前分解无法满足 → 需要权衡(tradeoff)

车库门例子的场景分配:

质量场景分配给
不同产品的设备与控制不同(可修改性)用户界面模块
不同产品使用不同处理器所有模块(通过虚拟机隔离)
0.1 秒内停止/重开(性能)调度器 + 障碍物检测模块
可通过家庭信息系统诊断管理诊断模块 + 通信模块(拆分)

4.7 每步分解结束时的产出

  • 模块分解为子模块,每个子模块有一组职责
  • 一组用例
  • 接口定义
  • 质量场景
  • 一组约束

五、ADD 的延伸影响

5.1 团队组织结构

  • 稳定的模块分解结构 → WBS(工作分解结构)
  • 团队之间低带宽通信就够了(这一点很关键,也验证了康威定律)
  • 按模块组建团队,按专长分配人员
  • 反过来,现有组织结构也会影响架构(因为组织有专门能力和既得利益)

5.2 骨架系统(Skeletal System)

  • 哪些部分需要打桩(stub)?
  • 演进交付生命周期(Evolutionary Delivery Life Cycle)
  • 里程碑 MS → “完整的”骨架系统 → “可工作的”低保真版本
  • 好处:早期验证架构,降低风险

六、ADD 方法总结

  1. 起点:关键架构驱动力确定后即可开始,不需要完整需求
  2. 过程:自顶向下的递归分解,用质量需求定义架构模式,用功能需求实例化模块类型
  3. 输出:前几层架构分解 + 多视图 + 接口 + 约束
  4. 组织影响:决定团队结构和通信路径;现有组织也影响架构
  5. 验证:每步分解都要验证质量场景和功能需求是否能满足

七、关联