代码整洁之道 pt2(第4章-第6章)

  • 4 注释
    • 4.1 注释不能美化糟糕的代码
    • 4.2 用代码来阐述
    • 4.3 好注释
      • 4.3.1 法律信息
      • 4.3.2 提供信息的注释
      • 4.3.3 对意图的解释
      • 4.3.4 阐释
      • 4.3.5 警示
      • 4.3.6 TODO注释
      • 4.3.7 放大
      • 4.3.8 javadoc
    • 4.4 坏注释
      • 4.4.1 喃喃自语
      • 4.4.2 多余的注释
      • 4.4.3 误导性注释
      • 4.4.4 循规式注释
      • 4.4.5 日志式注释
      • 4.4.6 废话注释
      • 4.4.7 可怕的注释
      • 4.4.8 能用函数或变量时就别用注释
      • 4.4.9 位置标记
      • 4.4.10 括号后面的注释
      • 4.4.11 归属与署名
      • 4.4.12 注释掉的代码
      • 4.4.13 HTML注释
      • 4.4.14 非本地信息
      • 4.4.15 信息过多
      • 4.4.16 不明显的联系
      • 4.4.17 函数头
      • 4.4.18 非公共代码中的javadoc
  • 5 格式
    • 5.1 格式的目的
    • 5.2 垂直格式
      • 5.2.1 向报纸学习
      • 5.2.2 概念间垂直方向上的区隔
      • 5.2.3 垂直方向上的靠近
      • 5.2.4 垂直距离
      • 5.2.5 垂直顺序
    • 5.3 横向格式
      • 5.3.1 水平方向上的区隔与靠近
      • 5.3.2 水平对齐
      • 5.3.3 缩进
      • 5.3.4 空范围
    • 5.4 团队规则
  • 6 对象和数据结构
    • 6.1 数据抽象
    • 6.3 德墨忒尔律
      • 6.3.1 火车失事
      • 6.3.2 混杂
    • 6.4 数据传送对象
      • 6.4.1 Active Record
    • 6.5 总结

4 注释

什么也比不上放置良好的注释来的有用。什么也不会比乱七八糟的注释更有本事搞乱一个模块。什么也不会比陈旧、提供错误信息的只是更有破坏性。

只有代码能忠实地告诉你它在做的事。那是唯一真正准确的信息来源。所以尽管有时也需要注释,但是也该多花心思尽量减少注释量。

4.1 注释不能美化糟糕的代码

写注释的常见动机之一是糟糕的代码的存在。

带有少量注释的症结而又表达力的代码,要比带有大量注释的零碎而复杂的代码像样得多。与其花时间编写解释你写出的糟糕的代码的注释,不如花时间清理那堆糟糕的代码。

4.2 用代码来阐述

// Check to see if the employee is eligible for full benefits
if ((employee.flags && HOURLY_FLAG) && (emplee.age > 65))
if (employee.isEligibleFullBenefits())

比较上面两种写法,明显下面一种写法会更规范。很多时候,简单到只需要创建一个描述了与注释所言同一事物的函数即可。

4.3 好注释

有些知识是必需的,也是有利的。不过,唯一真正好的做法是你想办法不去写注释。

4.3.1 法律信息

有时,公司代码规范要求编写与法律相关的注释。例如版权及著作权声明等。

// Copyright (c) 2003,2004,2005 By Object Mentor,....
// ....

这类注释不应是合同或法典,可以注释中指向一份标准许可或其他外部文档,而不必把所有条款都放到注释中。

4.3.2 提供信息的注释

有时,用注释来提供基本信息也有其用处。例如用在解释某个抽象方法的返回值等。

4.3.3 对意图的解释

有时,注释不仅提供了有关实现的有用信息,而且还提供了某个决定后面的意图。

4.3.4 阐释

有时,注释把某些晦涩难明的参数或返回值的意义翻译为某种可读形式,也是有用的。但通常,更好的方法是尽量让参数或返回值本身就足够清楚;但如果参数或返回值是某个标准库的一部分,或者你不能修改的代码,帮助阐释其含义的代码就会很有用。

4.3.5 警示

有时,用于竟是其他程序员可能会出现某种后果的注释也是有用的。

4.3.6 TODO注释

有时,有理由用//TODO行驶在源代码中放置要做的工作列表。

TODO是一种程序员认为应该做,但由于某些原因目前还没做的工作,它可能是要提醒删除某个不必要的特性,或是要求他人注意某个问题等,但无论TODO的目的如何,他都不是在系统中留下糟糕代码的借口。

4.3.7 放大

注释可以用来放大某种看来不合理之物的重要性。

4.3.8 javadoc

javadoc是一款描述良好的API。

4.4 坏注释

大多数注释都属此类。通常坏注释都是糟糕的代码的支撑或借口,或者是对错误决策的修正,基本上等于程序员自说自话。

4.4.1 喃喃自语

如果只是因为你觉得应该或者因为过程需要就添加注释,那就是无谓之举。如果决定写注释,就要花必要的时间确保写出最好的注释。

唯有检视系统其他部分的代码,弄清事情原委。任何迫使读者查看或其它模块的注释,都没能与读者沟通好,不值所费。

4.4.2 多余的注释

对于多余的注释,读它并不比读代码更容易,不如代码精确,误导读者接受不精确的信息,而不是正确地理解代码。

4.4.3 误导性注释

有误导性的信息,放在比代码本身更难阅读的注释里面,有可能会影响导致其他阅读者和使用者。

4.4.4 循规式注释

所谓每个函数都要有javadoc或每个变量都要有注释的规矩全然是愚蠢可笑的。这类注释突然让代码变得散乱,满口胡言,令人迷惑不解。

4.4.5 日志式注释

日志式注释即有人在每次编辑某块代码时,记录每次修改的日志。这种冗长的记录只会让代码模块变的凌乱不堪,应当全部删除。

4.4.6 废话注释

废话注释会使我们读代码时,不自主的将注意力停留在它们上面,当代码修改之后,这类注释就变作了谎言一堆。用整洁的代码替代废话注释会更好。

4.4.7 可怕的注释

/** The name **/
private String name;
/** The version **/
private String version;
/** The licenceName **/
private String licenceName;
/** The version **/
private String info;

一股脑的复制粘贴不加思考,提供错误的信息的注释就是可怕的注释。

4.4.8 能用函数或变量时就别用注释

// does the module from the global list <mod> depend on the subsystem we are part of?
if (smodule.getDependSubsystem().contains(subSysMod.getSubSystem()))

例如,以上注释可以通过变量改变为无注释版本:

ArrayList moduleDependees = smodule.getDependSubsystem();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem))

4.4.9 位置标记

// Actions //

把特定函数放置在这种标记栏下,多数时候实属无理,鸡零狗碎,理当删除—特别是尾部无意义的斜杠

如果标记栏不多,他就会显而易见。所以,尽量少用标记栏,只在特别有价值的时候用。如果滥用就会在无背景的噪音中被忽略。

4.4.10 括号后面的注释

在括号后面放置特殊的注释,尽管对于含有深度嵌套的长函数可能有意义,但只会对函数本身的结构带来混乱。

4.4.11 归属与署名

源代码控制系统非常善于记住是谁在何时添加了什么,没必要用署名签名注释搞乱了代码。

4.4.12 注释掉的代码

直接把代码注释掉是讨厌的做法,别这样干。

将代码注释掉只会给后来的人增加复杂、毫无意义的工作量,代码控制系统完全可以帮我们记住不要的代码。

4.4.13 HTML注释

源代码注释中的HTML标记是一种讨厌的东西。

4.4.14 非本地信息

**假如一定要写注释,请确保它描述了离他最近的代码。**别在本地注释的上下文环境中给出系统级的信息。

4.4.15 信息过多

别再注释中添加又取得历史性话题或无关的细节描述。

4.4.16 不明显的联系

注释及其描述的代码之间的来呢西应该显而易见,如果你不嫌麻烦要写注释,至少让读者能看到注释和代码,并且理解注释所谈何物。

4.4.17 函数头

短函数不需要太多描述。为只做一件事的短函数选个好名字,通常要比写函数头注释好。

4.4.18 非公共代码中的javadoc

虽然Javadoc对于公共API非常有用,但对于不打算做公共用途的代码就令人厌恶了。

5 格式

应该保持良好的代码格式,应该选用一套管理代码格式的简单规则,然后贯彻这些规则。
团队中应该一致同意采用一套简单的个事规则,所有成员都应该遵循这套规则。

5.1 格式的目的

代码格式很重要。代码格式不可忽略,必须严肃对待。代码格式关乎沟通,而沟通是专业开发者的头等大事。

功能代码随时可能会被修改,但代码的可读性却会对以后可能发生的修改行为产生深远影响。
原始代码修改之后很久,其代码风格和可读性仍会影响代码的可维护性和可扩展性。

5.2 垂直格式

短文件通常比长文件易于理解

5.2.1 向报纸学习

源文件应该像报纸一样,名称简单一目了然(名称本身应该足以告诉我们是否在正确的模块中)。源文件最顶部应该给出高层次的概念和算法,细节向下渐次展开,直到找到原文件中最底层的函数和细节。

5.2.2 概念间垂直方向上的区隔

几乎所有代码都是从上往下读,从左往右读。每行展现一个表达式或一个子句,每组代码行展示一条完整的思路。这些思路用空白行区隔开来。

5.2.3 垂直方向上的靠近

空白行隔开了概念,靠近的代码行则暗示了他们之间的紧密关系。

5.2.4 垂直距离

关系密切的概念应该互相靠近

  • 避免使用protected变量,不要把关系模切的概念放到不同的文件中。
  • 对于关系密切、放置于同一源文件中的概念,它们之间的区隔应该成为对彼此的易懂度影响有多重要的衡量标准。
  • 应该避免迫使读者在源文件和类中跳来跳去。

变量声明—变量声明应尽可能靠近其使用位置。

  • 因为函数很短,本地变量应该在函数的顶部出现。
  • 循环中的控制变量应该总是在循环语句中声明。
  • 在较长函数中,变量也可能在某个代码块顶部,或在循环之前声明。

实体变量—实体变量应该在累的顶部声明。

  • 一般不会增加变量的垂直距离,因为在设计良好的类中,他们如果不是被该类的所有方法所用,也会被大多数方法所用。
  • 在C++中实体变量一般放在底部,但在Java中,惯例是放在类的顶部。

相关函数—若某个函数调用了另外一个,则应该把他们放到一起,而且调用者应该尽可能放在被调用者上面。

  • 概念相关。概念相关的代码应该放在一起。代码的相关性越强,彼此之间的距离就该越短。

5.2.5 垂直顺序

一般而言,我们想自上向下展示函数调用依赖顺序。被调用的函数应该放在执行调用的函数下面。

像报纸文章一般,我们期望最重要的概念先出现,并期望以包括最少细节的方式表述它们,而期望底层细节最后出现。

5.3 横向格式

应该尽力保持代码的行短小。死守80个字符的上线有些僵化,代码宽度可以达到100字符或120字符,再多就不合适了。

5.3.1 水平方向上的区隔与靠近

使用空格将彼此尽力相关的事物连接到一起,也用空格字符把相关性较弱的事物隔离开。

  • 在赋值操作符周围加上空格字符,以达到强调的目的。
int lineSize = line.length();
  • 不在函数名和左圆括号之间加空格,这是因为函数与其参数密切相关,如果隔开,就会显得互无关系。把函数调用括号中的参数一一隔开,强调逗号,表示参数是互相分离的。
private void measureLine(String line, int lineCount) {...
}
  • 乘法之间不加空格,因为乘法具有较高优先级;加减法运算项之间用空格隔开,因为加法和减法的优先级较低。
private static double determinant(double a, double b, double c) {return b*b - 4*a*c;
}

5.3.2 水平对齐

Java中的代码水平对齐应该是这样的

public class FitNesseExpediter implements ResponseSender {private Socket socket;private InputStream input;private OutputStream output;private Request request;private Response response;protected Long requestParsingTimeLimit;...
}

而非

public class FitNesseExpediter implements ResponseSender {private    Socket              socket;private  InputStream     input;private   OutputStream    output;private  Request             request;private     Response            response;protected Long                     requestParsingTimeLimit;...
}

5.3.3 缩进

要想让代码的范围式继承结构课件,需要依据源代码行在继承结构中的位置对代码行做缩进处理。

对文件顶层的语句(例如类声明),根本不缩进。类中的方法相对该类缩进一个层级,方法的实现相对方法声明缩进一个层级。代码块的实现相对于其容器代码块缩进一个层级,以此类推。

//bad code 没有缩进时
public void delete(Page page) {try { deletePageAndAllReferences(page); } catch (Exception e){ logError(e); }}
// 加了缩进后
public void delete(Page page) {try {deletePageAndAllReferences(page);} catch (Exception e){logError(e);}
}

违反缩进规则。对于短小的if语句、while循环或小函数中也不应该违反缩进规则,一旦这么做了节能会出现范围层级坍塌到一行的情况。

// bad code
public void delete(Page page) {try {deletePageAndAllReferences(page);} catch (Exception e){ logError(e); }
}

5.3.4 空范围

有时,while或for语句的语句体为空,也需要确保空范围体的缩进,并用括号包围起来。

//bad code
while (dis.read(buf, 0, readBufferSize) != -1);

5.4 团队规则

  • 每个程序员都有自己喜欢的格式规则,但如果在团队中工作,就是团队说了算。
  • 一组开发者应当认同一种格式风格,每个成员应该采用大家都认同的那种风格。
  • 好的软件系统是由一系列读起来不错的代码文件组成的。它们需要拥有一致和顺畅的风格。
  • 绝对不要用各种不同的风格来编写源代码,这样会增加代码的复杂度。

6 对象和数据结构

将一个变量设置为私有(private)有一个理由:不想让别人依赖这些变量,我们还可以自由修改器类型个实现。

但为什么还会给私有变量添加赋值器和取值器,让它们如同公共变量一般呢?

6.1 数据抽象

即便变量是私有的,但通过变量取值器和赋值器使用私有变量,则其实现便被暴露了。

隐藏实现并非只是在变量之间加上一个函数层。隐藏实现关乎抽象!类并不简单地用取值器和赋值器将其变量推向外界,而是暴露抽象接口,以便用户无需了解数据的实现就能够操作数据本体。

以两个抽象方法为例

public interface Vehicle {double getFuelTankCapacityInGallons();double getGallonsOfGasoline();
}
public interface Vehicle {double getPercentFuelRemaining();
}

前者采用聚香手段与机动车的燃料层同通信,而后者采用百分比抽象,能确定前者都是一些变量存取器,而无法得知后者中的数据形态。

不应该暴露数据细节,而应该以抽象形态表述数据。但并不意味着只是用接口或赋值器、取值器就万事大吉。要以最好的方式呈现某个对象包含的数据,需要严肃的思考。

随意乱加取值器和赋值器是最坏的选择。

6.2 数据、对象的反对称性

对象与数据结构:对象把数据隐藏于抽象之后,暴露操作数据的函数;而数据结构暴露其数据,没有提供有意义的函数。但这两种定义的本质其实是对立的。

对象与数据结构之间的二分原理
过程式代码(使用数据结构的代码) 便于在不改动既有数据结构的前提下添加新函数,面向对象代码便于在不改动既有函数的前提下添加新类。
反过来同理
过程式代码难以添加新数据结构,因为必须修改所有函数;面向对象代码难以添加新函数,因为必须修改所有类。

所以,面向对象难以完成的事,对于过程式代码却较容易,反之亦然。

  • 对于想要添加新数据类型而不是新函数的时候,对象和面向对象就比较合适。
  • 对于想要添加新函数而不是数据类型的时候,过程式代码和数据结构就更适合。

6.3 德墨忒尔律

模块不应了解它所操作操作对象的内部情形。对象应该隐藏数据,暴露操作。不应通过存储器暴露其内部结构。

德墨忒尔律认为,类C的方法f只应该调用以下对象的方法:

  • C;
  • 由f创建的对象;
  • 作为参数传递给f的对象;
  • 由C的是实体变量持有的对象;

只跟朋友谈话,不与陌生人谈话:方法不应调用由任何函数返回的对象方法。

以下方法违反了该定律,因为他调用了getOptions()返回值的getScratchDir()方法,又调用了getScratchDir()返回值的getAbsolutePath()方法。

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

6.3.1 火车失事

连串的调用通常被认为是肮脏的风格,这类代码通常被称为火车失事,应该避免。

上述问题最好做类似如下的切分:

Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePayh();

6.3.2 混杂

混杂结构,即一半是对象,另一半是数据结构。这种结构拥有执行操作的函数,也有公共变量或公共访问器及改值器。

无论处于怎样的初衷,公共访问器及改值器都把私有变量公开化,诱导外部函数以过程式程序使用数据结构的方式使用这些变量。

混杂增加了添加新函数的难度,也增加了添加新数据结构的难度,应该避免创造这种结构。

6.4 数据传送对象

最为精炼的数据结构,是一个只有公共变量、没有函数的类。这种数据结构有时被称为数据传送对象(DTO)。

DTO是非常有用的结构,尤其是在与数据库通信或解析套接字传递的消息之类的场景中,在应用程序代码里一系列将原始数据数据转换为数据库翻译过程中,他们往往是排头兵。

6.4.1 Active Record

Active Record是一种特殊的DTO形式。他们是拥有公共变量的数据结构,但通常也会拥有类似save和find这样可浏览方法。一半是对数据库表或其他数据源的直接翻译。

6.5 总结

对象暴露行为,隐藏数据,便于添加新对象类型而无须修改既有行为,同时难以在既有对象中添加新行为;数据结构报录数据,没有明显的行为,便于向既有数据结构添加新行为,同时难以向既有函数添加新数据结构。

在任何系统中,我们有时会希望能够灵活地添加新数据类型,所以更喜欢在这部分使用对象。另一些时候我们希望能灵活地添加新行为,这是我们更喜欢使用数据类型和过程。

【苦练基本功】代码整洁之道 pt2(第4章-第6章)相关推荐

  1. 【苦练基本功】代码整洁之道 pt1(第1章-第3章)

    代码整洁之道 pt1(第1章-第3章) 1 整洁代码 1.1 要有代码 1.2 糟糕的代码 1.3 混乱的代价 1.3.1 什么是整洁代码? 2 有意义的命名 2.1 名副其实 2.2 避免误导 2. ...

  2. 【苦练基本功】代码整洁之道 pt4(第10章-第12章)

    代码整洁之道 pt4(第10章-第12章) 10 类 10.1 类的组织 10.2 类应该短小 10.2.1 单一权责原则 10.2.2 内聚 10.2.3 保持内聚性就会得到许多短小的类 10.3 ...

  3. 【苦练基本功】代码整洁之道 pt3(第7章-第9章)

    代码整洁之道 pt3(第7章-第9章) 7 错误处理 7.1 使用异常而非返回码 7.2 先写try-catch-finally 7.3 使用未检异常 7.4 给出异常发生的环境说明 7.5 依调用者 ...

  4. 【好书推荐】你想要的编码规范都在这里 | 《代码整洁之道》

    目录 一.引言 二.书籍简介 三.好代码自己会说话 1. 清晰的变量命名规范 2. 好注释与坏注释 3. 错误处理 四.总结 一.引言 你好,我是小雨青年,一名程序员. 今天为你推荐的书籍是<代 ...

  5. 重读【代码整洁之道】

    一.前言 [代码整洁之道]很经典,但也有些过时,翻译上也有些啰嗦,但总体上是好书.通过对本书核心内容的摘抄,结合自己的经验,整理了一些精简的点,这样你就省的去啃那本400多页的书了. 软件质量 = 架 ...

  6. 《代码整洁之道》(Clean Code)- 读书笔记

    一.关于Bob大叔的Clean Code <代码整洁之道>主要讲述了一系列行之有效的整洁代码操作实践.软件质量,不但依赖于架构及项目管理,而且与代码质量紧密相关.这一点,无论是敏捷开发流派 ...

  7. 代码整洁之道(一)最佳实践小结

    摘要: Any fool can write code that a computer can understand. Good programmers write code that humans ...

  8. 2015年第11本:代码整洁之道Clean Code

    前一段时间一直在看英文小说,在读到<Before I fall>这本书时,读了40%多实在看不下去了,受不了美国人啰啰嗦嗦的写作风格,还是读IT专业书吧. 从5月9日开始看<代码整洁 ...

  9. 《代码整洁之道:程序员的职业素养》一一1.5 参考文献

    本节书摘来自异步社区出版社<代码整洁之道:程序员的职业素养>一书中的第1章,第1.5节,作者:[美]Robert C. Martin(罗伯特 C. 马丁),更多章节内容可以访问云栖社区&q ...

最新文章

  1. mysql范式与反范式_给女同事讲解MySQL数据库范式与反范式,她直夸我“技术好”...
  2. 安卓市场官方版_尖端实用的安卓手机APP推荐 是你们想要的!
  3. JSON.parse(text[, reviver])
  4. SAP激活物料分类帐
  5. FTP的dos命令使用方法
  6. 公钥(Public Key)与私钥(Private Key)
  7. Nginx的反向代理 和 负载均衡
  8. linux c++ 运行时报 段错误 的一个原因
  9. Flask-SQLAlchemy 中如何不区分大小写查询?
  10. 3.过滤——相关滤波(Correlation Filtering)_3
  11. DNN的Friendly URL剖析及应用
  12. python 调用gpu算力_教你免费使用百度云GPU算力提交深度学习任务
  13. Gradle与Makefile构建工具的对比
  14. 前端技术规划与战略:2022
  15. 各类数据库数字与文本转换
  16. 6种以太坊(ETH)钱包的基本介绍
  17. p2p sdk接收文件服务端epoll模型源码
  18. 线性回归模型(Linear regression model)
  19. JetBrains.ReSharper 字典
  20. 路由器CFE-固件-NVRAM

热门文章

  1. c语言iota函数,C++ iota函数用法详解
  2. 巨帧(jumbo frame)
  3. 基于无人机倾斜摄影模型提取高精度地形DEM数据
  4. 生产服务器变慢了排查思路
  5. Mysql 与 python 的使用
  6. Trajectory Generation of a Quadrotor Transporting a Bulky Payload in the Cluttered Environments 论文解析
  7. E站账号cookie分享_产品经理必懂知识点—cookie和session
  8. HDFS HA支持多Standby节点机制
  9. html实现展开余下全文多个,DIV+css内容太长,实现点击展开余下全文
  10. web前端入门到实战:CSS角度单位:deg、grad、rad、turn