代码整洁之道阅读笔记一(命名、格式)

一、有意义的命名

1. 名副其实

名字能让人理解变量是用来做什么的,比如 daysSinceCreation比 d 用来表
示消逝的时间更好。

2. 避免误导

不用具有特殊含义的词语,比如list,除非它真的是个list
不用 l o

3. 做有意义的区分

不加没有作用的区分或者前缀后缀,比如money和moneyAmount没有区
别。

4. 使用读的出来的名称

不要瞎造词

5. 使用可搜索的名称

名称可以长,但要方便搜索,不要用e 等很短的,单字母名称 如 i只能用于
方法内变量,名称的长短应与其作用域大小相对应。

6. 避免使用编码

不需要加Hn phoneString等

7. 类名

类名和对象名应该是名词或名词短语,如Customer  WikiPage Account。
不应当是动词

8. 方法名

方法名应当是动词或动词短语,如postPayment save等,访问器和修改器
和断言应该加get set is
重载构造器时,应使用描述了参数的静态工厂方法名,如
Complex fulcurmPoint = Complex.FromRealNumber(23.0) 
好于
Complex fulcrumPoint = new Complex(23.0)

9. 每个概念应该对应一个单词

比如Controller Manager driver 同种类型的操作用一个专用术语就可以

10. 不用双关语

比如应该用append的地方用到了add

11. 使用解决方案领域名称,实在不行使用所涉及领域的名称

可以使用专业词语 比如JobQueue来命名,实在不行也可以用涉及到的问
题来命名。

12. 添加有意义的语境

比如firstName lastName street 尽量让语境内的变量在一个语境下面,并
能让别人知道这是在干什么,也能顺藤摸瓜猜出其他变量的含义

二、函数

1. 短小

函数不要太长,太长的代码块不要放进去,if while其中的代码块应该只有
一行,比如是一个函数调用语句。不要有太深的嵌套结构。

2. 只做一件事

只做一件事,做多件事的话可以拆分成多个函数,几十行最佳。做多件事
可以用上一个抽象层级包含起来。

3. 每个函数一个抽象层级

应该有一个自顶向下的代码阅读规则,细节和调用方不要放在一起,自顶
向下应该是一个树形结构,叶子节点应该在同一层上,上面都是对它的调
用。
其实这点很难做到,比如上面调用了一个函数,下面进行一个一行的操
作,不可能为这一行单独拆分成一个函数,所以我认为拆分原则还是要根
据代码的作用,比如这段代码要做一件事情,即使再短也最好拆分,如果
只是一个格式转换,那就没必要单独拆分。总之是为了代码的可读性。

4. switch语句

可以用来创建多态对象

5. 使用描述性的名称

为每个只做一件事的小函数取个好名字,函数越小,功能越集中。就越容
易起名字。不要害怕长名称,长而具有描述性的名称,比长注释要好。

6. 函数参数

• 最好的函数是无参
• 参数和函数名位于不同的抽象层级,给阅读代码带来困难,需要在阅读代
码时候去了解不重要的细节。
• 不要向函数内传标志参数,比如true false ,因为这会代表函数不止做了
一件事。而且要在上层和下层判断两次逻辑,为什么不统一判断一次呢
Eg:调用处:if() do(true) else do(false)
函数do:if(true) {}  else {}
这样逻辑就重复了,修改时也要一起修改。
• 二元参数如果是比较自然的,符合规律的,比如座标(x,y),是可以接受
的,一定要按照顺序来。
• 参数对象,如果需要一堆参数,那就说明有些参数可以封装成类了。
Eg:makeCircle(double x, double y, double radius);
可以改成 makeCircle(Point center, double radius)

7. 无副作用

上面说到了函数只做一件事,并且在函数名中最好体现出函数要做什么
事,不要偷偷做了某件事,但是调用者不知道,这就是副作用。
最好就不要违反函数只做一件事的原则。

8. 指令和询问分离

不要一个函数既做某些事情,又要返回不相干的结果。
操作和查询应该是分开的。可以是先查询,在指令。

9. 使用异常替代返回错误码

• 如果函数会返回错误码的话,可能会导致深层嵌套结构,要求调用方必须
马上处理错误。
所以应该用异常来替代返回错误码,这样错误处理代码就能分离出来
• 抽离try/catch
Try/catch代码块内的内容会弄乱代码结构,因为catch里的内容其实不属于
主体代码,将错误处理和正常流程混为一谈,所以最好将其中的内容抽离
出来,
• 错误处理就是一件事
函数应该只做一件事,错误处理就是一件事,所以处理错误的函数不应该
在finally后面还有其他的东西
• Error.java错误类 导致依赖  不应该存在
错误码的存在,意味着某处有个类或者枚举,定义了所有的错误码
这意味着其他类都要引入这个错误类,并且枚举修改时,其他类都要重新
编译

而使用异常替代错误码,新的异常就可以从异常类中派生出来,无需重新编译,而且解除了依赖

10. 别重复自己

有的算法或代码块会在很多函数内出现,可能会比较小,也不容易发现。
这样代码会臃肿,而且算法改变时也需要修改四次,可能会导致忽略或忘
掉。如果写代码时发现需要copy一段代码,那么首先考虑能不能抽离为函
数。
消灭重复。

11. 怎么写出这样的代码?

首先要认识到,不可能在没写时就已经设计好代码的结构,所以开始就随
便写吧,先实现功能,不要在开始就被规则束缚,然后为每个函数配好单
元测试,这时就开始打磨代码吧,保证测试通过。

三、注释

1. 注释不能美化糟糕的代码

糟糕的代码需要写注释才能弄清楚,但是最好是把代码弄干净。带有少量
注释的整洁有表达力的代码比带有大量注释的零碎复杂的代码更有用。

2. 用代码来阐述

有时用代码就能解释意图,只需要创建一个描述与注释同一事物的注释即
可。
比如把一段判断封装为函数
// check if the employee is eligible for full benefite
If ((employee.flags & HOURLY_FLAY) && (employee.age > 65))

改为
 if(employee.isEligibleForFullBenefits)

3. 好注释

有哪些注释是必要有用的呢?
	a. 法律信息,这类一般放在头部
	b. 提供信息的注释,这种一般可以用更好的方法名代替
	c. 对意图的解释,比如有两种解决办法,使用了某一种的原因
	d. 阐释,比如有一段判断很复杂,但是目的很简单,但是最好是让返回
	值本身就足够清楚。但有时调用了外部库,自己不能修改,那就可以帮
	它加阐释。
	比如: a.compareTo(a) == 0 ; // a == a
	e. 警示,提示某段代码有何风险。
	f. TODO注释,但是这类要留出时间定期查看和删除。
	g. 放大,有些地方可能看起来不合理,为了避免被改掉,加一段代码提
	示它的重要性。
	h. 公共API中的javadoc

4. 坏注释

	a. 喃喃自语
	b. 多余的注释,简单的地方就不需要加注释。
	c. 误导性数值:不精确,错的。
	d. 循轨式注释
	e. 日志式注释,因为有了源代码控制系统,所以这类不需要加。
	f. 废话注释,也是很简单的函数就不需要加注释。
	g. 错误的
	h. 位置标记  比如 ///////////////////////////a///////
	i. 括号后边的注释,比如while  else
	j. 归属与署名,也因为有源代码控制系统,所以不需要加。
	k. 注释掉的代码,别人也不敢删,要删就删掉,源代码控制系统可以恢
	复。
	l. html注释,没有用。
	m. 信息过多

四、格式

1、垂直格式

  1. 尽量用200~500行为单个文件。

  2. 源码应该像报纸一样:上面是大纲,下面是细节。最顶部应该是高层次概念和算法,细节应该依次向下展开,放在我们代码里意思就是:public方法应该放在上面,private方法应该放在下面。应该是一个总分结构。

  3. 概念上垂直方向的区隔
    代码中是需要一些空行的,方法间必须要有空行,方法中:两段功能不同的代码中也需要空行,以区别不同的代码段,但这个尺度需要掌握好,空行特别多时也会影响阅读。所以若干很短比如只有一行的代码段就无需再空行了
    String name = “A”;
    Int age = 10;
    String sex = “男”;

    User user = new User();
    User.setName(name);
    User.setAge(age);
    User.setSex(Sex);

    Mapper.insertUser(user);

  4. 垂直方向上的靠近
    与上面相反,一些功能或概念相似的代码段是应该放在一起的,比如我上面的代码中把变量初始化放在一起,new一个对象的过程放在一起。这样很容易知道它们是相似的。在类中也应该是这样,私有变量放在一起,公有变量放在一起。

  5. 垂直距离
    关系密切的概念应该相互靠近,条件是在同一个文件中。其实并不应该把关系密切的概念放在不同文件,所以应该避免使用protexted变量。
    a. 变量的声明应该靠近其使用位置,比如我们写循环的控制变量一般就放在循环上面。
    b. 实体变量应该在类的顶部声明,这好像是约定俗成,所以大家都会去顶部找实体变量。有些规范会放在中间,虽然靠近了使用位置,但其实更不好找。
    c. 相关函数,某个函数调用了另外一起就应该把它们放在一起,而且调用者应该尽可能放在被调用者上面,这点一定要适应。

2. 水平格式

  1. 宽度?
    以前的规范是80个字符,现在虽然屏幕变宽了,但是最好也不要太宽,超过120个字符有时就需要滚动了。
  2. 水平方向上的区隔与靠近
    使用空格连接紧密相关的事物,把相关性较弱的事物分隔开。
  • a. 赋值操作符周围加空格字符。因为空格可以区分 = 左右变量。

  • b. 函数名和左括号之间不加空格,因为函数和参数密切相关.
    Public static root(double a, double b, double c);{
    Return (a*b) + c;
    }
    可以看到乘法之间没加空格,因为优先级较高,这样读起来比较舒服。

      总之加不加空格,就看分隔符两边的关系,如果觉得需要分开提高可阅读性,就加空格,如果觉得是很紧密的,就不加空格。
    
  • c. 水平对齐
    以前在声明一些相同的变量会对齐,这样也方便批量操作。
    Private socket socket;
    Private a a;
    其实没什么用,中间用空格隔开就好。

  • d. 缩进
    这个没什么说的,日常都会缩进,方便了解代码结构。

  • e. 空范围
    尽量不适用while 或 for 中为空

  1. 团队规则
    一个系统中应使用同样的代码规范。

五、对象和数据结构

  1. 私有变量应该通过get set方法,这样不会暴露内部实现,自己也可以替换实现方法,如果都暴露出去就不能随意更改。
    这是关系到抽象关系,因为应该暴露的是抽象接口,而不是本身的数据细节,这是以抽象形态展示数据。所以也不能乱加get set
  2. 对象和数据结构之间的反对称性
    其实就是面向过程和面向对象之间的差异:
    面向过程容易添加函数,不容易添加数据结构。
    面向对象 容易添加数据结构,但添加函数需要每个都添加
  3. 得墨忒定律
    模块不应了解它所操作的对象的内部情形
    也就是说对象应该隐藏数据,暴露操作(需要注意,通过存取器也暴露了数据)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章