关于 codeprobe-solid
面向开发者的代码审查技能,用于检查代码是否符合五项 SOLID 设计原则。它识别职责划分、扩展性、可替换性、接口设计和依赖方面的潜在问题。
主要功能
- 01审查五项 SOLID 原则
- 02标记职责混杂的类
- 03检查扩展与替换模式
- 04发现过宽或未使用的接口
- 05检测对具体依赖的耦合
适用场景
- 01在代码变更中审查架构
- 02定位承担无关职责的类
- 03评估基于类型分支的扩展性
- 04查找直接依赖基础设施的代码
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ machineroom-dev/ codeprobe --skill codeprobe-solid安装后在 Claude Code 中输入/codeprobe-solid确认已加载。
详细介绍
独立模式
如果直接调用(而非通过编排器调用),必须先:
- 阅读
../codeprobe/shared-preamble.md(相对于此 SKILL.md 的位置解析,即同级的codeprobe技能目录,而非用户的项目),了解输出约定、执行模式和限制。 - 根据项目的技术栈,从
../codeprobe/references/(以相同方式解析)加载适用的参考文件。 - 默认使用
full模式,除非用户另有指定。
SOLID 原则审计器
领域范围
此子技能用于检测对五项 SOLID 原则的违反:
- 单一职责原则(SRP) — 一个类或模块应只有一个变更理由。
- 开闭原则(OCP) — 软件实体应对扩展开放,对修改关闭。
- 里氏替换原则(LSP) — 子类型必须能够替换其基类型,且不影响程序正确性。
- 接口隔离原则(ISP) — 不应强迫客户端依赖其不使用的接口。
- 依赖倒置原则(DIP) — 高层模块不应依赖低层模块;两者都应依赖抽象。
不会标记的情况
- 包含多个字段的简单 DTO/值对象 — 如果类表示单一数据概念,那么属性较多不违反 SRP。
- 无需依赖注入的小型脚本和 CLI — 设计应与实际需求相称。
- 用于 switch 语句且稳定、封闭的枚举(例如方位和星期几)— 只有当变体可能增加时,OCP 才适用。
- 遵循框架约定的框架生成类(例如 Laravel 迁移、Django 管理类、带有标准回调的 Rails ActiveRecord 模型)。
- 汇总设置工具的测试辅助类 — 测试基础设施有不同的设计限制。
检测说明
SRP — 单一职责原则
| ID 前缀 | 信号 | 检测方法 | 严重程度 |
|---|---|---|---|
SRP | 类有 5 个或更多执行不同任务的公共方法 | 统计公共方法。如果有 5 个或更多,检查这些方法是否围绕单一职责展开,还是涉及多个关注点(例如,身份验证 + 电子邮件 + 数据库)。留意暗示不同领域的方法名前缀。 | 主要 |
SRP | 方法超过 30 行代码且处理多个关注点 | 统计每个方法的行数(不包括空行和注释)。如果超过 30 行代码,检查该方法是否处理了多个不同步骤(验证、转换、持久化、通知),这些步骤是否可以提取出来。 | 次要 |
SRP | 类名含义模糊 | 标记名为 Manager、Handler、Utils、Helper、Processor、Service(未限定领域)的类 — 这些名称表明该类没有明确的单一用途。例外:如果类较小(少于 5 个方法且少于 100 行代码),则降级为建议。 | 次要 |
SRP | 构造函数有 5 个或更多依赖项 | 统计构造函数参数或注入的依赖项。5 个或更多依赖项表明该类可能承担过多职责。检查这些依赖项是否服务于不同领域。 | 主要 |
OCP — 开闭原则
| ID 前缀 | 信号 | 检测方法 | 严重程度 |
|---|---|---|---|
OCP | 基于类型或状态的 switch/if-else 链 | 查找基于类型、状态、角色或种类字段进行分支的 switch/if-else 链 — 尤其是添加新变体需要修改此代码块的情况。统计分支数量:对于可能扩展的类型,3 个或更多分支即为信号。 | 主要 |
OCP | 变体可能增加但没有扩展点 | 找到基于类型的 switch/if-else 语句后,检查是否存在接口、抽象类、策略模式或插件机制,以便在不修改现有代码的情况下添加新变体。如果没有,则标记。 | 次要 |
LSP — 里氏替换原则
| ID 前缀 | 信号 | 检测方法 | 严重程度 |
|---|---|---|---|
LSP | 子类重写方法,但改变了其语义 | 检查重写的方法:子类是否返回了根本不同的类型、改变了返回值的含义,或产生了父类不会产生的副作用?比较方法签名和文档注释。 | 重大 |
LSP | 子类抛出父类未声明的异常 | 查找重写的方法,检查其抛出的异常是否未出现在父类的 throws 子句或文档化的契约中。在动态语言中,查找父类方法不会抛出异常,但重写方法中出现 raise/throw 的情况。 | 重大 |
LSP | 子类忽略父类行为,或不执行任何操作 | 查找方法体为空、使用 pass、return null、return 或包含 // not implemented 注释的重写方法。这说明子类拒绝履行父类的契约。 | 次要 |
LSP | 多态调用后进行 instanceof/类型检查 | 搜索对本应以多态方式使用的对象进行 instanceof、is_a、typeof、type()、is 检查的代码。如果代码先对基类类型调用方法,随后又检查具体类型,则说明可替换性遭到破坏。 | 重大 |
ISP — 接口隔离原则
| ID 前缀 | 信号 | 检测方法 | 严重程度 |
|---|---|---|---|
ISP | 接口有 8 个或更多方法 | 统计每个接口/抽象类/协议中声明的方法数量。如果有 8 个或更多方法,该接口可能过于宽泛。检查这些方法是否可归为不同的组。 | 次要 |
ISP | 类实现接口,但将方法留空或抛出异常 | 查找接口实现,检查是否有一个或多个方法为空、返回 null、抛出 NotImplementedError/UnsupportedOperationException,或只包含 pass/TODO 注释。 | 重大 |
ISP | 一个接口中包含多个互不相关的方法组 | 分析接口的方法:它们是否属于 2 个或更多不同的职责组(例如,读取操作 + 写入操作 + 通知方法)?如果是,则应拆分该接口。 | 重大 |
DIP — 依赖倒置原则
| ID 前缀 | 信号 | 检测方法 | 严重程度 |
|---|---|---|---|
DIP | 业务逻辑中出现 new ConcreteClass() | 搜索业务逻辑方法中直接实例化(new ClassName()、直接调用构造函数)的基础设施类或服务类。排除:DTO、值对象、异常、集合、工厂和 DI 容器注册。 | 重大 |
DIP | 高层模块直接导入基础设施层内容 | 检查导入语句:领域/业务逻辑模块是否直接导入数据库驱动、HTTP 客户端、文件系统库或第三方 SDK,而没有抽象层?查找 import mysql、use Illuminate\Support\Facades\DB、from requests import 等模式。 | 重大 |
DIP | 没有构造函数注入,而是静态调用具体依赖的方法 | 查找业务逻辑中类似 Database::query()、Cache::get()、Logger.log() 的静态方法调用,且没有注入接口的情况。该类通过静态访问与具体实现紧密耦合。 | 次要 |
ID 前缀与修复提示词示例
SRP-— 单一职责原则违规OCP-— 开闭原则违规LSP-— 里氏替换原则违规ISP-— 接口隔离原则违规DIP-— 依赖倒置原则违规
在每个前缀内按顺序为发现项编号:SRP-001、SRP-002、OCP-001 等。
修复提示词示例
- “重构
src/OrderService.php:将支付逻辑(第 45-78 行)提取到新的PaymentService类中。通过构造函数将PaymentService注入OrderService。将calculateTotal()、applyDiscount()和processPayment()方法移至新类。” - “将
NotificationSender中对$type的 switch 语句(第 30-65 行)替换为策略模式:创建一个NotificationChannel接口,包含send(Message $message)方法。创建EmailChannel、SmsChannel和PushChannel实现。使用NotificationChannelFactory按类型解析正确的通道。” - “在
UserRepository.php中,将第 23 行的new MySqlConnection()替换为构造函数注入:在构造函数中添加一个DatabaseConnectionInterface $connection参数,并使用它替代具体类的实例化。”
评论
支持 Markdown