关于 solid
solid 是一项介绍 SOLID 五项原则的开发技能,供设计、重构和审查软件结构时参考。它提供检测问题的检查项、不同层级的应用方式和常见反模式。
主要功能
- 01说明 SRP、OCP、LSP、ISP、DIP
- 02提供类与模块检查清单
- 03涵盖函数到服务的应用方式
- 04列出常见反模式及修正方向
- 05给出接口与依赖设计建议
适用场景
- 01设计新的类、模块或接口
- 02重构职责过多的代码
- 03审查 PR 中的架构问题
- 04拆分大型类或精简接口
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill solid安装后在 Claude Code 中输入/solid确认已加载。
附带文件agents/openai.yamlreferences/PRINCIPLES.md
详细介绍
SOLID 原则
构建易于理解、扩展和维护的软件的五项原则。它们可以降低耦合、提高内聚性,并让代码更易于测试。
适用场景
在以下情况下参考这些原则:
- 设计新的类、模块或接口
- 重构职责过多的代码
- 从架构角度审查 PR
- 拆分上帝对象或臃肿接口
- 确定模块边界
- 提高代码的可测试性
快速参考
| 原则 | 一句话概括 | 警示信号 |
|---|---|---|
| SRP | 一个变更理由 | “这个类既处理 X 又 处理 Y 还 处理 Z” |
| OCP | 扩展而非修改 | 针对不同类型的 if/else 或 switch 链不断增长 |
| LSP | 子类型可替换 | 调用代码中进行类型检查或特殊处理 |
| ISP | 小而专注的接口 | 空方法实现或 throw new Error("Not implemented") |
| DIP | 依赖抽象 | 业务逻辑中出现 new ConcreteClass() |
详细说明和 TypeScript 示例请参阅 references/PRINCIPLES.md。
检查清单
针对每个类和模块,提出以下问题:
| 问题 | 违反的原则 |
|---|---|
| 这个类是否有多个变更理由? | SRP |
| 添加新变体时,我是否需要修改现有代码? | OCP |
| 调用代码是否需要针对子类型进行类型检查或特殊处理? | LSP |
| 实现类是否被迫为未使用的方法提供空实现? | ISP |
| 高层逻辑是否直接实例化基础设施? | DIP |
在不同层级应用 SOLID
| 层级 | SRP | OCP | LSP | ISP | DIP |
|---|---|---|---|---|---|
| 函数 | 只做一件事 | — | — | — | 将抽象作为参数传入 |
| 类 | 一个变更理由 | 通过组合扩展 | 子类型遵守契约 | 只实现实际使用的内容 | 构造函数注入 |
| 模块 | 一个有界上下文 | 插件架构 | 实现可互换 | 精简的公共 API | 依赖方向向内 |
| 服务 | 单一领域 | 新功能 = 新服务 | API 契约稳定 | 最小化 API 表面 | 在边界处使用抽象 |
原则之间的关系
- SRP + ISP:拆分职责通常也意味着拆分接口
- OCP + DIP:依赖抽象才能实现无需修改即可扩展
- LSP + OCP:如果子类型可替换,就可以通过添加新子类型来扩展行为
- DIP + ISP:小而专注的接口让依赖倒置切实可行
常见反模式
| 反模式 | 违反的原则 | 修复方法 |
|---|---|---|
| 什么都做的上帝类 | SRP | 提取职责专一的类 |
整个代码库中都按类型使用 switch | OCP、LSP | 改用多态 |
| 抛出“not supported”的子类 | LSP、ISP | 重新设计继承层次,拆分接口 |
| 包含 20 个方法的臃肿接口 | ISP | 拆分为按角色划分的接口 |
| 业务逻辑导入数据库驱动 | DIP | 注入存储库接口 |
| 服务自行创建依赖项 | DIP | 使用构造函数注入 |
最佳实践
应该
- 从 SRP 开始——它是其他原则的基础
- 使用接口定义组件之间的边界
- 让违规问题从实际问题中显现,再加以修复
- 扩展行为时优先使用组合,而非继承
- 保持接口精简,并与具体角色相对应
- 通过构造函数注入依赖项
不应该
- 教条式地将 SOLID 用于琐碎代码(5 行的实用工具不需要接口)
- 在至少有两个实现之前就创建抽象
- 将 SRP 误解为“单个方法”——它关注的是变更理由,而非大小
- 强迫本不该处于同一继承层次的类遵循里氏替换原则
- 在一组内聚的方法更合理时,将接口过度拆分为单方法片段
评论
支持 Markdown