<audio id="audio" title="29 | 编写经济代码的检查清单" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/1d/a3/1d3eb00043afcc10ddc1fca236fa32a3.mp3"></audio>

通过前面十几讲的学习，我们已经把代码“经济”篇的内容学习完了。今天，我们一起把前面讨论到的观点总结一下，并探索一下编写经济代码时的最佳实践检查清单。

## 为什么需要经济的代码？

我在[经济篇这一模块开始的时候](https://time.geekbang.org/column/article/81394)讲过这个问题，这里再来简单回忆一遍。

**1.提升用户体验**

一致性的性能体验，是软件产品赢得竞争的关键指标。复杂的，反应迟钝的软件，很难赢得用户的尊敬。

**2.降低研发成本**

通过降低软件的复杂度，提高软件的复用，提前考虑性能问题，可以降低软件研发成本，缩短软件开发周期。

**3.降低运营成本**

经济的代码可以降低软件的复杂度，提高计算资源的使用效率，降低运营成本。

**4.防范可用性攻击**

复杂的代码和性能低下的代码，更容易成为黑客攻击的目标。如果一个服务器，需要耗费很多资源才能处理一个请求，那么数量很少的模拟请求攻击，就可以导致服务器瘫痪。

## 怎么编写经济的代码？

既然我们都知道编写经济代码的重要性，那么如何让自己的代码经济又高效呢？

在前面的文章中，我给你从避免过度设计、选择简单直观、超越线程同步、减少内存使用、避免性能陷阱、规模扩张能力等角度探讨了一些方法，下面我提炼了几个点，我们再来重新温习一遍。

**1.避免过度设计**

我们从[需求和设计两个角度](https://time.geekbang.org/column/article/81899)探讨了代码的经济问题。

避免需求膨胀的方式主要有两个，第一个是识别核心需求，我们要从用户的角度出发，知道什么是核心需求，什么是衍生需求，什么是无效需求。就像建火车站一样，能够满足乘客出行需求的就是好的设计方案，其他方面再细心认真，起到的也只是锦上添花的效果。那么有一些功能现在好像用不上，但又必须做，该怎么办呢？这就用到了第二个方法：迭代演进，有所主次。

避免过度设计和避免需求膨胀一样，我们需要时刻问自己，什么是现在就必须做的？什么是必须做的？

搞清楚这两个问题，有助于我们始终关注核心需求和核心问题，为代码的质量和编码的效率打好基础。

避免需求膨胀和过度设计，是编写经济代码最需要注意的根基性问题。

**2.选择简单直观**

我们用了两篇文章，讨论了[让代码简单直观的原则和实践](https://time.geekbang.org/column/article/82335)。

设计一个简单直观的接口，首先，我们要从问题开始。把问题逐步拆解成一个个已经完全穷尽的小问题，这就是我讲到的“相互独立，完全穷尽”原则。在拆解的过程中，软件的接口与接口之间的关系会自然而然地产生。

此外我们还要注意，一个接口只应该做一件事情，如果这个情况太理想化，就要想办法减少接口的依赖关系。

一定记住这个经过实践检验的理念：选择最简单，最直观的解决方案。

**3.超越线程同步**

现实中，线程同步需要排队，有损效率。我们用了两篇文章，主要讲了[该怎么超越线程的同步](https://time.geekbang.org/column/article/82954)。

只要满足这三个条件中的一个，我们就不需要线程同步了：使用单线程；不关心共享资源的变化；没有改变共享资源的行为。

我们要重新认识Java的“final”这个限定词。使用了限定词“final”的类变量，只能被赋值一次，而且只能在实例化之前被赋值。这样的变量，就是不可变的量。如果一个类的所有的变量，都是不可变的，那么这个类也是不可变的。不可变的量是无法改变的资源，不需要线程同步。

如果线程同步不可避免，就要想办法减少线程同步时间。

另外，我们还讨论了如何使用同步的代码，调动异步的事件。异步编程，可以大幅度降低线程同步的使用，更有效地使用计算机资源。

**4.减少内存使用**

内存管理对任何一门编程语言来讲都是一个难题。我们用了两篇文章，讨论了[提高内存使用效率](https://time.geekbang.org/column/article/83045)的一些方法。

减少内存的使用主要有两个方法，第一个方法是减少实例的数量，第二个办法是减小实例的尺寸。

如何减少实例的数量呢？我们可以使用数据静态化的处理方式（比如枚举类型）、用单实例模式、延迟分配技术等。

在减小实例尺寸这一模块，我们要尽量减少独占的空间，尽量使用共享的实例。不可变（immutable）的资源和禁止修改（unmodifiable）的资源，是两类理想的共享资源。

**5.规避性能陷阱**

我们要学会规避一些[常见的性能陷阱](https://time.geekbang.org/column/article/84096)，比如字符串的操作、内存泄露、未正确关闭的资源和遗漏的hashCode等。

另外，我们还顺便使用了一个基准测试工具JMH，并通过它分析了一些性能陷阱。我们要有意识地使用一些性能测试工具，通过测试数据来认识、积累性能问题的最佳实践。

**6.规模扩张能力**

经济的代码需要跟得上产品的规模扩张。我们要理解规模垂直扩张和规模水平扩张这两种方式，特别是[支持规模水平扩张](https://time.geekbang.org/column/article/84488)。

状态数据是影响规模水平扩张能力的最重要的因素。分离无状态数据、提供无状态服务，减少有状态服务的规模，是提升规模水平扩张能力的最佳实践。

## 经济代码的检查清单

了解了编写经济代码的方法论之后，我们再来看下检查清单。这个检查清单是经济篇这一模块的凝练，也是我看代码的时候，通常会使用的检查点。你也可以参考一下。

如果有检查点没有通过，那么你在阅读代码的时候，就要集中注意力，深入分析；在设计和编写代码的时候，要花时间衡量、妥协、改进；在评审代码的时候，要问清楚为什么这么做，能不能有所改进，并且给出合理的建议。

### 需求评审

<li>
需求是真实的客户需求吗？
</li>
<li>
要解决的问题真实存在吗？
</li>
<li>
需求具有普遍的意义吗？
</li>
<li>
这个需求到底有多重要？
</li>
<li>
需求能不能分解、简化？
</li>
<li>
需求的最小要求是什么？
</li>
<li>
这个需求能不能在下一个版本再实现？
</li>

### 设计评审

<li>
能使用现存的接口吗？
</li>
<li>
设计是不是简单、直观？
</li>
<li>
一个接口是不是只表示一件事情？
</li>
<li>
接口之间的依赖关系是不是明确？
</li>
<li>
接口的调用方式是不是方便、皮实？
</li>
<li>
接口的实现可以做到不可变吗？
</li>
<li>
接口是多线程安全的吗？
</li>
<li>
可以使用异步编程吗？
</li>
<li>
接口需不需要频繁地拷贝数据？
</li>
<li>
无状态数据和有状态数据需不需要分离？
</li>
<li>
有状态数据的处理是否支持规模水平扩张？
</li>

### 代码评审

<li>
有没有可以重用的代码？
</li>
<li>
新的代码是不是可以重用？
</li>
<li>
有没有使用不必要的实例？
</li>
<li>
原始数据类的使用是否恰当？
</li>
<li>
集合的操作是不是多线程安全？
</li>
<li>
集合是不是可以禁止修改？
</li>
<li>
实例的尺寸还有改进的空间吗？
</li>
<li>
需要使用延迟分配方案吗？
</li>
<li>
线程同步是不是必须的？
</li>
<li>
线程同步的阻塞时间可以更短吗？
</li>
<li>
多状态同步会不会引起死锁？
</li>
<li>
是不是可以避免频繁的对象创建、销毁？
</li>
<li>
是不是可以减少内存的分配、拷贝和释放频率？
</li>
<li>
静态的集合是否会造成内存泄漏？
</li>
<li>
长时间的缓存能不能及时清理？
</li>
<li>
系统的资源能不能安全地释放？
</li>
<li>
依赖哈希值的集合，储存的对象有没有实现hashCode()和equals()方法？
</li>
<li>
hashCode()的实现，会不会产生撞车的哈希值？
</li>
<li>
代码的清理，有没有变更代码的逻辑？
</li>

## 小结

编写经济的代码，是我们在编程入门之后，需要积累的一项重要技能。正是因为要考虑性能、安全等因素，编写代码才成了一个具有挑战性的工作。

如果我们有以下这两个好习惯，那么编写经济的代码的能力就会越来越强大。

第一个习惯是，要尽早地考虑性能问题。如果你最早接触的是需求制定，就从需求开始考虑；如果你最早接触的是软件架构，就从架构层面开始考虑；如果你最早接触的是软件设计，就从软件设计开始考虑；如果你最早接触到的是代码，代码也有很多性能问题可以考虑。总之，要主动、尽早地考虑效率问题。

第二个习惯是，性能的实践经验需要日积月累。性能的实践经验和技术丰富繁杂，大到产品蓝图，小到每一行代码，中间还有软件的架构、选型、部署等诸多环节，都有很多的最佳实践可以积累。而且这些最佳实践，也会随着时间的推移发生变化，比如说会出现更好的技术方案，曾经的技术满足不了新需求等。所以，我们也要随时更新我们的储备，摒弃过时的经验。

希望你根据自己的实际情况，不断修改、完善、丰富上面的清单，让这份清单更契合你自己的工作领域。

## 一起来动手

不同的场景，检查清单也不一定相同。我上面的清单，就没有考虑数据库和Web服务架构。如果让你列一个你实际工作中需要的，编写经济代码的检查清单，会是什么样子的？ 你可以在我上面的清单上加减检查点，或者新做一个列表。欢迎在留言区公布你的检查清单，我们一起来讨论、学习。

另外，推荐一本书《重新定义公司——谷歌是如何运营的》。如果你没有时间，看看随书附带的小册子也行。这本书，谈的虽然是公司运营，但是我们可以也从中学习到如何设计优秀的产品，如何编写优秀的代码的一些基本思想。

推荐的另外一本书是《Effective Java》。建议找找最新的版本（现在是第三版）。这本书里，有很多非常实用的小经验，每一个小经验都讲得深入又透彻。是一本Java程序员必备的好书。

如果你觉得这篇文章有所帮助，欢迎点击“请朋友读”，把它分享给你的朋友或者同事。


