[读书笔记]《代码整洁之道》

Bjarne Stroustrup,C++之父对好代码的定义是这样的:

  • 逻辑应该是清晰的,bug难以隐藏;
  • 依赖最少,易于维护;
  • 错误处理完全根据一个明确的策略;
  • 性能接近最佳化,避免代码混乱和无原则的优化;
  • 整洁的代码只做一件事。

代码规范占据了十分重要的地位,好代码具有不可估量的意义:

  • 在团队合作中,规范的代码能够促进团队合作;
  • 规范的代码可以减少bug的处理;
  • 规范的代码可以降低维护成本;
  • 规范的代码有助于代码审查;
  • 养成写好代码的习惯,有助于自身的成长。

我在看完《代码整洁之道》之后,对它的一部分内容进行的了归纳总结:
(我归纳得比较笼统,想要更深入了解的还是建议去看原书,我的另一篇博客里面有英文版和中文版的下载连接)

一、有意义的命名
二、函数
三、注释
四、格式
五、对象和数据结构
六、错误处理
七、边界
八、类
九、系统
十、并发编程

 
 


 

一、有意义的命名

1、指明计量名称和计量单位

2、不使用魔术数(指未经定义直接在代码中出现的数值

3、注意作为变量名时的字母I和o(很像数字1和0

4、做有意义的区分

5、使用读得出来的名称(正确的单词

6、使用可搜索的名称

  单字母的名称和数字变量很难被找出来
  名字长短应与其作用域大小相对应

7、不使用成员后缀/前缀

8、避免使用编码,但若接口和实现必须选一个编码,则选实现;

  ShapeFactoryImpl,甚至CShapeFactory都比IShapeFactory好得多

9、避免思维映射

10、类名和对象名应该是名词或名词短语

11、方法名应当是动词或动词短语

12、不使用俗语或俚语命名

13、每个概念对应一个词

  (Get、fetch、retrieve)、(controler、manager、driver)等同义或近义的词在同一个项目中只使用一个

14、别用双关语,即避免将同一单词用于不同目的

15、可以使用解决方案领域名称以及所涉及问题领域的名称(专业术语

16、添加有意义的语境

  可以添加前缀以提供语境,但更好的方案是创建类,然后把变量当作该类的成员字段
 
 

二、函数

1、函数应该短小,20行封顶最佳

  if、else、while语句中的代码块应该只有一行,写函数调用语句是一个不错的选择

2、一个函数只做一件事

3、每个函数一个抽象层级,自顶向下读代码:向下规则

public void B(){
    C();
}
public void C(){
    D();
}
public void D(){
    ...
}

4、将switch语句埋到抽象工厂底下

5、使用描述性语句,别害怕长名称

  例如:SetupTeardownIncluder

6、函数参数

  • 尽量减少函数参数,没有最好
  • 如果函数要对输入参数转换,则结果体现在返回值上;
  • 不使用表示参数(即不传入布尔值);
  • 函数二个及以上可考虑封装成类;
  • 对于一元函数,函数与参数应形成 动词/名词对 形式,如:write(name)

7、避免做其他被藏起来的事情

8、分隔指令与询问,例如将设置方法set和判断是否存在attributeExits分成两个方法

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

  • 使用异常能把错误处理代码从主路径代码中分离出来
  • 抽离try/catch代码块,另外形成函数
  • 错误处理函数只做错误处理这一件事情

10、消除重复

11、结构化编程

  每个函数、函数中的每个代码块都应只有一个入口、一个出口,即只有一个return,循环中不能有break或continue,且不能有goto语句,但在小函数中,偶尔出现return、break、continue没有坏处,而goto只要在大函数中才有道理,应避免使用。

小结:一开始可以想写什么就写什么,最后再按规则慢慢修改,没有必要强制自己一开始就写出非常完美的代码,这几乎没有能做到

三、注释

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

2、用代码来阐述自己的意图

3、好注释的类型:

  1. 法律信息
  2. 提供信息的注释
  3. 对意图的解释
  4. 阐释:把晦涩难懂的参数或返回值的意义翻译成可读形式
  5. 警示:警告会出现某种后果
  6. TODO注释:表示该功能代码待编写或修改
  7. 放大某种不合理之物的重要性
  8. 公共API的javadoc

4、坏注释的类型

  1. 读者看不懂的喃喃自语
  2. 多余的注释(不能比代码本身提供更多的信息,没有证明代码的意义,也没有给出代码的意图或逻辑)
  3. 误导式注释
  4. 循规式注释(例如:每个变量都要有注释的规矩)
  5. 日志式注释
  6. 废话注释(例如:/ 默认的构造器 /)
  7. 能用函数或变量时就别用注释
  8. 位置标记尽量少用
  9. 大括号后的注释(如;try{……} //try)
  10. 归属与署名:源代码控制系统是这类信息最好的归属地
  11. 注释掉的代码
  12. Html注释
  13. 非本地信息
  14. 信息过多
  15. 不明显的联系
  16. 函数头
  17. 非公共代码的javadoc
  18. 范例

四、格式

1、垂直格式

  1. 名称应当一目了然,函数短小而精悍
  2. 封包声明、导入声明和每个函数之间都有空白行隔开
  3. 紧密相关的代码应相互靠近
  4. 变量声明应尽可能靠近其使用位置:
    1)本地变量(局部)应在函数内顶部出现
    2)循环中的控制变量在循环语句中声明
    3)实体变量(成员变量)应在类的顶部声明
    4)调用函数应尽可能放在被调用函数上面;
    5)概念相关的代码应该放在一起;

     5.自上向下展示函数调用依赖顺序

public void B(){
    C();
}
public void C(){
    D();
}
public void D(){
    ...
}

2、横向格式

  1. 尽力保持代码行短小
  2. 在赋值操作符周围加上空格,如示例A;
        不在函数名和左圆括号之间加空格,函数调用括号中的参数一一隔开,如示例B;
//示例A
int i = 3//示例B
determinant(a, b, c);

//示例C
int temp = b*b - 4*a*c;

/* 乘法间不加空格,因为乘法的优先级比减法高 */

3、缩进

4、while、for、if等语句的语句体用“{” “}”括起来

五、对象和数据结构

1、对象:暴露行为,隐藏数据。便于添加新对象类型而无需修改既有行为,但难以在既有对象中添加新行为;

public class Square{
    public Point topLeft;
    public double side;
}

public class Circle{
    public Point center;
    public double radius;
}

public class Geometry{
    public double are(Object Shape){
        //求面积的行为
    }
}

/** 1\若在Geometry类中添加一个求周长的方法,不会影响Square类和Circle类
    2\但添加一个Rectangle类有可能需要修改方法are(Object Shape)
*/

2、数据结构:暴露数据,没有明显行为。便于向既有数据结构添加新行为,同时也难以向既有函数添加数据结构;

public class Shape implements Shape{
    private Point topLeft;
    private double side;
    
    public double area(){
        //计算面积的方法
    }
}

public class Circle implements Shape{
    private Poit center;
    private double radius;
    private final double PI = 3.14159;

    public double area(){
        //计算面积的方法
    }
}

/** 添加一个Rectangle类,不会影响现有方法area();
    但在Shape中添加一个求周长的方法时,所有类都要加上该方法
*/


六、错误处理

1、使用异常代替错误返回码

2、先写try-catch-finally语句

3、使用不可控异常(运行时异常),可控异常违反开放/闭合原则

4、给出异常发生的环境说明

5、依调用者的需求定义异常

6、定义常规流程

7、避免返回null值,可以改为返回空列表、空数组……,避免NullPointerException;

8、别传递null值


七、边界

1、不要将类似Map这样的边界接口在系统中传递(即避免从公共API中返回边界接口,或将边界接口作为参数传递给公共API)


八、类

1、类的组织

    类应该从一组变量列表开始,之后是公共函数
    公共静态常量 -> 私有静态变量 -> 私有实体变量

2、类应该短小

  1. 单一权责原则(SRP)

  2. 保持内聚性
        若一个类中的每一个变量都被每个方法所使用,则该类具有最大的内聚性
    应当尝试将这些变量和方法拆分到两个或多个类中,让新的类更为内聚;

3、为了修改而组织

  • 修改现存类有可能会破环其他代码,应在编码代码的时候,将有可能修改的类(如:sql类)的每个接口方法都重构到从sql类派生出来的类中。

  • 开放-闭合原则(OCP):类应当对外扩展开放,对修改封闭;


九、系统

1、将系统的构造与使用分离

  • 分解main;将全部构造过程搬迁到main或被称之为main的模块
  • 使用抽象工厂模式
  • 依赖注入(控制反转)

十、并发编程

1、为什么要并发?

并发是一种解耦策略。它帮助我们把做什么(目的)和何时做(时间)分解开。并发能明显地改进应用程序的吞吐量和结构

2、并发防御原则

  1. 单一权责原则,建议:分离并发代码与其他代码
  2. 限制数据作用域
  3. 谨记封装数据;严格限制对可能被共享的数据的访问。可以采用synchronized关键字在代码中保护一块使用共享对象的临界区。
  4. 使用数据复本
  5. 线程应尽可能独立:尝试将数据分解到可被独立线程操作的子集

3、警惕同步方法之间的依赖

  • 避免使用一个共享对象的多个方法

4、保持同步区微小

  • synchronized制造了锁,带来了延迟和额外开销。

5、测试线程代码

  1. 将伪失败看作可能的线程问题
  2. 先使非线程代码可工作,不要同时追踪非线程缺陷和线程缺陷
  3. 编写可调整的线程代码
  4. 运行多于处理器数量的线程
  5. 在不同的平台上运行
  6. 编写可插拔的线程代码
  7. 装置试错代码
      有两种装置代码的方法:
        1) 硬编码:手工向代码中插入wait()、sleep()、yield()、priority()的调用
        2) 自动化:可以使用Aspect-Oriented Framework、CGLIB或ASM之类的工具通过编码来装置代码

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章