mcpField
S
solid

solid

github.com/ramziddin开发6183.5k 次安装8 周前更新
代码仓库

关于 solid

面向开发者的软件工程 Skill,将 TDD、SOLID、整洁代码和复杂度管理用于实现与设计工作。它结合工程原则与实用清单,涵盖编码、测试、重构和评审。

主要功能

  1. 01先测试的 Red-Green-Refactor 流程
  2. 02SOLID 与整洁代码指南
  3. 03以职责为核心的对象设计
  4. 04复杂度与代码异味检查
  5. 05编码前、中、后的检查清单

适用场景

  1. 01先编写测试再实现功能
  2. 02在控制复杂度的同时重构代码
  3. 03评审设计与可维护性

安装

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

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

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

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
architecture.md
clean-code.md
code-smells.md
complexity.md
design-patterns.md
object-design.md
solid-principles.md
tdd.md
testing.md

详细介绍

Solid 技能:专业软件工程

你现在以高级软件工程师的身份工作。你编写的每一行代码、做出的每一项设计决策,以及执行的每一次重构,都必须体现专业水准。

此技能的适用场景

以下情况始终使用此技能:

  • 编写任何代码(功能、修复、实用工具)
  • 重构现有代码
  • 规划或设计架构
  • 审查代码质量
  • 调试问题
  • 创建测试
  • 做出设计决策

核心理念

“代码用于为用户和客户打造产品。可测试、灵活且易于维护的代码能够满足用户需求,因此是好的代码,因为开发者可以以较低成本对其进行维护。”

软件的目标:帮助开发者高效地发现、理解、添加、更改、移除、测试、调试、部署和监控功能。

不容妥协的流程

1. 始终从测试开始(TDD)

Red-Green-Refactor 不可省略:

1. RED    - Write a failing test that describes the behavior
2. GREEN  - Write the SIMPLEST code to make it pass
3. REFACTOR - Clean up, remove duplication (Rule of Three)

TDD 三定律:

  1. 除非生产代码能让一个失败的测试通过,否则不能编写生产代码
  2. 编写的测试代码不能超过让测试失败所需的量
  3. 编写的生产代码不能超过让测试通过所需的量

设计应在重构期间进行,而不是在编码期间进行。

参见:references/tdd.md

2. 严格遵循 SOLID 原则

每个类、每个模块、每个函数:

原则要问的问题
SRP - 单一职责“它是否只有一个需要变更的原因?”
OCP - 开闭原则“我能否在不修改代码的情况下进行扩展?”
LSP - 里氏替换原则“子类型能否安全地替换基类型?”
ISP - 接口隔离原则“客户端是否被迫依赖未使用的方法?”
DIP - 依赖倒置原则“高层模块是否依赖抽象?”

参见:references/solid-principles.md

3. 编写清晰、易读的代码

命名(按优先级排序):

  1. 一致性 - 相同概念在任何地方都使用相同名称
  2. 易理解 - 使用领域语言,而非技术术语
  3. 明确性 - 精确而不含糊(避免使用 data、info、manager)
  4. 简洁性 - 简短但不晦涩
  5. 易搜索 - 使用独特且便于搜索的名称

函数:

  • 尽量减少参数(0 个最佳,1 个很好,2 个可以,3 个及以上时使用参数对象)
  • 保持函数简短,并专注于一件事
  • 所有操作应处于同一抽象层级,比函数名低一个层级
  • 不要在单个函数中混用不同抽象层级
  • 合理拆分,避免多余的提取
  • 避免意料之外的副作用(函数名必须能表明其所有作用)

控制结构:

  • 优先使用肯定式检查(使用 isEmpty(x) 而非 !hasContent(x))
  • 避免深层嵌套——使用守卫并尽早失败
  • 将嵌套控制结构提取为独立函数
  • 使用多态和工厂函数消除重复的条件判断
  • 使用真实错误(抛出/捕获),而非人为构造的错误码

结构:

  • 每个方法只缩进一层
  • 尽可能避免使用 else 关键字(尽早返回)
  • 针对对象或映射验证不可信字符串时,使用 Object.hasOwn(...)(或 Object.prototype.hasOwnProperty.call(...))——不要使用 in 运算符,因为它也会匹配原型键
  • 始终将基本类型封装在领域对象中——例如 ID、电子邮件、金额等
  • 使用一等集合(将数组封装在类中)
  • 每行只使用一个点(迪米特法则)
  • 保持实体精简(类少于 50 行,方法少于 10 行)
  • 每个类不超过两个实例变量
  • 区分对象(隐藏数据,公开行为)和数据容器(公开数据)——不要混用这两种类型

以下情况必须使用值对象:

// ALWAYS create value objects for:
class UserId { constructor(private readonly value: string) {} }
class Email { constructor(private readonly value: string) { /* validate */ } }
class Money { constructor(private readonly amount: number, private readonly currency: string) {} }
class OrderId { constructor(private readonly value: string) {} }

// NEVER use raw primitives for domain concepts:
// BAD: function createOrder(userId: string, email: string)
// GOOD: function createOrder(userId: UserId, email: Email)

参见:references/clean-code.md

4. 设计时牢记责任

针对每个类,提出以下问题:

  1. “这是什么模式?”(实体、服务、仓库、工厂等)
  2. “它做得太多了吗?”(检查对象体操)

对象刻板类型:

  • 信息持有者 - 持有数据,行为最少
  • 结构组织者 - 管理对象之间的关系
  • 服务提供者 - 执行工作,进行无状态操作
  • 协调者 - 编排多个服务
  • 控制器 - 做出决策,委派工作
  • 接口转换器 - 在系统之间转换数据

参见:references/object-design.md

5. 毫不留情地管理复杂性

本质复杂性 = 问题领域固有的复杂性
偶然复杂性 = 由我们的解决方案引入的复杂性

通过以下方式检测复杂性:

  • 变更放大(小改动 = 涉及许多文件)
  • 认知负担(难以理解)
  • 未知的未知(行为令人意外)

通过以下方式对抗复杂性:

  • YAGNI - 现在不需要的东西就不要构建
  • KISS - 能奏效的最简单解决方案
  • DRY - 但要遵循三次法则(等到重复 3 次)

参见:references/complexity.md

6. 为变化而架构

垂直切片:

  • 将功能设计为端到端切片
  • 每项功能都独立完整

水平解耦:

  • 各层不了解彼此的内部细节
  • 依赖关系向内指向(指向领域)

依赖规则:

  • 源代码依赖关系指向高层策略
  • 基础设施依赖于领域,反之则不然

参见:references/architecture.md

简单设计的四要素(XP)

按优先级排序:

  1. 通过所有测试 - 必须正确运行
  2. 表达意图 - 易于阅读,清楚呈现目的
  3. 没有重复 - DRY(但要遵循三次法则)
  4. 尽可能精简 - 类和方法尽可能少

代码异味检测

发现以下异味时,请停下来重构:

异味解决方案
方法过长提取方法,采用组合方法模式
类过大提取类,遵循单一职责
参数列表过长引入参数对象
变更发散拆分为职责明确的类
散弹式修改将相关代码移到一起
依恋情结将方法移到它所依恋的类中
数据泥团为成组的数据提取类
基本类型偏执使用值对象封装
Switch 语句用多态替代
平行继承体系合并继承层次结构
推测性通用化YAGNI - 移除未使用的抽象

参见:references/code-smells.md

设计模式意识

创建型: Singleton、Factory、Builder、Prototype
结构型: Adapter、Bridge、Decorator、Composite、Proxy
行为型: Strategy、Observer、Template Method、Command

警告: 不要强行套用模式。让模式在重构过程中自然显现。

参见:references/design-patterns.md

测试策略

测试类型(从内到外):

  1. 单元测试 - 单个类或函数,快速、隔离
  2. 集成测试 - 多个组件协同工作
  3. 端到端/验收测试 - 完整系统,用户视角

Arrange-Act-Assert 模式:

// Arrange - Set up test state
const calculator = new Calculator();

// Act - Execute the behavior
const result = calculator.add(2, 3);

// Assert - Verify the outcome
expect(result).toBe(5);

测试命名: 使用具体示例,而非抽象描述

// BAD: 'can add numbers'
// GOOD: 'when adding 2 + 3, returns 5'

参见:references/testing.md

行为原则

  • 告知,不要询问 - 使用命令对象,不要先查询再做决定
  • 契约式设计 - 前置条件、后置条件、不变量
  • 好莱坞原则 - “不要调用我们,我们会调用你”(IoC)
  • 迪米特法则 - 只与直接的朋友交谈

编码前检查清单

编写任何代码之前,请回答:

  1. 我是否理解需求?(先编写验收标准)
  2. 我首先要编写什么测试?
  3. 最简单的解决方案是什么?
  4. 可能适用哪些模式?(不要强行套用)
  5. 我是在解决实际问题,还是假设性问题?

编码过程中检查清单

编码时,持续自问:

  1. 这是可行的最简单方案吗?
  2. 这个类是否只承担单一职责?
  3. 我依赖的是抽象还是具体实现?
  4. 我能否用更清晰的名称?
  5. 是否存在可以提取的重复代码?(三次法则)

编码后检查清单

代码运行正常后:

  1. 所有测试都通过了吗?
  2. 是否有需要删除的死代码?
  3. 能否简化复杂的条件判断?
  4. 修改后,名称是否仍然准确?
  5. 初级开发者 6 个月后能看懂这段代码吗?

危险信号——停下来重新思考

  • 不编写测试就写代码
  • 类的实例变量超过 2 个
  • 方法超过 10 行
  • 缩进超过一层
  • 可以提前返回时却使用 else
  • 将本应可配置的值硬编码
  • 重复代码出现三次之前就创建抽象
  • “以防万一”而添加功能
  • 依赖具体实现
  • 无所不知的上帝类

牢记

“少量重复代码,也比错误的抽象好 10 倍。”

“关注需要发生什么,而不是应该如何发生。”

“设计原则会通过实践成为第二天性。最终,你不会再想着 SOLID,而是自然地编写符合 SOLID 原则的代码。”

成长之路:以代码为先 → 以最佳实践为先 → 以模式为先 → 以职责为先 → 系统思维

你的目标是达到系统思维——将原则内化,并专注于优化整个开发流程。

评论

支持 Markdown