# Architecture Review Guide 架构设计审查指南,帮助评估代码的架构是否合理、设计是否恰当。 ## SOLID 原则检查清单 ### S - 单一职责原则 (SRP) **检查要点:** - 这个类/模块是否只有一个改变的理由? - 类中的方法是否都服务于同一个目的? - 如果要向非技术人员描述这个类,能否用一句话说清楚? **代码审查中的识别信号:** ``` ⚠️ 类名包含 "And"、"Manager"、"Handler"、"Processor" 等泛化词汇 ⚠️ 一个类超过 200-300 行代码 ⚠️ 类有超过 5-7 个公共方法 ⚠️ 不同的方法操作完全不同的数据 ``` **审查问题:** - "这个类负责哪些事情?能否拆分?" - "如果 X 需求变化,哪些方法需要改?如果 Y 需求变化呢?" ### O - 开闭原则 (OCP) **检查要点:** - 添加新功能时,是否需要修改现有代码? - 是否可以通过扩展(继承、组合)来添加新行为? - 是否存在大量的 if/else 或 switch 语句来处理不同类型? **代码审查中的识别信号:** ``` ⚠️ switch/if-else 链处理不同类型 ⚠️ 添加新功能需要修改核心类 ⚠️ 类型检查 (instanceof, typeof) 散布在代码中 ``` **审查问题:** - "如果要添加新的 X 类型,需要修改哪些文件?" - "这个 switch 语句会随着新类型增加而增长吗?" ### L - 里氏替换原则 (LSP) **检查要点:** - 子类是否可以完全替代父类使用? - 子类是否改变了父类方法的预期行为? - 是否存在子类抛出父类未声明的异常? **代码审查中的识别信号:** ``` ⚠️ 显式类型转换 (casting) ⚠️ 子类方法抛出 NotImplementedException ⚠️ 子类方法为空实现或只有 return ⚠️ 使用基类的地方需要检查具体类型 ``` **审查问题:** - "如果用子类替换父类,调用方代码是否需要修改?" - "这个方法在子类中的行为是否符合父类的契约?" ### I - 接口隔离原则 (ISP) **检查要点:** - 接口是否足够小且专注? - 实现类是否被迫实现不需要的方法? - 客户端是否依赖了它不使用的方法? **代码审查中的识别信号:** ``` ⚠️ 接口超过 5-7 个方法 ⚠️ 实现类有空方法或抛出 NotImplementedException ⚠️ 接口名称过于宽泛 (IManager, IService) ⚠️ 不同的客户端只使用接口的部分方法 ``` **审查问题:** - "这个接口的所有方法是否都被每个实现类使用?" - "能否将这个大接口拆分为更小的专用接口?" ### D - 依赖倒置原则 (DIP) **检查要点:** - 高层模块是否依赖于抽象而非具体实现? - 是否使用依赖注入而非直接 new 对象? - 抽象是否由高层模块定义而非低层模块? **代码审查中的识别信号:** ``` ⚠️ 高层模块直接 new 低层模块的具体类 ⚠️ 导入具体实现类而非接口/抽象类 ⚠️ 配置和连接字符串硬编码在业务逻辑中 ⚠️ 难以为某个类编写单元测试 ``` **审查问题:** - "这个类的依赖能否在测试时被 mock 替换?" - "如果要更换数据库/API 实现,需要修改多少地方?" --- ## 架构反模式识别 ### 致命反模式 | 反模式 | 识别信号 | 影响 | |--------|----------|------| | **大泥球 (Big Ball of Mud)** | 没有清晰的模块边界,任何代码都可能调用任何其他代码 | 难以理解、修改和测试 | | **上帝类 (God Object)** | 单个类承担过多职责,知道太多、做太多 | 高耦合,难以重用和测试 | | **意大利面条代码** | 控制流程混乱,goto 或深层嵌套,难以追踪执行路径 | 难以理解和维护 | | **熔岩流 (Lava Flow)** | 没人敢动的古老代码,缺乏文档和测试 | 技术债务累积 | ### 设计反模式 | 反模式 | 识别信号 | 建议 | |--------|----------|------| | **金锤子 (Golden Hammer)** | 对所有问题使用同一种技术/模式 | 根据问题选择合适的解决方案 | | **过度工程 (Gas Factory)** | 简单问题用复杂方案解决,滥用设计模式 | YAGNI 原则,先简单后复杂 | | **船锚 (Boat Anchor)** | 为"将来可能需要"而写的未使用代码 | 删除未使用代码,需要时再写 | | **复制粘贴编程** | 相同逻辑出现在多处 | 提取公共方法或模块 | ### 审查问题 ```markdown 🔴 [blocking] "这个类有 2000 行代码,建议拆分为多个专注的类" 🟡 [important] "这段逻辑在 3 个地方重复,考虑提取为公共方法?" 💡 [suggestion] "这个 switch 语句可以用策略模式替代,更易扩展" ``` --- ## 耦合度与内聚性评估 ### 耦合类型(从好到差) | 类型 | 描述 | 示例 | |------|------|------| | **消息耦合** ✅ | 通过参数传递数据 | `calculate(price, quantity)` | | **数据耦合** ✅ | 共享简单数据结构 | `processOrder(orderDTO)` | | **印记耦合** ⚠️ | 共享复杂数据结构但只用部分 | 传入整个 User 对象但只用 name | | **控制耦合** ⚠️ | 传递控制标志影响行为 | `process(data, isAdmin=true)` | | **公共耦合** ❌ | 共享全局变量 | 多个模块读写同一个全局状态 | | **内容耦合** ❌ | 直接访问另一模块的内部 | 直接操作另一个类的私有属性 | ### 内聚类型(从好到差) | 类型 | 描述 | 质量 | |------|------|------| | **功能内聚** | 所有元素完成单一任务 | ✅ 最佳 | | **顺序内聚** | 输出作为下一步输入 | ✅ 良好 | | **通信内聚** | 操作相同数据 | ⚠️ 可接受 | | **时间内聚** | 同时执行的任务 | ⚠️ 较差 | | **逻辑内聚** | 逻辑相关但功能不同 | ❌ 差 | | **偶然内聚** | 没有明显关系 | ❌ 最差 | ### 度量指标参考 ```yaml 耦合指标: CBO (类间耦合): 好: < 5 警告: 5-10 危险: > 10 Ce (传出耦合): 描述: 依赖多少外部类 好: < 7 Ca (传入耦合): 描述: 被多少类依赖 高值意味着: 修改影响大,需要稳定 内聚指标: LCOM4 (方法缺乏内聚): 1: 单一职责 ✅ 2-3: 可能需要拆分 ⚠️ >3: 应该拆分 ❌ ``` ### 审查问题 - "这个模块依赖了多少其他模块?能否减少?" - "修改这个类会影响多少其他地方?" - "这个类的方法是否都操作相同的数据?" --- ## 分层架构审查 ### Clean Architecture 层次检查 ``` ┌─────────────────────────────────────┐ │ Frameworks & Drivers │ ← 最外层:Web、DB、UI ├─────────────────────────────────────┤ │ Interface Adapters │ ← Controllers、Gateways、Presenters ├─────────────────────────────────────┤ │ Application Layer │ ← Use Cases、Application Services ├─────────────────────────────────────┤ │ Domain Layer │ ← Entities、Domain Services └─────────────────────────────────────┘ ↑ 依赖方向只能向内 ↑ ``` ### 依赖规则检查 **核心规则:源代码依赖只能指向内层** ```typescript // ❌ 违反依赖规则:Domain 层依赖 Infrastructure // domain/User.ts import { MySQLConnection } from '../infrastructure/database'; // ✅ 正确:Domain 层定义接口,Infrastructure 实现 // domain/UserRepository.ts (接口) interface UserRepository { findById(id: string): Promise; } // infrastructure/MySQLUserRepository.ts (实现) class MySQLUserRepository implements UserRepository { findById(id: string): Promise { /* ... */ } } ``` ### 审查清单 **层次边界检查:** - [ ] Domain 层是否有外部依赖(数据库、HTTP、文件系统)? - [ ] Application 层是否直接操作数据库或调用外部 API? - [ ] Controller 是否包含业务逻辑? - [ ] 是否存在跨层调用(UI 直接调用 Repository)? **关注点分离检查:** - [ ] 业务逻辑是否与展示逻辑分离? - [ ] 数据访问是否封装在专门的层? - [ ] 配置和环境相关代码是否集中管理? ### 审查问题 ```markdown 🔴 [blocking] "Domain 实体直接导入了数据库连接,违反依赖规则" 🟡 [important] "Controller 包含业务计算逻辑,建议移到 Service 层" 💡 [suggestion] "考虑使用依赖注入来解耦这些组件" ``` --- ## 设计模式使用评估 ### 何时使用设计模式 | 模式 | 适用场景 | 不适用场景 | |------|----------|------------| | **Factory** | 需要创建不同类型对象,类型在运行时确定 | 只有一种类型,或类型固定不变 | | **Strategy** | 算法需要在运行时切换,有多种可互换的行为 | 只有一种算法,或算法不会变化 | | **Observer** | 一对多依赖,状态变化需要通知多个对象 | 简单的直接调用即可满足需求 | | **Singleton** | 确实需要全局唯一实例,如配置管理 | 可以通过依赖注入传递的对象 | | **Decorator** | 需要动态添加职责,避免继承爆炸 | 职责固定,不需要动态组合 | ### 过度设计警告信号 ``` ⚠️ Patternitis(模式炎)识别信号: 1. 简单的 if/else 被替换为策略模式 + 工厂 + 注册表 2. 只有一个实现的接口 3. 为了"将来可能需要"而添加的抽象层 4. 代码行数因模式应用而大幅增加 5. 新人需要很长时间才能理解代码结构 ``` ### 审查原则 ```markdown ✅ 正确使用模式: - 解决了实际的可扩展性问题 - 代码更容易理解和测试 - 添加新功能变得更简单 ❌ 过度使用模式: - 为了使用模式而使用 - 增加了不必要的复杂度 - 违反了 YAGNI 原则 ``` ### 审查问题 - "使用这个模式解决了什么具体问题?" - "如果不用这个模式,代码会有什么问题?" - "这个抽象层带来的价值是否大于它的复杂度?" --- ## 可扩展性评估 ### 扩展性检查清单 **功能扩展性:** - [ ] 添加新功能是否需要修改核心代码? - [ ] 是否提供了扩展点(hooks、plugins、events)? - [ ] 配置是否外部化(配置文件、环境变量)? **数据扩展性:** - [ ] 数据模型是否支持新增字段? - [ ] 是否考虑了数据量增长的场景? - [ ] 查询是否有合适的索引? **负载扩展性:** - [ ] 是否可以水平扩展(添加更多实例)? - [ ] 是否有状态依赖(session、本地缓存)? - [ ] 数据库连接是否使用连接池? ### 扩展点设计检查 ```typescript // ✅ 好的扩展设计:使用事件/钩子 class OrderService { private hooks: OrderHooks; async createOrder(order: Order) { await this.hooks.beforeCreate?.(order); const result = await this.save(order); await this.hooks.afterCreate?.(result); return result; } } // ❌ 差的扩展设计:硬编码所有行为 class OrderService { async createOrder(order: Order) { await this.sendEmail(order); // 硬编码 await this.updateInventory(order); // 硬编码 await this.notifyWarehouse(order); // 硬编码 return await this.save(order); } } ``` ### 审查问题 ```markdown 💡 [suggestion] "如果将来需要支持新的支付方式,这个设计是否容易扩展?" 🟡 [important] "这里的逻辑是硬编码的,考虑使用配置或策略模式?" 📚 [learning] "事件驱动架构可以让这个功能更容易扩展" ``` --- ## 代码结构最佳实践 ### 目录组织 **按功能/领域组织(推荐):** ``` src/ ├── user/ │ ├── User.ts (实体) │ ├── UserService.ts (服务) │ ├── UserRepository.ts (数据访问) │ └── UserController.ts (API) ├── order/ │ ├── Order.ts │ ├── OrderService.ts │ └── ... └── shared/ ├── utils/ └── types/ ``` **按技术层组织(不推荐):** ``` src/ ├── controllers/ ← 不同领域混在一起 │ ├── UserController.ts │ └── OrderController.ts ├── services/ ├── repositories/ └── models/ ``` ### 命名约定检查 | 类型 | 约定 | 示例 | |------|------|------| | 类名 | PascalCase,名词 | `UserService`, `OrderRepository` | | 方法名 | camelCase,动词 | `createUser`, `findOrderById` | | 接口名 | I 前缀或无前缀 | `IUserService` 或 `UserService` | | 常量 | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` | | 私有属性 | 下划线前缀或无 | `_cache` 或 `#cache` | ### 文件大小指南 ```yaml 建议限制: 单个文件: < 300 行 单个函数: < 50 行 单个类: < 200 行 函数参数: < 4 个 嵌套深度: < 4 层 超出限制时: - 考虑拆分为更小的单元 - 使用组合而非继承 - 提取辅助函数或类 ``` ### 审查问题 ```markdown 🟢 [nit] "这个 500 行的文件可以考虑按职责拆分" 🟡 [important] "建议按功能领域而非技术层组织目录结构" 💡 [suggestion] "函数名 `process` 不够明确,考虑改为 `calculateOrderTotal`?" ``` --- ## 快速参考清单 ### 架构审查 5 分钟速查 ```markdown □ 依赖方向是否正确?(外层依赖内层) □ 是否存在循环依赖? □ 核心业务逻辑是否与框架/UI/数据库解耦? □ 是否遵循 SOLID 原则? □ 是否存在明显的反模式? ``` ### 红旗信号(必须处理) ```markdown 🔴 God Object - 单个类超过 1000 行 🔴 循环依赖 - A → B → C → A 🔴 Domain 层包含框架依赖 🔴 硬编码的配置和密钥 🔴 没有接口的外部服务调用 ``` ### 黄旗信号(建议处理) ```markdown 🟡 类间耦合度 (CBO) > 10 🟡 方法参数超过 5 个 🟡 嵌套深度超过 4 层 🟡 重复代码块 > 10 行 🟡 只有一个实现的接口 ``` --- ## 工具推荐 | 工具 | 用途 | 语言支持 | |------|------|----------| | **SonarQube** | 代码质量、耦合度分析 | 多语言 | | **NDepend** | 依赖分析、架构规则 | .NET | | **JDepend** | 包依赖分析 | Java | | **Madge** | 模块依赖图 | JavaScript/TypeScript | | **ESLint** | 代码规范、复杂度检查 | JavaScript/TypeScript | | **CodeScene** | 技术债务、热点分析 | 多语言 | --- ## 参考资源 - [Clean Architecture - Uncle Bob](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) - [SOLID Principles in Code Review - JetBrains](https://blog.jetbrains.com/upsource/2015/08/31/what-to-look-for-in-a-code-review-solid-principles-2/) - [Software Architecture Anti-Patterns](https://medium.com/@christophnissle/anti-patterns-in-software-architecture-3c8970c9c4f5) - [Coupling and Cohesion in System Design](https://www.geeksforgeeks.org/system-design/coupling-and-cohesion-in-system-design/) - [Design Patterns - Refactoring Guru](https://refactoring.guru/design-patterns)