mcpField
S
solid

solid

github.com/jordancoin开发1483 次安装36 周前更新
代码仓库

关于 solid

solid 是一项开发技能,介绍五项 SOLID 设计原则,并提供实践建议和代码审查问题,面向构建模块化、可测试系统的开发者。它也说明了何时不必增加抽象。

主要功能

  1. 01讲解全部五项 SOLID 原则
  2. 02列举常见设计违例
  3. 03提供实践应用建议
  4. 04提供代码审查清单
  5. 05提示避免过度抽象

适用场景

  1. 01审查模块和组件的职责划分
  2. 02重构臃肿接口或分支逻辑
  3. 03评估依赖边界是否便于测试

安装

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

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

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

详细介绍

SOLID 原则

应用之前

如果 .agents/stack-context.md 存在,请先阅读。根据检测到的技术栈,使用符合其惯例的模式来应用此原则。涉及框架的细节,请使用 context7 MCP 或网络搜索,不要猜测。

原则

五项设计原则,可构建模块化、易于测试且可靠的系统。这些原则最初是针对面向对象设计提出的,但其核心思想适用于任何编程范式——函数、模块、服务或组件。

为什么这对生产环境很重要

遵循 SOLID 原则的代码更易于修改。生产系统会持续变化——添加新功能、修复 Bug、集成服务、扩展规模。按照 SOLID 原则设计的系统能够局部吸收变化,而不必引发连锁修改。这意味着回归问题更少、功能交付更快,出错时影响范围也更小。


S — 单一职责原则

一个模块应该只有一个,且只有一个,变更原因。

每个模块、函数或组件都应只负责一项功能。收到变更请求时,你应该能够直接判断需要编辑哪个文件,而不必四处查找。

违反原则的表现:

  • 一个函数同时验证输入、查询数据库、应用业务逻辑并格式化响应
  • 一个“utils”文件不断膨胀,因为其他功能都找不到明确的归属
  • 一个类会随着系统中任何功能的变化而修改

应用方法:

  • 如果一个函数做了两件事,就将其拆分为两个函数
  • 如果一个模块既负责“做什么”,又负责“怎么做”,就将策略与机制分开
  • 根据职责为模块命名——如果名称含糊(“Manager”“Helper”“Utils”),说明职责不明确

O — 开闭原则

软件应当对扩展开放,对修改关闭。

你应该能够添加新行为,而无需修改现有的可用代码。这并不意味着你永远不动现有代码,而是说你应当通过设计,让常见扩展点无需修改现有代码。

违反原则的表现:

  • 每添加一项功能,switch 语句就要增加一个分支
  • 一个函数对不断扩大的类型列表逐一检查 if type == "x"
  • 每次添加新集成都必须修改一个中央分发函数

应用方法:

  • 使用分发表、注册表或多态,而不是根据类型进行分支
  • 围绕契约(接口、协议、函数签名)进行设计,让新的实现能够满足这些契约
  • 使用插件架构——但前提是确实有多个插件(参见 YAGNI)

L — 里氏替换原则

子类型必须能在任何需要父类型的地方使用,且不会带来意外。

如果代码接受某个基类型,那么该类型的任何实现都应能正常工作。不要特殊处理,也不要添加“除非类型是 X”的判断。

违反原则的表现:

  • 子类对继承的方法抛出“未实现”错误
  • 接受通用类型后,再通过类型检查(instanceof、typeof)来决定行为
  • 使用方必须知道当前使用的是哪个具体实现

应用方法:

  • 如果子类型无法满足父类型的完整契约,就不应将其作为子类型——改用组合
  • 根据使用方的需求设计接口,而不是根据实现能够提供什么来设计
  • 通过替换进行测试:更换实现,并验证系统仍能正确运行

I — 接口隔离原则

不应强迫任何使用方依赖自己不会用到的方法。

让接口保持小巧且专注。只读取数据的使用方,不应依赖一个同时包含写入、删除和管理操作的接口。

违规情况包括:

  • 包含 20 多个方法的“上帝接口”,大多数实现只能部分满足其要求
  • 使用方导入了大型模块,却只用到其中一个函数
  • 测试中的模拟对象需要桩接 15 个方法,而测试实际上只会调用其中 2 个

应用方式:

  • 将大型接口拆分为按角色划分的接口:用 Reader、Writer、Admin 代替 Repository
  • 在没有接口的语言中,将这一原则应用于模块边界——只导出使用方需要的内容
  • 如果测试模拟对象很难编写,说明你的接口范围太广

D — 依赖倒置原则

高层策略不应依赖底层细节。两者都应依赖抽象。

业务逻辑不应直接导入数据库驱动、HTTP 客户端或文件系统操作。它应依赖由这些细节实现的抽象。

违规情况包括:

  • 业务逻辑直接实例化数据库连接、HTTP 客户端或外部 SDK
  • 不启动真实基础设施就无法测试业务规则
  • 更换数据库或 API 提供商需要重写业务逻辑

应用方式:

  • 通过依赖注入传入依赖,而不是在内部构造它们
  • 在业务逻辑的边界定义接口——让基础设施适配这些接口
  • 在函数式代码中:接受函数作为参数,而不是将对特定实现的调用硬编码

边界

  • SOLID 是一组启发式原则,而非法律。 小型脚本、原型和胶水代码不需要完整遵循 SOLID。在这些原则能降低变更成本的地方应用它们。
  • 不要过度抽象。 将 SOLID 推向极端会产生大量细小的类和接口,比原来的单体更难理解。抽象必须能服务多个使用方或实现有意义的可测试性,才能体现其价值。
  • 与 KISS 的张力: 每个接口、注入点和抽象都会增加一层间接性。在系统确实会沿这些方面变化的地方应用 SOLID,而不是为了预防而应用。
  • 函数式代码在某些方面天然满足 SOLID。 纯函数天然符合 SRP。高阶函数天然符合 DIP。应将 SOLID 的重点放在最重要的架构边界上。

代码审查清单

  • 每个模块是否都有明确且单一的职责?
  • 能否在不修改现有可正常工作的代码的情况下添加新行为?
  • 接口是否足够小,使使用方不必依赖自己不会用到的内容?
  • 依赖是否通过注入传入,而不是硬编码?
  • 是否可以替换某个实现(例如数据库、API 客户端),而不触及业务逻辑?

相关技能

  • separation-of-concerns:用于分层结构(SoC 说明“是什么”,SOLID 说明“怎么做”)
  • law-of-demeter:用于减少组件之间的耦合
  • yagni:用于避免通过过早抽象而过度应用 SOLID

评论

支持 Markdown