Appearance
从《代码之丑》专栏了解好代码
书籍推荐(来自评论区):
- 《你的灯亮着吗》
命名
- 描述意图,而非细节:描述出这段代码在做的事情。也需要避免使用技术术语命名(例如 bookList 使用了 List 类型,可以改为 books)。
- 精准表述,而不宽泛(宽泛案例:data、info、flag、process、handle、build、maintain、modify、manage)。
- 建立团队的词汇表,让团队成员有信息可以参考——业务词汇表可同产品团队商讨确定。
- 一般包括:中文名称、英文表述、说明等信息。
- 好的命名,是体现业务含义的命名。
- 函数名应该是动词结构,可以上网易有道查询同一词汇的不同形式,纠结时使用与业务相关性更高的那个。
重复代码
- 不要复制粘贴,如果需要复制粘贴,首先应该做的是提取一个新的函数出来,把公共的部分先统一掉。
- 写出长函数时,需要把各部分功能提取为小函数,做好关注点分离。
- 函数越短越好!!!
- 当一个类职责不单一时,就说明它该拆了。
- 字段分组
- 职责转移
- 类越小越好
- 参数列表过长时,可以将相关参数封装成参数类。
- 如果一个函数过多地依赖其他类,需要考虑将其转移至依赖类。
- 参数列表中存在标记时,可将标记参数代表的不同路径拆分出来,分解为多个函数。
- 参数列表也是越小越好。
- 在 if 结构中,应把最容易返回的情况写在最前面,即令函数尽早返回。
- 同时可避免使用多个 else
- 尽量避免写 else
- 在 switch 结构中,可引入多态来取代条件表达式
- 将 case 判断封装为一个方法(例如获取用户等级)
- 然后将用户等级抽取为接口,不同等级实现该接口
封装
- 声明完一个类的字段之后,请停下生成 getter 的手,转而让大脑开始工作,思考这个类应该提供的行为。
- 一个好的封装是需要基于行为的,所以,如果把视角再提升一个角度,我们应该考虑的问题是类应该提供哪些行为,而非简简单单地把数据换一种形式呈现出来。
- 不同层次的内容放入不同函数中。
数据
- 移除设值函数(Remove Setting Method):消除 setXXX 函数
- 解决可变数据,还有一个解决方案是编写不变类。
- 所有的字段只在构造函数中初始化;
- 所有的方法都是纯函数;
- 如果需要有改变,返回一个新的对象,而不是修改已有字段。
- 实体对象要限制数据变化,而值对象就要设计成不变类。
- 变量仅在开始使用时声明。
- 在类 MVC 结构中,通常 Controller 中获取的数据(例如根据登录 token 获取的用户ID),好像放在哪里(Controller 或 Service)都行,这导致了参数不明也不稳定
- 可以引入一个防腐层,将需要的参数封装起来,业务和内部接口隔离
- 引入模型,将业务和具体实现隔离
- 业务代码中任何与业务无关的东西都是潜在的坏味道。
- 代码应该向着稳定的方向依赖。
- 高层不应依赖底层模块,二者均应依赖抽象。
- 抽象不应依赖细节,细节应依赖于抽象。
- 空对象问题,不要忘记处理为空的情况——Java 8 有个 Optional 类型,PHP 可以考虑每次都抛出一个 EmptyException 异常。
其他
- 代码评审暴露的问题越多越好,频率越高越好。
- 代码评审角度:
- 实现方案的正确性:尽可能考虑 Sad Path
- 算法正确性:是否会陷入死循环等
- 代码坏味道
- 对外提供的接口越少越好——谨慎对待接口和实体的变动,多沟通,探索真实意图以及需求的必要性。
- 消除 static 函数
- 从本质上说,static 函数是一种全局函数,static 变量是一种全局变量,全局的函数和变量是我们尽量规避的
- 一个函数调用了 static 函数不好测试
- 除了写程序库,日常开发尽可能不用 static 函数
- 如果一个接口只有一个实现类,可以命名为 DefaultXXX,而不是和接口同名。
- 先竭力把代码写到不需要用注释,而把注释当作最后的选择。