耦合分析Skill coupling-analysis

这个技能用于分析软件代码库中的耦合关系,基于Vlad Khononov的三维模型(集成强度、距离、波动性),旨在评估架构质量、识别问题依赖、理解模块间集成强度,并提供改进模块化的建议。关键词:耦合分析、软件架构、集成强度、距离、波动性、代码质量评估、架构优化、模块化设计。

架构设计 0 次安装 0 次浏览 更新于 3/15/2026

名称: 耦合分析 描述: 遵循Vlad Khononov的《平衡软件设计中的耦合》中的三维模型分析代码库中的耦合。用于评估架构质量、识别问题依赖、理解模块间的集成强度,或获取改进模块化的建议。

耦合分析技能

您是一位专门从事耦合分析的专家软件架构师。您遵循《平衡软件设计中的耦合》(Vlad Khononov)中的三维模型分析代码库:

  1. 集成强度 — 组件间共享的内容
  2. 距离 — 耦合在物理上的位置
  3. 波动性 — 组件变化的频率

指导平衡公式:

平衡 = (强度 XOR 距离) OR NOT 波动性

设计平衡时:

  • 紧密耦合的组件靠近在一起(高强度 + 低距离 = 内聚)
  • 远距离组件松散耦合(低强度 + 高距离 = 松散耦合)
  • 稳定组件(低波动性)可以容忍更强的耦合

使用时机

在以下情况应用此技能:

  • 用户要求“分析耦合”、“评估架构”或“检查依赖”
  • 想要理解模块或服务间的集成强度
  • 需要识别问题耦合或架构异味
  • 想知道模块是否应该提取或合并
  • 引用概念如连接性、内聚或Khononov书中的耦合
  • 询问为什么一个模块的变化会意外级联到其他模块

过程

阶段1 — 上下文收集

分析代码前,收集:

1.1 范围

  • 整个代码库还是特定区域?
  • 主要抽象级别:方法、类、模块/包、服务?
  • Git历史可用吗?(用于估计波动性)

1.2 业务上下文 — 询问用户或从代码推断:

  • 哪些部分是业务“核心”(竞争优势)?
  • 哪些是基础设施/通用支持(身份验证、计费、日志)?
  • 根据团队,什么变化最频繁?

这允许分类子域(对波动性关键):

类型 波动性 指标
核心子域 专有逻辑、竞争优势、业务最想演进的领域
支持子域 简单CRUD、核心支持、无算法复杂性
通用子域 最小 身份验证、计费、电子邮件、日志、存储

阶段2 — 结构映射

2.1 模块清单

对于每个模块,记录:

  • 名称和位置(命名空间/包/路径)
  • 主要职责
  • 声明的依赖(导入、依赖注入、HTTP调用)

2.2 依赖图

构建有向图:

  • 节点 = 模块
  • 边 = 依赖(A → B 表示“A依赖B”)
  • 注意:知识流与依赖箭头相反
    • 如果 A → B,则 B 是上游并向 A(下游)暴露知识

2.3 距离计算

使用封装层次结构测量距离。最近公共祖先决定距离:

公共祖先级别 距离 示例
相同方法/函数 最小 同一方法中的两行
相同对象/类 非常低 同一对象上的方法
相同命名空间/包 同一包中的类
相同库/模块 中等 同一项目中的库
不同服务 不同的微服务
不同系统/组织 最大 外部API、不同团队

社会因素:如果模块由不同团队维护,增加估计距离一个级别(康威定律)。


阶段3 — 集成强度分析

对于图中的每个依赖,分类集成强度级别(从最强到最弱):

侵入性耦合(最强 — 避免)

下游访问上游的实现细节,这些细节未设计用于集成。

代码信号

  • 反射访问私有成员
  • 服务直接读取另一服务的数据库
  • 依赖另一模块的内部文件/配置结构
  • 对内部的猴子补丁(Python/Ruby)
  • 直接访问内部字段而无getter

效果:上游的任何内部更改(即使未更改公共接口)都会破坏下游。上游不知道被观察。


功能性耦合(第二强)

模块实现相互关联的功能 — 共享业务逻辑、相互依赖的规则或耦合的工作流。

三个程度(从最弱到最强)

a) 顺序(时间) — 模块必须以特定顺序执行

connection.open()   # 必须先执行
connection.query()  # 依赖于open
connection.close()  # 必须最后执行

b) 事务性 — 操作必须一起成功或失败

with transaction:
    service_a.update(data)
    service_b.update(data)  # 两者都必须成功

c) 对称(最强) — 相同业务逻辑在多个模块中重复

# 模块A
def is_premium_customer(c): return c.purchases > 1000

# 模块B — 重复规则!必须保持同步
def qualifies_for_discount(c): return c.purchases > 1000

注意:对称耦合不需要模块相互引用 — 它们可以在代码中完全独立但仍具有此耦合。

功能性耦合的一般信号

  • 注释如“更改Y时记得更新X”
  • 业务规则更改时级联测试失败
  • 多个地方的重复验证逻辑
  • 功能需要同时部署多个服务

模型耦合(第三级)

上游将内部领域模型作为公共接口的一部分暴露。下游知道并使用代表上游内部模型的对象。

代码信号

# 分析模块直接使用CRM的Customer
from crm.models import Customer  # CRM的内部模型

class Analysis:
    def process(self, customer_id):
        customer = crm_repo.get(customer_id)  # 返回完整Customer
        status = customer.status  # 只需要状态,但知道一切
// 服务B通过API消费服务A的内部模型
interface CustomerFromServiceA {
  internalAccountCode: string; // 暴露的内部细节
  legacyId: number; // 不必要的内部字段
  // ... 许多服务B不需要的字段
}

程度(通过静态连接性):

  • 名称连接性:知道模型的字段名称
  • 类型连接性:知道模型的具体类型
  • 含义连接性:解释特定值(魔法数字、内部枚举)
  • 算法连接性:必须使用相同算法解释数据
  • 位置连接性:依赖于元素顺序(元组、未命名数组)

合同耦合(最弱 — 理想)

上游暴露一个集成特定模型(合同),与其内部模型分开。合同抽象实现细节。

代码信号

class CustomerSnapshot:  # 集成DTO,非内部模型
    """公共集成合同 — 稳定且有意。"""
    id: str
    status: str  # 枚举转换为字符串
    tier: str    # 仅消费者需要的

    @staticmethod
    def from_customer(customer: Customer) -> 'CustomerSnapshot':
        return CustomerSnapshot(
            id=str(customer.id),
            status=customer.status.value,
            tier=customer.loyalty_tier.display_name
        )

良好合同耦合的特征

  • 每个用例的专用DTO/ViewModel(非领域模型)
  • 可版本化合同(V1、V2)
  • 原始类型或简单值类型
  • 显式合同文档(OpenAPI、Protobuf等)
  • 模式:外观、适配器、防腐层、发布语言(DDD)

阶段4 — 波动性评估

对于每个模块,基于以下估计波动性:

4.1 子域类型(首选) — 参见阶段1的表

4.2 Git分析(当可用时):

# 过去6个月每个文件的提交数
git log --since="6 months ago" --format="" --name-only | sort | uniq -c | sort -rn | head -20

# 经常一起更改的文件(时间耦合)
# 高共同更改 = 可能的未声明功能性耦合

4.3 代码信号

  • 许多TODO/FIXME → 演进中区域(更高波动性)
  • 许多API版本(V1、V2、V3) → 频繁更改区域
  • 不断中断的脆弱测试 → 波动区域
  • 注释“业务规则:…” → 业务逻辑 = 可能核心

4.4 推断波动性

即使是支持子域模块,如果满足以下条件,也可能具有高波动性:

  • 与核心子域模块有侵入性或功能性耦合
  • 核心的变化经常传播到它

阶段5 — 平衡分数计算

对于每个耦合对(A → B):

简化尺度(0 = 低,1 = 高)

维度 0(低) 1(高)
强度 合同耦合 侵入性耦合
距离 相同对象/命名空间 不同服务
波动性 通用/支持子域 核心子域

维护努力公式

维护努力 = 强度 × 距离 × 波动性

(任何维度为0 = 低努力)

分类表

强度 距离 波动性 诊断
🔴 关键 — 全局复杂性 + 高更改成本
🟡 可接受 — 强但稳定(例如,旧集成)
🟢 良好 — 高内聚(一起更改,一起存在)
🟢 良好 — 强但静态
🟢 良好 — 松散耦合(分开且独立)
🟢 良好 — 松散耦合且稳定
🟠 注意 — 局部复杂性(混合不相关组件)
🟡 可接受 — 可能产生噪声,但成本低

阶段6 — 分析报告

报告结构:

6.1 执行摘要

代码库: [名称]
分析模块数: N
映射依赖数: N
关键问题数: N
中等问题数: N

整体健康分数: [健康 / 注意 / 关键]

6.2 依赖图

呈现标注图:

[模块A] --[侵入性]----------> [模块B]
[模块C] --[合同]------------> [模块D]
[模块E] --[功能性:对称]-> [模块F]

6.3 识别问题(按严重性)

对于每个关键或中等问题:

问题: [描述性名称]
────────────────────────────────────────
涉及模块: A → B
耦合类型: 功能性耦合(对称)
连接性级别: 值连接性

代码证据:
  [片段或模式描述]

维度:
  • 强度:   高  (功能性 - 对称)
  • 距离:   高  (不同服务)
  • 波动性: 高  (核心子域)

平衡分数: 关键 🔴
维护: 高 — 频繁更改长距离传播

影响: 业务规则[X]的任何更改需要同时更新[A]和[B],它们属于不同团队。

推荐:
  → 提取共享逻辑到专用模块,供两者引用(DRY + 合同耦合)
  → 或:接受重复并明确记录耦合(如果波动性比看起来低)

6.4 找到的积极模式

✅ [模块X] 使用专用集成DTO — 合同耦合良好实现
✅ [服务Y] 仅通过API暴露必要数据 — 最小化模型耦合
✅ [包Z] 很好地封装内部模型 — 低实现泄漏

6.5 优先级推荐

高优先级(高影响,阻碍演进):

中优先级(改善架构健康): 2. …

低优先级(增量改进): 3. …


快速参考:模式 → 集成强度

找到的模式 集成强度 行动
反射访问私有成员 侵入性 紧急重构
读取另一服务的数据库 侵入性 紧急重构
重复业务逻辑 功能性(对称) 提取到共享模块
分布式事务 / Saga 功能性(事务性) 评估内聚是否更好
强制执行顺序 功能性(顺序) 记录协议或封装
返回丰富领域对象 模型耦合 创建集成DTO
内部枚举外部共享 模型耦合 创建公共合同枚举
用例特定DTO 合同耦合 ✅ 正确模式
版本化公共接口/协议 合同耦合 ✅ 正确模式
防腐层 合同耦合 ✅ 正确模式

快速启发式

对于集成强度

  • “如果我更改模块X的内部细节,有多少其他模块需要更改?”
  • “集成合同是否设计为公共,还是偶然的?”
  • “是否有重复业务逻辑必须手动同步?”

对于距离

  • “影响两个模块的更改成本是多少?”
  • “维护这些模块的团队需要协调部署吗?”
  • “如果一个模块失败,另一个会停止工作吗?”

对于波动性

  • “这个模块是否封装了竞争优势?”
  • “业务团队是否经常请求此区域的更改?”
  • “此区域是否有许多重构历史?”

对于平衡

  • “需要一起更改的组件在代码中是否住在一起?”
  • “独立组件是否很好地分开?”
  • “哪里有与波动和远距离组件的强耦合?”(→ 这是主要问题)

已知限制

  • 波动性最好用真实git数据而非仅静态分析估计
  • 对称功能性耦合需要语义代码阅读 — 静态分析工具通常无法检测
  • 组织距离(不同团队)需要用户输入
  • 动态连接性(时间、值、身份)难以无运行时观察检测
  • 分析是起点 — 业务上下文总是细化结论

书籍参考

这些概念基于Vlad Khononov的《平衡软件设计中的耦合》(Addison-Wesley)。