mcpField
S
solid

solid

github.com/gusen1453开发336 次安装6 周前更新
代码仓库

关于 solid

solid 是帮助开发者运用 SOLID 原则设计和审查模块边界的技能,适用于需要改善依赖方向与可测试性的工作。它通过职责划分、依赖箭头和接口检查来引导设计,避免仅为测试而引入抽象。

主要功能

  1. 01划分模块职责与边界
  2. 02检查依赖方向
  3. 03精简调用方接口
  4. 04将 IO 放在适配器边缘
  5. 05避免仅为测试创建抽象

适用场景

  1. 01规划新增模块与依赖关系
  2. 02审查类和 API 的边界设计
  3. 03重构以减少单元测试中的模拟对象
  4. 04处理模块耦合导致的测试困难

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gusen1453/creed --skill solid

安装后在 Claude Code 中输入/solid确认已加载。

详细介绍

SOLID

输出语言: 使用用户自己的语言回答并编写产物(匹配用户的消息,而不是仓库的历史)。

概述

应用 SOLID,让行为易于修改和测试,而不必堆砌一大堆模拟对象。

配套技能: 使用 grill → write-spec 塑造产品;使用 tdd + test-design 验证行为。本技能只关注结构和依赖方向。

核心原则:难以测试通常意味着难以使用——修正边界,不要用模拟对象掩盖问题。

触发模式(重要)

模式触发时机要做什么
Gate(主要)write-spec(或免除设计阶段)之后、write-plan 之前,且该切片新增模块/端口/IO 边界时执行以下完整工作流——宣布 Using solid to …
Rescue(救援)测试需要 ≥3 个模拟对象;评审/PR 出现耦合;在 tdd 过程中卡在结构问题上执行完整工作流;然后返回调用方
Smell only(仅提示异味,位于 grill 内)比较会导致不同边界划分的方案时不要将本技能作为主技能加载。由 Grill 主导会话;方案标签可以指出一种边界异味(“这会让领域层依赖 SQL,之后就会堆满模拟对象”)。正式的 SOLID 检查等到 Gate 阶段再做

默认: 如果产品的“是什么/为什么”仍未确定 → 使用 grill,而不是 solid。

何时使用

  • 规范已获批准,下一步将要设计包、端口或依赖箭头
  • 设计或评审模块边界、类/API 结构、依赖方向
  • 单元测试需要大量模拟对象才能“隔离”
  • 为提高可测试性而重构;PR 引入耦合/上帝类

何时不要使用:

  • 产品决策仍未确定(使用 grill)
  • 纯粹的胶水代码/顺手重命名/一次性试验
  • 小改动且没有新边界(现有类型、单一行为)——跳过 Gate;转到 write-plan 或 tdd
  • 用来替代对产品权衡的讨论

铁律(不可妥协)

1. New non-trivial behavior lives behind a boundary you can name (SRP).
2. Domain/policy does not depend on IO/framework details (DIP).
3. Fake abstractions invented only to satisfy a test are forbidden (no theater).

违反字面要求就是违反其精神。“把整个世界都模拟出来”和“暂时用一个上帝类也没关系”都不算符合要求。

五项工程检查

S — 单一职责
  • 一个单元 → 一个变更理由
  • 如果名称需要用“和”连接/表示“什么都管的管理器” → 拆分
  • 分离:编排、策略、IO 和格式化
O — 开闭原则
  • 优先通过新增类型/策略/组合来扩展,而不是永远修改核心分支
  • YAGNI:不要预先构建插件系统;但要避免不断把新情况塞进一团代码
L — 里氏替换原则
  • 子类型必须遵守调用方的契约(前置条件/后置条件、错误)
  • 不要为了复用而继承,导致调用方遇到意外;优先使用组合
I — 接口隔离原则
  • 端口应当小巧并贴合调用方,而不是一个庞大的通用接口
  • 调用方不应依赖自己从不使用的方法
D — 依赖倒置原则
  • 高层策略依赖抽象(端口),而非数据库/HTTP/文件系统/SDK 的具体实现
  • 适配器放在边缘;核心应保持纯粹且易于替换
  • 信号: 单元测试需要 ≥3 个模拟对象 → 很可能存在 DIP/SRP 问题(参见 test-design)

工作流(调用时——Gate 或 Rescue)

宣布“Using solid to …”,然后:

  1. 列出单元——每个单元负责什么、如何使用、依赖什么
  2. 画出依赖箭头——领域层 → 端口 ← 适配器(绝不要让领域层直接依赖 SQL/HTTP 客户端)
  3. 检查 SRP——拆分包含“和”的职责
  4. 检查 ISP——精简臃肿的接口
  5. 检查 DIP——将 IO 移到外部;注入端口
  6. 检查形式主义——每个新增接口都必须有产品层面的理由,而不能只有测试层面的理由
  7. 交接——转到 write-plan(Gate)或恢复 tdd(Rescue)。如果产品问题重新出现 → 转到 grill

危险信号

  • 上帝类/包“了解整个功能”
  • 领域层导入 ORM、HTTP 客户端或 UI 套件
  • 子类通过重写来弱化不变量
  • 一个调用方只使用 2 个方法,接口却有 15 个方法
  • 新接口只用于测试(没有生产适配器)
  • 单元测试为了“隔离”而模拟系统的一半

合理化借口对照表

借口现实
“等遇到问题再拆分”需要 4 个模拟对象时,问题已经出现了。
“具体实现比端口更简单”直到出现第二条 IO 路径。端口加一个适配器,比重写更省事。
“继承可以复用代码”继承会耦合层级;应改用组合。
“一个服务接口更整洁”臃肿的接口会迫使你实现伪方法,并带来 ISP 债务。
“模拟对象能证明 DIP”模拟对象会掩盖 DIP 的失败。真正端口就绪后,模拟对象更少才是证明。
“我们还在梳理需求——现在就全面运行 solid”异味只出现在选项标签中。确定 what/why 后,再全面运行 solid。

检查清单

  • 产品的 what/why 已确定(或正在 Rescue 中途实施)
  • 每个单元都有一个明确用途,名称中不含“和”
  • 依赖箭头指向策略,而非指向 IO
  • 端口精简;适配器负责框架
  • 没有仅用于测试的抽象
  • 已准备好进入 write-plan 或 tdd/test-design,无需堆砌模拟对象

交接

转换关卡——在将边界交给 write-plan(关卡)或退回代码(Rescue)之前,询问用户(使用一次 AskUserQuestion;绝不自动推进):

A) Recommended: Proceed → write-plan (Gate) / resume tdd (Rescue)
B) Review first — run **review** on these boundaries (`references/solid.md`; upstream = spec's promised boundaries), then return to this gate
C) Adjust — rework the structure, then return to this gate
D) Something else (I will type it)
  • 产品决策仍未确定 → grill → write-spec
  • 边界已确定 → write-plan
  • 正在实施 → tdd + test-design
  • 正在发布 → review / commit-and-push

评论

支持 Markdown