谷歌代码健康指南

要点:

  1. 谷歌对待代码的态度

  2. 什么是易理解的代码

  3. 好的接口设计很难被误用

  4. 不要痴迷于原始类型

1

 

谷歌对待代码的态度

 

良好的编码实践包含许多方面,但并不是任何代码问题都能由检查工具发现。

我们关心软件工程过程与实践。无论软件是如何编写的,都可能影响代码的可读性、可维护性、稳定性和简单性。我们将上面提到的这些方面定义为“代码健康”。

你如何确保工程师遵循这些实践,同时仍然允许他们独立做出合理的工程决策呢?

我们所做的工作是致力于改善工程师的日常工作状态,使他们能够以更短的迭代时间开发代码、减少开发工作、提高稳定性和改进性能。

每个人都希望他们的代码变得更容易理解,他们的代码库变得更简单。因为我们都知道,这些东西能让我们更快前进,制造更好的产品。

这并不是说我们对谷歌的工程实践有严格规定。我们仍然鼓励工程师对其项目做出自己认为最明智的决定。

因此,为了帮助每个人,我们计划在未来几周和几个月内发布文章,详细介绍具体的实践,我们鼓励在任何地方应用内部实践,以帮助你和你的团队!

 

 

2

什么是易理解的代码

 

当你看不懂别人提交给你审核的代码是什么意思时,你也许很容易会产生有一种感觉,觉得提交者比您更聪明。但实际上,如果这段代码很难理解,那么,很可能是它过于复杂了。假如您熟悉它所使用的编程语言,那么阅读健康的代码几乎就像用您的母语阅读一本书一样简单。

假设一个开发人员发给你下面这段Python代码,请你做代码审核,

 

尝试理解它,如果理解它的时间超过几秒钟,那就是一种坏味道。此时,你只需要添加一个代码评论,说"我很难理解这段代码”,或者更具体一点,比如“请在这里使用更具描述性的命名”。

再过一会,提交者可能已经修改了代码,并将其再次发送给您审核:

现在我们可以很容易地阅读它,这本身就是一个好处。

通常,只需要要求开发人员写出清晰的代码,就会带来根本性的改进。在上面的例子中,开发人员意识到了可能的改进,因为修改后的代码更容易理解——当数字不是素数时,函数可以更早返回了,并且循环变成为 n / 2 ,而不是 n。

现在我们可以很容易地理解这段代码了,还可以看到其中的很多问题。 

例如,对于 0 和 1 ,该函数会有奇怪行为,当然还有其他一些问题。

但最重要的是,现在显而易见的是,应该删除整个函数,因为已经有预先定义好的函数库提供了这个函数所要实现的功能,即:检测数字是否为素数的函数。

澄清代码有助于开发人员和审阅者。

总之,不必浪费时间审查难以理解的代码,要求提交者将其澄清就好了。 

 

事实上,这样的评论是代码评审员最有用和最重要的工具之一!

 

 

3

 

好的接口设计很难被误用

 

 

我们都试图避免代码中的错误。那么,使用你写的接口的调用者创建的错误呢?

良好的接口设计可以使调用者轻松做正确的事情,并且让调用者很难做错事。不要将你的代码责任推给调用者。

下面的代码中,你看出问题来了么?

 

如果调用者忘记了调用AddSlots(),当Insert()被调用时,就可能会发生不可预期的行为。因此,不要将复杂性通过接口推给它的调用者,不要将实现细节暴露给了调用者。

在这个类中, slots 的维护职责与调用者的可视行为无关。因此,不要将其暴露给外界。通过将增加席位的功能放在Insert()函数中,使调用者不能触发不可预期的行为。

 

由编译器强制对协议(Contracts)进行检查,通常比运行时强制进行协议检查更好。更糟糕的情况是,调用者必须查看说明文档中的协议描述以后,才能正确的调用这个接口,执行正确的操作。

下面是另外一些容易发生接口误用的情况:

  • 要求调用者调用一个初始化函数(替代方法:公开使对象完全初始化的工厂方法)。

  • 要求调用者执行定制化的清理任务(替代方法:使用特定于语言的构造,以确保在对象超出范围时自动清理)。

  • 允许创建没有必需参数对象的代码实现(例如,没有ID的用户)。

  • 限制参数的取值范围,尤其是当该参数可以使用多种类型时(例如,使用Duration timeout, 而不是int timeout_in_millis)

我们可能无法 百分之百地保证,所有的接口都能设计得万无一失。

但在某些情况下,由于某些要求无法在接口中表达,此时必须做依赖静态分析或者写好文档(例如,某个回调函数必须是线程安全的)。

不要强制执行你不需要强制执行的操作,避免使用过于防御性的代码。

 

例如,功能参数的广泛验证会增加复杂性,并且可能会降低性能。

 

4

 

不要痴迷于原始类型

 

编程语言提供基本类型,例如整数,字符串和Map,这些类型在许多上下文中都很有用。例如,字符串可用于保存从人名到网页URL的所有内容。但是,过于依赖基本类型而非自定义抽象的代码(如Java语言中的Class)可能难以理解和维护。

 

原始类型的痴迷是指过度使用基本(“原始”)类型来表示更高层次的概念。例如,下面的代码使用基本类型来表示形状:

 

pair 在这里并不是一个正确的抽象层次,因为它的一般命名 first 和 second两个参数被用于表示 X 和 Y,其中一处被用做标识 lower-left (或者, upper-left?) 而另外一处用作 upper-right (或, lower-right?) 。更糟糕的是,基本类型并没有对领域专属的代码进行封装,例如边框(bounding box)和面积(area)。

 

用更高级别的抽象可以产生更清晰且封装性更好的代码

 

 

下面是一些原始类型痴迷的例子:

 

  • maps, lists, vectors等类型可以很方便地成为一个容器集,将值合并到自定义的更高级抽象中。

 

  • 包含魔术键值的 vector 或 map ,例如 用 0,1 和 2 分别代表 name ,  address, and phone #,应该将其放入到更高一级的抽象中。

 

  • 用一个字符串表示复杂或结构化的文本(例如一个日期)。更好的做法是使用更高层的抽象(如Date类型) ,它还提供自我说明的访问函数(例如 GetMonth ) ,并且还保证正确性。

 

  • 用一个整型或浮点数表过一个时间值,例如多少秒。更好的做法是使用结构化的时间戳 (timestamp) 或 时间段类型(duration)。

 

对于某个任务来说,任何类型都有可能过于原始,从最原始的int,到复杂的红黑树。

如果你看到代码使用了很多的基础类型,那么,使用更高级别的抽象更能够表达清楚,封装性也会更好。

 

你应该把它重构了,或者礼貌地提醒作者要 保持优雅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值