关于 clean-code
面向软件开发者的整洁代码技能,汇总可读、易测试代码的实践,涵盖命名、函数、注释、错误处理、类、测试与代码异味,并附有章节索引。
主要功能
- 01体现意图的命名
- 02小而专注的函数
- 03注释与错误处理建议
- 04类设计与内聚原则
- 05测试与 TDD 实践
- 06代码异味参考
适用场景
- 01检查代码可读性问题
- 02重构过大的函数和类
- 03改进单元测试结构
- 04识别重复代码与死代码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ratacat/ claude-skills --skill clean-code安装后在 Claude Code 中输入/clean-code确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
chapters/classes.mdcomments.mderror-handling.mdfunctions.mdnames.mdobjects-and-data.mdtests.md详细介绍
Clean Code
概述
整洁的代码读起来就像文笔流畅的文章。每个名称都能揭示意图。每个函数都在讲述一个故事。每个类都有单一用途。目标不只是让代码正常运行,而是让他人能够快速理解、安全修改并放心扩展代码。
“整洁的代码总是让人觉得是由一个用心的人写出来的。” — Michael Feathers
“当每个例程的表现都基本符合你的预期时,你就知道自己正在编写整洁的代码。” — Ward Cunningham
童子军规则: 让代码比你发现它时更整洁。每次提交都应该提升质量,哪怕只提升一点点。点滴改进终会积少成多。
章节参考
此技能提供概述和快速参考。有关详细指南和示例,请参阅各章节文件:
chapters/names.md- 有意义的名称(揭示意图、便于搜索、易于发音)chapters/functions.md- 函数(简短、只做一件事、参数少)chapters/comments.md- 注释(为何应避免,以及哪些注释可以接受)chapters/objects-and-data.md- 对象和数据结构(迪米特法则、DTO)chapters/error-handling.md- 错误处理(异常、null 处理、特例模式)chapters/tests.md- 单元测试(TDD、F.I.R.S.T.、整洁的测试)chapters/classes.md- 类(SRP、内聚性、OCP、DIP)smells-and-heuristics.md- 完整的代码异味参考(66 种异味及其说明)
快速参考:名称
名称应揭示意图,并便于搜索。
| 规则 | 不佳 | 良好 |
|---|---|---|
| 揭示意图 | d | elapsedTimeInDays |
| 避免误导 | accountList(实际不是 List) | accounts |
| 做出区分 | a1, a2 | source, destination |
| 易于发音 | genymdhms | generationTimestamp |
| 便于搜索 | 7 | MAX_CLASSES_PER_STUDENT |
| 类名 = 名词 | Process | Customer, Account |
| 方法名 = 动词 | data | postPayment(), save() |
避免: 在类名中使用 Manager、Processor、Data、Info——这些名称暗示职责不明确。
关键见解: 如果你需要用注释解释变量是什么,不如直接重命名它。
快速参考:函数
大小和范围
- 理想长度: 4-10 行,通常不超过 20 行
- 缩进层级: 最多一到两层
- 只做一件事 — 如果你能提取出另一个名称不重复描述原意的函数,那么当前函数做得太多了
参数
| 数量 | 建议 |
|---|---|
| 0 | 最佳 |
| 1 | 良好 |
| 2 | 可以接受 |
| 3+ | 避免——封装到对象中 |
标志参数(布尔值)很糟糕。 它们表明函数要做两件事。应将其拆分:
# Bad
def render(is_suite: bool): ...
# Good
def render_for_suite(): ...
def render_for_single_test(): ...
关键规则
- 命令查询分离: 要么执行操作,要么回答问题,不能两者兼做
- 无副作用: 如果
checkPassword()还会初始化会话,它就是在说谎 - 优先使用异常而非错误码: 将正常流程与错误处理分开
- 提取 try/catch 块: 错误处理是一件独立的事
快速参考:注释
注释充其量是必要之恶。正确使用注释,是为了弥补我们无法用代码清楚表达自己的不足。
删除以下注释
- 冗余注释 — 重述代码已经表达的内容
- 日志/变更记录 — 使用 git
- 注释掉的代码 — 这是糟糕的做法,git 会记住
- 噪声注释 —
// default constructor、// increment i - 右花括号注释 —
} // end if意味着嵌套层级太多
可以接受的注释
- 法律声明
- 解释意图(为什么,而不是什么)
- 提示后果(
// takes 30 minutes) - TODO(但要及时清理)
- 澄清外部库的行为
规则: 当你想要添加注释时,先尝试重构代码,让注释变得多余。
快速参考:错误处理
错误处理很重要,但如果它掩盖了逻辑,那就做错了。
| 规则 | 详情 |
|---|---|
| 优先使用异常,而不是返回码 | 将算法与错误处理分开 |
| 提供上下文 | 包含失败的操作和失败类型 |
| 封装第三方 API | 最大限度减少依赖,支持模拟 |
| 使用特例模式 | 返回能处理特例的对象(空列表、默认值) |
| 不要返回 null | 会带来额外工作,还可能引发 NullPointerException |
| 不要传入 null | 比返回 null 更糟——默认情况下禁止这样做 |
# Bad - null checks everywhere
if employees is not None:
for e in employees:
total += e.pay
# Good - return empty collection instead of null
for e in get_employees(): # Returns [] if none
total += e.pay
快速参考:类
单一职责原则(SRP)
一个类应该只有一个,而且只有一个,改变的理由。
检验:
- 你能为它取一个简洁的名称吗?(避免使用
Manager、Processor、Super) - 你能在不使用“如果”“和”“或”“但是”的情况下,用 25 个词描述它吗?
内聚性
方法应该使用类的实例变量。如果某些方法围绕特定变量聚集,而不使用其他变量,就应该拆分类。
开闭原则(OCP)
类应该对扩展开放,对修改关闭。通过子类化添加新行为,而不是修改现有代码。
依赖倒置原则(DIP)
依赖抽象,而不是具体细节。注入依赖以便于测试。
# Bad - can't test without network
class Portfolio:
def __init__(self):
self.exchange = TokyoStockExchange()
# Good - injectable, testable
class Portfolio:
def __init__(self, exchange: StockExchange):
self.exchange = exchange
快速参考:测试
TDD 三定律
- 在编写失败的测试之前,不要编写生产代码
- 编写的测试不要超过足以让测试失败的范围
- 编写的生产代码不要超过足以让测试通过的范围
F.I.R.S.T. 原则
- 快速(Fast) — 快速运行,这样你就能经常运行
- 独立(Independent) — 彼此之间不要相互依赖
- 可重复(Repeatable) — 在任何环境中结果都相同
- 自验证(Self-Validating) — 输出布尔结果(通过/失败)
- 及时(Timely) — 在编写生产代码之前立即编写
整洁的测试
- 可读性至关重要
- 使用 构建-操作-检查 模式
- 创建特定于领域的测试语言
- 每个测试一个概念(不一定只有一个断言)
警告: 测试代码和生产代码同样重要。如果任由测试腐坏,你的代码也会腐坏。
对象与数据结构
| 概念 | 隐藏 | 暴露 | 易于添加…… |
|---|---|---|---|
| 对象 | 数据 | 函数 | 新类型 |
| 数据结构 | 无 | 数据 | 新函数 |
认为一切都是对象的想法是个误区。 有时,你需要的是简单的数据结构,以及对其进行操作的过程。
迪米特法则
一个方法只能调用以下对象的方法:
- 类自身
- 它创建的对象
- 作为参数传入的对象
- 保存在实例变量中的对象
不要调用允许使用的方法所返回对象的方法(链式调用灾难):
# Bad
output_dir = ctxt.get_options().get_scratch_dir().get_absolute_path()
# Good - tell the object to do the work
bos = ctxt.create_scratch_file_stream(class_file_name)
最关键的代码异味
在第 17 章的完整列表中,以下是最重要的几项:
G5:重复
软件领域万恶之源。 每处重复都意味着错失了一次抽象机会:
- 相同代码 → 提取为函数
- 重复的 switch/if-else → 多态
- 相似算法 → 模板方法或策略模式
G30:函数应该只做一件事
如果你能从中提取出另一个函数,说明原函数做的不止一件事。
N1:选择描述性名称
名称决定了代码可读性的 90%。花时间慎重选择。
F1:参数过多
零个最好,其次是一个、两个、三个。更多参数则需要给出理由。
F3:标志参数
布尔参数意味着函数在做两件事。将其拆分。
G9:死代码
不会执行的代码。删除它——版本控制会记住。
G11:不一致
如果某件事采用一种方式处理,所有类似的事情也都应采用相同方式。
C5:注释掉的代码
令人厌恶。立即删除。
精工之道
“编写整洁的代码,需要有纪律地运用无数细小技巧,而这份纪律源于经过艰苦培养的‘整洁感’。代码感是关键。”
整洁的代码不是机械地遵循规则写出来的,而是源自推动我们践行规范的价值观——热爱精工、尊重代码的读者,并为专业的工作成果感到自豪。
怎样才能写出整洁的代码? 初稿总是笨拙的——函数很长、循环嵌套、名称随意、代码重复。你需要不断打磨:拆分函数、修改名称、消除重复、缩短方法。没人能从一开始就写出整洁的代码。
让软件正常运行与让代码保持整洁,是两项不同的工作。我们大多数人的脑力有限,所以会先专注于让代码正常运行。问题在于,我们当中有太多人认为,程序能运行就算完工了。 我们没有转而关注组织结构和整洁性,而是继续处理下一个问题,没有回头把臃肿的类拆分为彼此解耦的单元。
别这样做。回过头来。把代码整理好。让它比你接手时更好。