软件设计

KISS 原则:把代码写简单的真正含义

开发时,有时会莫名觉得代码写得越复杂,才越显得自己厉害。

4 分钟阅读
KISS 原则:把代码写简单的真正含义 封面图

开发时,有时会莫名觉得代码写得越复杂,才越显得自己厉害。

大量加入设计模式,层层堆叠抽象层,还会想着“以后可能需要扩展”,提前把接口挖好。

但几年后重新打开那份代码,你会发现真正受苦的是未来的自己。

今天我们来聊聊站在另一端的原则:KISS。

先说结论,KISS 是“Keep It Simple, Stupid”的缩写。直译大概就是“保持简单,笨蛋”。

这是一种设计理念:系统不是做得复杂时运行得最好,而是保持简单时才运行得最好。


KISS 原则是从哪里来的?

这句话原本并不是软件术语。

普遍认为,这句话是 20 世纪 60 年代由美国洛克希德公司的航空工程师 Kelly Johnson 提出的。他设计了著名的 U-2 和 SR-71 Blackbird 侦察机。

据说 Kelly Johnson 对团队提出了这样的要求。

制造一架在战斗情况下,普通维修人员只用基本工具就能修好的飞机。

无论设计多么出色,如果无法在现场维修,就没有意义。

这种精神后来延伸到了软件领域。代码再怎么巧妙,如果同事无法阅读和修复,就不能算是好代码。

顺便说一句,“Stupid”并不是在叫开发者笨,而是更接近“简单到任何人都能理解”的语气。


代码中 KISS 被破坏的时刻

说起来容易,但在实际代码中是什么样子?下面介绍几种我经常见到的模式。

1. 看起来很厉害的一行代码

// 这样写看起来很聪明
let result = arr.reduce([Int: Item]()) { $0.merging([$1.id: $1]) { _, new in new } }

// 这样更容易阅读
var result = [Int: Item]()
for item in arr {
    result[item.id] = item
}

上面的代码虽然很短,但第一次看到的人要盯着看很久。下面的代码任谁都能在 3 秒内理解。

2. 永远用不到的可扩展性

比如只创建一个按钮,却连工厂模式和策略模式都用上。原因是担心“以后按钮类型增加怎么办?”。但这个“以后”大多数时候并不会到来。

3. 条件语句迷宫

if 里面套 if,里面又套一个 if。只嵌套三层,脑内栈就快要溢出了。这时只要用 early return 展开代码,就能让它大幅简化。

复杂代码和简单代码,6 个月后就能看出差别
复杂代码和简单代码,6 个月后就能看出差别

遵守 KISS 的 3 个实战标准

所以我写代码时,会用这三点作为标准。

标准 问题
说明测试 我能在 1 分钟内向旁边的同事解释这段代码吗?
未来测试 6 个月后的我看到它,也能马上理解吗?
必要性测试 这段代码是因为现在就需要才加入的,还是因为觉得以后可能会用到才加入的?

只要有一项回答“不能”,我就会重新思考是否存在更简单的方法。

尤其是第三点很重要,它也符合 YAGNI(You Aren’t Gonna Need It)原则。KISS 和 YAGNI 基本上总是成套运作。


简单和粗制滥造不是一回事

这里有一点不能误解。

KISS 并不是“不要思考,随便写”的意思,恰恰相反。

把事情变复杂很容易,因为只要把想到的东西全部加上去就行。把事情变简单却很难,因为必须认真思考该删掉什么。

Pascal 曾在信中留下过一句名言。

因为没有时间写得更短,所以我写得很长。

代码也是一样。简单代码不是敷衍的结果,而是努力去除复杂性的成果。

简单是不断删减和打磨的结果
简单是不断删减和打磨的结果

总结

KISS 原则总结如下。

  • 代码被阅读的时间远比编写的时间长。请为阅读代码的人,把代码写得简单。
  • 不要加入当前不需要的可扩展性。你担心的情况,实际发生的概率比想象中低得多。
  • 简单不是能力不足,而是能力的证明。

最近做代码评审时,我好像最常留下的评论就是:“这段代码不能再简单一点吗?”不可思议的是,仅仅这个问题就能同时减少 bug 和评审时间。

你的代码,旁边的同事能在 1 分钟内理解吗?今天不妨问问自己。

延伸阅读