并发安全检查器策略模式重构报告#
执行摘要#
本次重构针对并发安全静态检查器(check_concurrent_safety模块)的 ConcurrentSafetyVisitor 类进行策略模式拆分。原924行的巨型类承担8种不同维度的并发安全检查职责,严重违反单一职责原则。重构后拆分为1个协调器 + 8个独立检查器,显著提升可维护性、可测试性和扩展性。
重构验证结果#
验证项 |
结果 |
说明 |
|---|---|---|
单元测试 |
✅ 48/48 全部通过 |
覆盖所有8个检查维度 |
真实代码扫描 |
✅ 正常工作 |
|
自我扫描 |
✅ 全部通过 |
重构后的14个文件100分,0个问题 |
向后兼容 |
✅ 保持兼容 |
保留 |
CLI功能 |
✅ 正常 |
帮助、JSON输出、维度过滤全部正常 |
1. 问题#
并发安全静态检查器(.agents/scripts/lib/check_concurrent_safety/visitor.py)的 ConcurrentSafetyVisitor 类承担了8种不同维度的并发安全检查职责,严重违反单一职责原则,导致代码难以维护、测试和扩展。
1.1. 职责过重导致类膨胀#
ConcurrentSafetyVisitor 类约 924行,承担了以下8种检查职责:
TIMEOUT(超时检查):检测锁/等待操作是否设置超时
IDEMPOTENT(幂等检查):检测列表追加操作是否有去重保护
BOUNDARY(边界检查):检测热路径中的O(n)线性查找
DEFENSIVE(防御检查):检测可变默认参数和外部可变对象的防御性拷贝
CONFIG(配置检查):检测并发参数是否硬编码
I18N(国际化检查):检测业务逻辑中的中文字面量使用
DEADLOCK(死锁顺序检查):检测多锁获取顺序一致性
LEAK(资源泄漏检查):检测线程池/进程池是否正确关闭
每个检查维度都有独立的状态变量、检查逻辑和报告生成逻辑,但这些都被耦合在同一个类中。
1.2. 检查逻辑耦合导致难以维护#
类中包含 13个visit_*方法,每个方法都混合了多种检查逻辑。修改任何一个检查维度的逻辑,都需要理解整个类的状态变量和调用关系,维护成本极高。
1.3. 可测试性差#
由于所有检查逻辑耦合在一起,难以对单个检查维度进行独立的单元测试。当前测试需要构造复杂的AST节点和完整的状态环境,测试代码冗长且脆弱。
1.4. 扩展性差#
添加新的检查维度需要修改多个 visit_* 方法,违反了开闭原则。
2. 收益#
通过职责拆分和策略模式重构,显著提升代码的可维护性、可测试性和扩展性。
2.1. 降低维护成本#
将单个924行的庞大类拆分为 1个协调器 + 8个独立检查器,每个检查器平均约100-150行
修改某个检查维度时,只需关注对应的检查器类,不影响其他检查逻辑
预计维护成本降低约 40-50%
2.2. 提升可测试性#
每个检查器可以独立进行单元测试,无需构造完整的状态环境
所有48个单元测试全部通过
测试代码更加简洁,单个测试用例聚焦于单一检查维度
2.3. 增强扩展性#
添加新的检查维度只需实现新的检查器类,无需修改现有代码
遵循开闭原则,扩展时对现有代码零修改
支持动态启用/禁用特定检查维度,便于CI流水线按需配置
2.4. 提高代码可读性#
每个检查器类职责单一,命名清晰,易于理解
协调器类只负责遍历AST和分发检查任务,逻辑简洁
新开发者可以快速定位和理解特定检查维度的实现
3. 方案#
采用 策略模式 + 访问者模式 的组合重构方案,将8种检查职责拆分为独立的检查器类,通过协调器统一调度。
3.1. 架构变更对比#
graph TB
subgraph 当前架构
A[ConcurrentSafetyVisitor<br/>924行] --> B[visit_Module]
A --> C[visit_ClassDef]
A --> D[visit_FunctionDef]
A --> E[visit_Call]
A --> F[其他visit方法]
E --> G[TIMEOUT检查]
E --> H[IDEMPOTENT检查]
E --> I[其他6种检查]
style A fill:#ffcccc
end
graph TB
subgraph 重构后架构
COORD[ConcurrentSafetyCoordinator<br/>251行] --> V1[visit_Module]
COORD --> V2[visit_ClassDef]
COORD --> V3[visit_FunctionDef]
COORD --> V4[visit_Call]
V4 --> DISP[检查器分发器]
DISP --> T1[TimeoutChecker<br/>137行]
DISP --> T2[IdempotentChecker<br/>65行]
DISP --> T3[BoundaryChecker<br/>61行]
DISP --> T4[DefensiveChecker<br/>109行]
DISP --> T5[ConfigChecker<br/>47行]
DISP --> T6[I18nChecker<br/>91行]
DISP --> T7[DeadlockChecker<br/>177行]
DISP --> T8[LeakChecker<br/>122行]
style COORD fill:#ccffcc
style T1 fill:#ccffcc
style T2 fill:#ccffcc
end
3.2. 文件结构#
文件 |
行数 |
职责 |
|---|---|---|
204 |
检查器基类 |
|
251 |
AST遍历协调器 |
|
137 |
TIMEOUT维度:锁/等待超时检查 |
|
65 |
IDEMPOTENT维度:列表追加去重检查 |
|
61 |
BOUNDARY维度:O(n)线性查找检查 |
|
109 |
DEFENSIVE维度:可变默认参数/防御性拷贝 |
|
47 |
CONFIG维度:硬编码参数检查 |
|
91 |
I18N维度:中文字面量检查 |
|
177 |
DEADLOCK维度:锁获取顺序一致性检查 |
|
122 |
LEAK维度:线程池/进程池资源泄漏检查 |
|
~20 |
向后兼容别名 |
3.3. 核心设计#
CheckerContext 共享上下文#
使用 @dataclass 封装所有检查器共享的AST遍历状态,避免约20个实例变量散落在协调器中:
@dataclass
class CheckerContext:
filepath: Path
content_lines: list[str]
current_class: str = ""
function_name: str = ""
in_test_function: bool = False
loop_depth: int = 0
# ... 锁变量、池变量、函数参数等共享状态
BaseChecker 抽象基类#
定义统一的检查器接口,支持AST节点生命周期钩子:
check_module_enter/exit- 模块级检查check_class_enter/exit- 类级检查check_function_enter/exit- 函数级检查check_call- 函数调用检查check_if/while/for_enter/exit- 控制流检查check_compare/return/assign/with/subscript- 其他AST节点检查
协调器调度模式#
协调器只负责AST遍历生命周期管理,将具体检查逻辑分发给所有注册的检查器:
def visit_Call(self, node: ast.Call) -> None:
if self._ctx.in_test_function:
self.generic_visit(node)
return
for checker in self._checkers:
checker.check_call(node)
self.generic_visit(node)
4. 回归验证#
4.1. 单元测试结果#
所有48个单元测试全部通过,覆盖全部8个检查维度:
TIMEOUT维度:11个测试用例(锁超时、wait超时、join超时、while True死循环、asyncio.wait_for等)
IDEMPOTENT维度:3个测试用例(append去重、not in守卫、set.add)
BOUNDARY维度:2个测试用例(列表in查找、set查找)
DEFENSIVE维度:7个测试用例(可变默认参数、return内部状态、防御拷贝)
CONFIG维度:2个测试用例(硬编码sleep、常量超时)
I18N维度:8个测试用例(中文比较、日志豁免、字典key、枚举常量)
DEADLOCK维度:3个测试用例(AB-BA逆序、一致顺序、with语句)
LEAK维度:3个测试用例(无shutdown、with上下文管理器、显式shutdown)
CLI测试:3个测试用例(帮助、JSON输出、维度过滤)
回归测试:4个测试用例(八维常量、原有功能验证)
干净代码测试:2个测试用例(修复后代码通过、测试函数跳过)
4.2. 真实代码扫描验证#
conflict_resolution.py 扫描结果#
【.agents\scripts\lib\collaboration\conflict_resolution.py】100分(527行,0个问题)
重构后模块自扫描结果#
14个Python文件全部通过,0个问题:
- checker_base.py: 100分
- checkers/* (8个文件): 全部100分
- coordinator.py: 100分
- cli.py, constants.py, models.py, scanner.py: 全部100分
4.3. 向后兼容性#
保留
ConcurrentSafetyVisitor别名指向ConcurrentSafetyCoordinatorscanner.py和cli.py的导入和调用方式无需修改检查报告格式和退出码保持不变
所有命令行参数(
--json,--dimensions,--path等)保持不变
5. 经验总结#
策略模式适合多维度检查场景:当一个类承担多种"检查/验证/规则应用"职责时,策略模式是理想的拆分方案
共享上下文避免状态重复:使用数据类封装共享状态,比每个检查器独立维护状态更清晰
生命周期钩子提供灵活性:通过
check_*_enter/exit钩子,检查器可以在需要时介入AST遍历的特定阶段向后兼容降低迁移成本:保留旧类名作为别名,确保现有代码无需立即修改