12.2. 好的代码可以自我记录

有人认为,如果代码编写得当,那么显而易见,不需要注释。这是一个美味的神话,就像谣言说冰淇淋对您的健康有益:我们真的很想相信!不幸的是,事实并非如此。可以肯定的是,在编写代码时可以做一些事情来减少对注释的需求,例如选择好的变量名(请参阅 第十四章:选择的名字 )。尽管如此,仍有大量设计信息无法用代码表示。例如,只能在代码中正式指定类接口的一小部分,例如其方法的签名。接口的非正式方面,例如对每种方法的作用或其结果含义的高级描述,只能在注释中描述。

一些开发人员认为,如果其他人想知道某个方法的作用,那么他们应该只阅读该方法的代码:这将比任何注释都更准确。读者可能会通过阅读其代码来推断该方法的抽象接口,但这既费时又痛苦。另外,如果在编写代码时期望用户会阅读方法实现,则将尝试使每个方法尽可能短,以便于阅读。如果该方法执行了一些重要操作,则将其分解为几个较小的方法。这将导致大量浅层方法。此外,它并没有真正使代码更易于阅读:为了理解顶层方法的行为,读者可能需要了解嵌套方法的行为。

此外,注释是抽象的基础。回顾 第四章:模块应该是深的 ,抽象的目的是隐藏复杂性:抽象是实体的简化视图,该实体保留必要的信息,但忽略了可以安全忽略的细节。如果用户必须阅读方法的代码才能使用它,则没有任何抽象:方法的所有复杂性都将暴露出来。没有注释,方法的唯一抽象就是其声明,该声明指定其名称以及其参数和结果的名称和类型。该声明缺少太多基本信息,无法单独提供有用的抽象。例如,提取子字符串的方法可能有两个参数,开始和结束,表示要提取的字符范围。仅凭宣言,无法确定提取的子字符串是否将包含 end 指示的字符,或者如果 start> end 会发生什么。注释使我们能够捕获调用者所需的其他信息,从而在隐藏实现细节的同时完成简化的视图。用人类语言(例如英语)写注释也很重要;这使它们不如代码精确,但提供了更多的表达能力,因此我们可以创建简单直观的描述。如果要使用抽象来隐藏复杂性,则注释必不可少。

Some people believe that if code is written well, it is so obvious that no comments are needed. This is a delicious myth, like a rumor that ice cream is good for your health: we’d really like to believe it! Unfortunately, it’s simply not true. To be sure, there are things you can do when writing code to reduce the need for comments, such as choosing good variable names (see Chapter 14). Nonetheless, there is still a significant amount of design information that can’t be represented in code. For example, only a small part of a class’s interface, such as the signatures of its methods, can be specified formally in the code. The informal aspects of an interface, such as a high-level description of what each method does or the meaning of its result, can only be described in comments. There are many other examples of things that can’t be described in the code, such as the rationale for a particular design decision, or the conditions under which it makes sense to call a particular method.

Some developers argue that if others want to know what a method does, they should just read the code of the method: this will be more accurate than any comment. It’s possible that a reader could deduce the abstract interface of the method by reading its code, but it would be time-consuming and painful. In addition, if you write code with the expectation that users will read method implementations, you will try to make each method as short as possible, so that it’s easy to read. If the method does anything nontrivial, you will break it up into several smaller methods. This will result in a large number of shallow methods. Furthermore, it doesn’t really make the code easier to read: in order to understand the behavior of the top-level method, readers will probably need to understand the behaviors of the nested methods. For large systems it isn’t practical for users to read the code to learn the behavior.

Moreover, comments are fundamental to abstractions. Recall from Chapter 4 that the goal of abstractions is to hide complexity: an abstraction is a simplified view of an entity, which preserves essential information but omits details that can safely be ignored. If users must read the code of a method in order to use it, then there is no abstraction: all of the complexity of the method is exposed. Without comments, the only abstraction of a method is its declaration, which specifies its name and the names and types of its arguments and results. The declaration is missing too much essential information to provide a useful abstraction by itself. For example, a method to extract a substring might have two arguments, start and end, indicating the range of characters to extract. From the declaration alone, it isn’t possible to tell whether the extracted substring will include the character indicated by end, or what happens if start > end. Comments allow us to capture the additional information that callers need, thereby completing the simplified view while hiding implementation details. It’s also important that comments are written in a human language such as English; this makes them less precise than code, but it provides more expressive power, so we can create simple, intuitive descriptions. If you want to use abstractions to hide complexity, comments are essential.