代码整洁之道(每日阅读)待完善
- 参考
《代码整洁之道》书籍
三方应用: https://pdai.tech/md/dev-spec/code-style/code-style-alibaba.html
一.有意义的命名
- 名副其实,体现本意
- 避免代码误导
- 有意义的命名,不要有a,b,c类似的命名
- 不要有Info、Data、Object后缀,变量 a,an,the前缀含糊其辞
- 名字的长度应与作用域名大小相对应,作用域越大名字越
- 避免使用前缀来标明作为哪个类的成员变量
- 如果多个临时变量命名冲突,与其使用前缀,不如花时间思考更具描述性的变量名。通过准确地描述变量的用途和含义,可以避免命名冲突。例如,如果有多个表示时间的临时变量,可以分别命名为
startTime
、endTime
、processingTime
等,而不是使用temp1_time
、temp2_time
这样的命名方式。 - 类名为名词,要直抒胸臆不要加没不要的前后缀
- 方法名为动词,如果遇到前缀使用isxxx进行判断的需要修改防止命名冲突应使用checkxxx,定义为基本数据类型
Boolean isDeleted
;的属性,它的方法也是isDeleted()
,RPC 框架在反向解析的时候,“以为”对应的属性名称是deleted
,导致属性获取不到,进而抛出异常。
- 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。 应用工具类包名为
com.alibaba.open.util
、类名为MessageUtils
(此规则参考 spring 的框架结构) - 不要一个概念用多个词,如果都是query就不要get,如果都是controller就不要manager
- 不要一语双关
- 对方法返回值命名都使用result, 参数如果是有一个好的类名直接使用param, 如果需要表达特定含义或为一般类型使用特定的参数名
- 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长
MAX_STOCK_COUNT
- 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾
- 如果使用到了设计模式,建议在类名中体现出具体模式
- 接口类中的方法和属性不要加任何修饰符号(
public
也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。接口方法签名:void f();
接口基础常量表示:String COMPANY = "alibaba";
JDK8 中接口允许有默认实现,那么这个default
方法,是对所有实现类都有价值的默认实现。 - 接口和实现类的命名有两套规则:对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。如果是形容能力的接口名称,取对应的形容词做接口名(通常是–able 的形式)。
- 枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。枚举其实就是特殊的常量类,且构造方法被默认强制是私有。枚举名字:
DealStatusEnum
,成员名称:SUCCESS / UNKOWN_REASON
。 - long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字 1 混淆,造成误解。 说明: Long a = 2l;
二.函数
- 短小、短小、再短小
- 每个函数只做一件事,这一件事可能会有相同层级的多件事需要再拆分成单个函数进行调用,直到进行细节操作
- 注意if嵌套,最多嵌套两层
- 对大于等于三个条件的switch进行抽象
- 不要有重复代码,一般的通用代码是放到其他位置统一处理
- 函数不要有副作用,如果出现相对于此函数的副作用,就将副作用函数提炼成只依附该函数的子函数
- 对函数的操作要么只查询,要么只操作,不要在if的条件中放置操作函数然后返回一个bool
- 将try/catch进行抽离,抽离为最顶层,这个方法只做异常处理,因为异常也是一件事
- 如果不是在走业务逻辑进行了if-else分支,业务逻辑分支是对不同业务情况走不同操作, 而代码逻辑的if-else应新分出一个方法进行处理
- 对于复杂的参数封装通常会重新另起一个方法
- 将所有相关的枚举类型放到一个通用类中其中分成多个枚举类
- 对于在请求头或响应头中的参数类型中,对一个变量有多种不同装配使用内部枚举类,对于json套json的格式,类似请于实体类中套另外实体(也就是说作用域只在一个类中的枚举或方法或实体不要抽取一个新的类),对json要使用对应实体
- 不要使用直接访问dao层,要再加一个biz
- 避免死循环,将范围扩大
- 使用工具类进行集合操作
- 对于大于等于三个或三个以上的参数列表进行封装为一个新的类
- 别返回null,别传递null
- 测试代码要清晰
- 所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较
- 构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中
- 序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如 果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值
- RPC 方法的返回值和参数必须使用包装数据类型。
- 循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
- 只要重写 equals,就必须重写 hashCode。
- 不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
Iterator<String> it = a.iterator();
while (it.hasNext()) {
String temp = it.next();
if (删除元素的条件) {
it.remove();
}
}
- 使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用 Map.foreach 方法。values() 返回的是 V 值集合,是一个 list 集合对象;keySet() 返回的是 K 值集合,是一个 Set 集合对象;entrySet() 返回的是 K-V 值组合集合。
- 高度注意 Map 类集合 K/V 能不能存储 null 值的情况
- 利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的 contains 方法进行遍历、对比、去重操作。
三.注释(待完善)
- 使用好的命名和函数流程化来去除注释
- 注释在含法律风险的信息、对业务需求复杂帮助理解意图的解释、警示(不要碰!)、todo注释、放大重要性警示
- 不要在实体类中总全部加冗余的注释
- 注释直抒胸臆不要有废话
四.格式
- 垂直代码
- 物理垂直:代码长度尽量减小
- 概念垂直:亲密性把持(换行分割)
- 相关函数和使用变量放在一块
- 调用方的函数块放在被调用方函数块的上面(人眼自上而下)
- 横向代码
- 物理横向:减少宽度
- 概念横向:
- 运算符之间(加减除法用空格,乘法不用,负号不用)空格,参数间空格
五.对象和数据结构
- 对象暴露行为, 隐藏数据, 便于添加新对象类型而无须修改既有行为, 同时难以在既有对象添加新行为,数据结构暴露数据,没有明显的行为, 便于向既有数据结构添加新行为, 同时难以向既有的函数添加新的数据结构
- 面向过程式的代码:便于在不改动既有数据结构的前提下添加新函数;面向对象式的代码:便于在不改动既有函数的前提下添加新类;
所以:过程式代码难以添加新的数据结构,因为必须修改所有函数;面向对象代码难以添加新函数,因为必须修改所有类;
- 方法不应一次性调用由任何函数返回对象的方法导致一连串的调用,火车失事,最好做切分
六.类
- 类应短小
- 功能类在某一功能模块应具备单一职责(如果大多类都使用了User类,属于同一个业务区域,将User提取出来公用, 但属于不同业务区域,应独立创建新的类)
- 公用dto应尽量内聚到相同业务中的不同功能,保持内聚性就能得到很多短小的类,并且该dto不能修改字段属性和数量(内聚性)
- 将各个类的元数据修改操作隔离开(解耦合)
- 只对可修改代码使用setter方法
- 所有的覆写方法,必须加@Override 注解。
- 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
- 相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。
- 不能使用过时的类或方法。
- 所有的 POJO 类属性必须使用包装数据类型。
- 定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值, 但聚合对象可以。
七.异常
八.日志
九.并发处理
十.设计
- 将创建与运行分割开,能通过创建进行“服务扩容”
- 使用依赖注入
- 不可重复
- 增加表达力
- 尽可能少的类和方法
- 软件设计原则
- 开闭原则:对扩展开放,对修改关闭
- 里氏代换原则:在子类中可以扩展父类原有的功能,但不能改变父类原有的功能
- 依赖倒转原则:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象,对抽象进行编程
- 接口隔离原则:客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。(基本上就是一个业务应当多个功能接口让其应用不同功能接口的服务进行实现)
- 迪米特法则:只和你的直接朋友交谈,不跟“陌生人”说话(不可连环调用)
- 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
- 设计模式使用场景
- 构建时参数列表过长:建造者模式
- 全局配置:
十一.Mysql数据库
建表规约
索引规约
SQL语句
ORM映射
应用分层
二方库依赖
服务器