Skip to content
On this page

从《代码之丑》专栏了解好代码

该部分笔记来自极客时间代码之丑 专栏。时间原因仅作粗略总结。

书籍推荐(来自评论区):

  • 《你的灯亮着吗》

命名

  1. 描述意图,而非细节:描述出这段代码在做的事情。也需要避免使用技术术语命名(例如 bookList 使用了 List 类型,可以改为 books)。
  2. 精准表述,而不宽泛(宽泛案例:data、info、flag、process、handle、build、maintain、modify、manage)。
  3. 建立团队的词汇表,让团队成员有信息可以参考——业务词汇表可同产品团队商讨确定。
    1. 一般包括:中文名称、英文表述、说明等信息。
  4. 好的命名,是体现业务含义的命名。
  5. 函数名应该是动词结构,可以上网易有道查询同一词汇的不同形式,纠结时使用与业务相关性更高的那个。

重复代码

  1. 不要复制粘贴,如果需要复制粘贴,首先应该做的是提取一个新的函数出来,把公共的部分先统一掉
  2. 写出长函数时,需要把各部分功能提取为小函数,做好关注点分离。
  3. 函数越短越好!!!
  4. 当一个类职责不单一时,就说明它该拆了。
    1. 字段分组
    2. 职责转移
    3. 类越小越好
  5. 参数列表过长时,可以将相关参数封装成参数类
    1. 如果一个函数过多地依赖其他类,需要考虑将其转移至依赖类。
    2. 参数列表中存在标记时,可将标记参数代表的不同路径拆分出来,分解为多个函数。
    3. 参数列表也是越小越好。
  6. 在 if 结构中,应把最容易返回的情况写在最前面,即令函数尽早返回
    1. 同时可避免使用多个 else
    2. 尽量避免写 else
  7. 在 switch 结构中,可引入多态来取代条件表达式
    1. 将 case 判断封装为一个方法(例如获取用户等级)
    2. 然后将用户等级抽取为接口,不同等级实现该接口

封装

  1. 声明完一个类的字段之后,请停下生成 getter 的手,转而让大脑开始工作,思考这个类应该提供的行为。
  2. 一个好的封装是需要基于行为的,所以,如果把视角再提升一个角度,我们应该考虑的问题是类应该提供哪些行为,而非简简单单地把数据换一种形式呈现出来。
  3. 不同层次的内容放入不同函数中。

数据

  1. 移除设值函数(Remove Setting Method):消除 setXXX 函数
  2. 解决可变数据,还有一个解决方案是编写不变类。
    1. 所有的字段只在构造函数中初始化;
    2. 所有的方法都是纯函数;
    3. 如果需要有改变,返回一个新的对象,而不是修改已有字段。
  3. 实体对象要限制数据变化,而值对象就要设计成不变类。
  4. 变量仅在开始使用时声明。
  5. 在类 MVC 结构中,通常 Controller 中获取的数据(例如根据登录 token 获取的用户ID),好像放在哪里(Controller 或 Service)都行,这导致了参数不明也不稳定
    1. 可以引入一个防腐层,将需要的参数封装起来,业务和内部接口隔离
    2. 引入模型,将业务和具体实现隔离
  6. 业务代码中任何与业务无关的东西都是潜在的坏味道。
  7. 代码应该向着稳定的方向依赖。
    1. 高层不应依赖底层模块,二者均应依赖抽象
    2. 抽象不应依赖细节,细节应依赖于抽象。
  8. 空对象问题,不要忘记处理为空的情况——Java 8 有个 Optional 类型,PHP 可以考虑每次都抛出一个 EmptyException 异常。

其他

  1. 代码评审暴露的问题越多越好,频率越高越好。
  2. 代码评审角度:
    1. 实现方案的正确性:尽可能考虑 Sad Path
    2. 算法正确性:是否会陷入死循环等
    3. 代码坏味道
  3. 对外提供的接口越少越好——谨慎对待接口和实体的变动,多沟通,探索真实意图以及需求的必要性。
  4. 消除 static 函数
    1. 从本质上说,static 函数是一种全局函数,static 变量是一种全局变量,全局的函数和变量是我们尽量规避的
    2. 一个函数调用了 static 函数不好测试
    3. 除了写程序库,日常开发尽可能不用 static 函数
  5. 如果一个接口只有一个实现类,可以命名为 DefaultXXX,而不是和接口同名。
  6. 先竭力把代码写到不需要用注释,而把注释当作最后的选择。