## 重构 实施安全且渐进式的代码重构,定量评估 SOLID 原则的遵守情况。将技术债务可视化,明确改进的优先级。 ### 使用方法 ```bash # 识别复杂代码并制定重构计划 find . -name "*.js" -exec wc -l {} + | sort -rn | head -10 "重构大文件以降低复杂度" # 检测并整合重复代码 grep -r "function processUser" . --include="*.js" "通过 Extract Method 将重复函数共通化" # 检测 SOLID 原则违反 grep -r "class.*Service" . --include="*.js" | head -10 "评估这些类是否遵循单一职责原则" ``` ### 基本示例 ```bash # 检测长方法 grep -A 50 "function" src/*.js | grep -B 50 -A 50 "return" | wc -l "用 Extract Method 拆分 50 行以上的方法" # 条件分支的复杂度 grep -r "if.*if.*if" . --include="*.js" "用 Strategy 模式改进嵌套的条件语句" # 检测代码异味 grep -r "TODO\|FIXME\|HACK" . --exclude-dir=node_modules "解决成为技术债务的注释" ``` ### 重构技法 #### Extract Method(提取方法) ```javascript // Before: 冗长的方法 function processOrder(order) { // 50 行复杂处理 } // After: 职责分离 function processOrder(order) { validateOrder(order); calculateTotal(order); saveOrder(order); } ``` #### Replace Conditional with Polymorphism(多态替换条件) ```javascript // Before: switch 语句 function getPrice(user) { switch (user.type) { case "premium": return basePrice * 0.8; case "regular": return basePrice; } } // After: Strategy 模式 class PremiumPricing { calculate(basePrice) { return basePrice * 0.8; } } ``` ### SOLID 原则评分 (0-100 分) #### 评估标准与计分 ```text S - Single Responsibility(20 分) ├─ 类的职责数: 1 个 (20 分) | 2 个 (15 分) | 3 个 (10 分) | 4 个以上 (5 分) ├─ 方法数: <7 个 (+5 分) | 7-15 个 (+3 分) | >15 个 (0 分) ├─ 变更原因的明确性: 明确 (+5 分) | 模糊 (0 分) └─ 评分例: UserService(认证+数据处理) = 10 分 O - Open/Closed(20 分) ├─ 扩展点: Strategy/Template Method(20 分) | 仅继承 (10 分) | 无 (5 分) ├─ 新功能添加时的现有代码变更: 不需要 (+5 分) | 最小限 (+3 分) | 需要 (0 分) ├─ 接口利用: 适当 (+5 分) | 部分 (+3 分) | 无 (0 分) └─ 评分例: PaymentProcessor(Strategy) = 20 分 L - Liskov Substitution(20 分) ├─ 派生类的契约遵守: 完全 (20 分) | 部分 (10 分) | 违反 (0 分) ├─ 先决条件的加强: 无 (+5 分) | 有 (-5 分) ├─ 后决条件的弱化: 无 (+5 分) | 有 (-5 分) └─ 评分例: Square extends Rectangle = 0 分 (违反) I - Interface Segregation(20 分) ├─ 接口大小: 1-3 方法 (20 分) | 4-7(15 分) | 8+(5 分) ├─ 未使用方法实现: 无 (+5 分) | 1-2 个 (+2 分) | 3 个以上 (0 分) ├─ 角色的明确性: 单一角色 (+5 分) | 多个角色 (0 分) └─ 评分例: Readable/Writable 分离 = 20 分 D - Dependency Inversion(20 分) ├─ 依赖方向: 仅抽象 (20 分) | 混合 (10 分) | 仅具象 (5 分) ├─ DI 利用: Constructor Injection(+5 分) | Setter(+3 分) | 无 (0 分) ├─ 可测试性: Mock 可能 (+5 分) | 困难 (0 分) └─ 评分例: Repository Pattern = 20 分 总评分 = S + O + L + I + D ├─ 90-100 分: Excellent(SOLID 完全遵守) ├─ 70-89 分: Good(轻微改进余地) ├─ 50-69 分: Fair(重构建议) ├─ 30-49 分: Poor(大規模改进必要) └─ 0-29 分: Critical(设计重新考虑必要) ``` ### 重构步骤 1. **现状分析** - 复杂度测量 (循环复杂度) - 重复代码检测 - 依赖关系分析 2. **渐进式执行** - 小步骤 (15-30 分钟单位) - 每次变更后执行测试 - 频繁提交 3. **质量确认** - 维持测试覆盖率 - 性能测量 - 代码审查 ### 常见的代码异味 - **God Object**: 承担过多职责的类 - **Long Method**: 超过 50 行的长方法 - **Duplicate Code**: 相同逻辑的重复 - **Large Class**: 超过 300 行的大类 - **Long Parameter List**: 4 个以上的参数 ### 自动化支持 ```bash # 静态分析 npx complexity-report src/ sonar-scanner # 代码格式化 npm run lint:fix prettier --write src/ # 测试执行 npm test npm run test:coverage ``` ### 技术债务的定量化 #### 债务计算公式 ```text 技术债务 (时间) = 复杂度评分 × 影响范围 × 修复难度 复杂度评分: ├─ 循环复杂度: 1-5(低) | 6-10(中) | 11-20(高) | 21+(危险) ├─ 认知复杂度: 嵌套深度 × 条件分支数 ├─ 代码行数: <50(1 点) | 50-200(2 点) | 200-500(3 点) | 500+(5 点) └─ 重复率: 0-10%(1 点) | 10-30%(2 点) | 30-50%(3 点) | 50%+(5 点) 影响范围: ├─ 依赖模块数: 直接依赖 + 间接依赖 × 0.5 ├─ 使用频率: API 调用次数/日 ├─ 业务重要度: Critical(×3) | High(×2) | Medium(×1) | Low(×0.5) └─ 团队知识: 理解者 1 名 (×3) | 2-3 名 (×2) | 4 名以上 (×1) 修复难度: ├─ 测试覆盖率: 0%(×3) | <50%(×2) | 50-80%(×1.5) | >80%(×1) ├─ 文档: 无 (×2) | 不充分 (×1.5) | 充分 (×1) ├─ 依赖关系: 密耦合 (×3) | 中等 (×2) | 疏耦合 (×1) └─ 变更风险: Breaking Change(×3) | 兼容性考虑 (×2) | 安全 (×1) 成本换算: ├─ 时间成本: 债务时间 × 开发者时薪 ├─ 机会损失: 新功能开发延迟日数 × 日次销售影响 ├─ 质量成本: 缺陷发生概率 × 修复成本 × 发生频率 └─ 总成本: 时间 + 机会损失 + 质量成本 ``` #### 优先级矩阵 | 优先级 | 影响度 | 修复成本 | 处理期限 | 具体例 | 推荐行动 | | ------------------------- | ------ | -------- | -------- | ------------------------ | -------------------- | | **Critical(即座处理)** | 高 | 低 | 1 周内 | God Object、循环依赖 | 即座重构开始 | | **Important(计划性处理)** | 高 | 高 | 1 个月内 | 大规模职责分离、架构变更 | 纳入 Sprint 计划 | | **Watch(监视对象)** | 低 | 高 | 3 个月内 | 复杂度高的内部处理 | 指标监视、恶化时处理 | | **Acceptable(容许范围)** | 低 | 低 | 无需处理 | 轻微的代码异味 | 通常重构时处理 | ### 重构步骤 1. **现状分析和测量** - 复杂度测量 (循环、认知) - SOLID 评分计算 (0-100 分) - 技术债务定量化 (时间/成本) - 优先级矩阵创建 2. **渐进式执行** - 小步骤 (15-30 分钟单位) - 每次变更后测试执行 - 频繁提交 - SOLID 评分的持续测量 3. **质量确认** - 测试覆盖率维持 - 性能测量 - 技术债务删减确认 - 代码审查 ### 常见代码异味和债务评分 | 代码异味 | 检测标准 | 债务评分 | 改进手法 | | ----------------------- | -------------------- | ----------- | ----------------------- | | **God Object** | 职责 >3, 方法 >20 | 高 (15-20h) | Extract Class, SRP 适用 | | **Long Method** | 行数 >50, 复杂度 >10 | 中 (5-10h) | Extract Method | | **Duplicate Code** | 重复率 >30% | 高 (10-15h) | Extract Method/Class | | **Large Class** | 行数 >300, 方法 >15 | 高 (10-20h) | Extract Class | | **Long Parameter List** | 参数 >4 | 低 (2-5h) | Parameter Object | | **Feature Envy** | 其他类引用 >5 | 中 (5-10h) | Move Method | | **Data Clumps** | 同一参数群重复 | 低 (3-5h) | Extract Class | | **Primitive Obsession** | 原始类型过度使用 | 中 (5-8h) | Replace with Object | | **Switch Statements** | case >5 | 中 (5-10h) | Strategy Pattern | | **Shotgun Surgery** | 变更时影响位置 >3 | 高 (10-15h) | Move Method/Field | ### 实践例: SOLID 评分评估 ```javascript // 评估对象: UserService 类 class UserService { constructor(db, cache, logger, emailService) { // 4 个依赖 this.db = db; this.cache = cache; this.logger = logger; this.emailService = emailService; } // 职责 1: 认证 authenticate(username, password) { /* ... */ } refreshToken(token) { /* ... */ } // 职责 2: 用户管理 createUser(data) { /* ... */ } updateUser(id, data) { /* ... */ } deleteUser(id) { /* ... */ } // 职责 3: 通知 sendWelcomeEmail(user) { /* ... */ } sendPasswordReset(email) { /* ... */ } } // SOLID 评分评估结果 S: 10 分 (职责 3 个: 认证、CRUD、通知) O: 5 分 (无扩展点,直接实现) L: 15 分 (无继承,不适用) I: 10 分 (接口未分离) D: 10 分 (具象类依赖) 总合: 50 分 (Fair - 重构建议) // 技术债务 复杂度: 15 (方法 7 个,职责 3 个) 影响范围: 8 (认证系在全功能使用) 修复难度: 2 (有测试,文档不足) 债务时间: 15 × 8 × 2 = 240 小时 优先级: Critical (认证系应即座处理) ``` ### 改进后实现例 ```javascript // SOLID 原则适用后 (评分: 90 分) // S: 单一职责 (20 分) class AuthenticationService { authenticate(credentials) { /* ... */ } refreshToken(token) { /* ... */ } } // O: 开放封闭 (20 分) class UserRepository { constructor(storage) { // Strategy Pattern this.storage = storage; } save(user) { return this.storage.save(user); } } // I: 接口分离 (20 分) interface Readable { find(id); findAll(); } interface Writable { save(entity); delete(id); } // D: 依赖倒置 (20 分) class UserService { constructor( private auth: IAuthService, private repo: IUserRepository, private notifier: INotificationService ) {} } // 债务删减: 240 小时 → 20 小时 (92% 删减) ``` ### 注意事项 - **禁止功能变更**: 不改变外部行为 - **测试优先**: 重构前添加测试 - **渐进式方法**: 不要一次性大幅变更 - **持续验证**: 每步都执行测试