VIP代码规约
2019-08-27 10:34:11 2 举报
AI智能生成
VIP代码规约是指为提高代码质量和可维护性而制定的一套编码规范。它包括命名规则、注释规范、代码格式等方面,旨在使代码更加易读易懂、易于修改和扩展。例如,命名规则要求变量名、函数名等应该具有描述性,能够清晰地表达其作用;注释规范要求在必要的地方添加注释,以便于他人理解代码;代码格式要求缩进、换行等方面保持一致,以提高代码的美观程度。遵循VIP代码规约可以使代码更加规范化、标准化,从而提高开发效率和代码质量。
作者其他创作
大纲/内容
VIP代码规约
https://vipshop.github.io/vjtools/#/standard/
(一) 命名规约
Rule 1. 禁止拼音缩写,避免阅读者费劲猜测;尽量不用拼音,除非中国式业务词汇没有通用易懂的英文对应。【*】
禁止: DZ[打折] / getPFByName() [评分]
尽量避免:Dazhe / DaZhePrice
Rule 2. 禁止使用非标准的英文缩写 【*】
反例: AbstractClass 缩写成 AbsClass;condition 缩写成 condi。
Rule 3. 禁用其他编程语言风格的前缀和后缀 【*】
Rule 4. 命名的好坏,在于其“模糊度”【*】
1、 如果上下文很清晰,局部变量可以使用 list 这种简略命名, 否则应该使用 userList 这种更清晰的命名。
3、 方法的参数名叫 bookList ,方法里的局部变量名叫 theBookList 也是很没诚意。
5、 callerId 与 calleeId , mydearfriendswitha 与 mydearfriendswithb 这种拼写极度接近,考验阅读者眼力的。
Rule 5. 包名全部小写。点分隔符之间尽量只有一个英语单词,即使有多个单词也不使用下划线或大小写分隔【*】
正例: com.vip.javatool
Rule 6. 类名与接口名使用UpperCamelCase风格,遵从驼峰形式【*】
正例:UserId / XmlService / TcpUdpDeal / UserVO
反例:UserID / XMLService / TCPUDPDeal / UserVo
Rule 7. 方法名、参数名、成员变量、局部变量使用lowerCamelCase风格,遵从驼峰形式
正例: localValue / getHttpMessage();
Rule 8. 常量命名全大写,单词间用下划线隔开。力求语义表达完整清楚,不要嫌名字长【*】
正例: MAX_STOCK_COUNT
反例: MAX_COUNT
例外:当一个static final字段不是一个真正常量,比如不是基本类型时,不需要使用大写命名。
private static final Logger logger = Logger.getLogger(MyClass.class)
例外:枚举常量推荐全大写,但如果历史原因未遵循也是允许的,所以我们修改了Sonar的规则。
Rule 9. 如果使用到了通用的设计模式,在类名中体现,有利于阅读者快速理解设计思想【*】
正例:OrderFactory, LoginProxy ,ResourceObserver
Rule 10. 枚举类名以Enum结尾; 抽象类使用Abstract或Base开头;异常类使用Exception结尾;测试类以它要测试的类名开始,以Test结尾【*】
正例:DealStatusEnum, AbstractView,BaseView, TimeoutException,UserServiceTest
Rule 11. 实现类尽量用Impl的后缀与接口关联,除了形容能力的接口【*】
正例:CacheServiceImpl 实现 CacheService接⼝。
返例: Foo 实现 Translatable接⼝。
Rule 12. POJO类中布尔类型的变量名,不要加is前缀,否则部分框架解析会引起序列化错误【*】
反例:Boolean isSuccess的成员变量,它的GET方法也是isSuccess(),部分框架在反射解析的
Rule 13. 避免成员变量,方法参数,局部变量的重名复写,引起混淆 【*】
类的私有成员变量名,不与父类的成员变量重名
方法的参数名/局部变量名,不与类的成员变量重名 (getter/setter例外)
(二) 格式规约
Rule 1. 使用项目组统一的代码格式模板,基于IDE自动的格式化 【*】
IDE的默认代码格式模板,能简化绝大部分关于格式规范(如空格,括号)的描述。
统一的模板,并在接手旧项目先进行一次全面格式化,可以避免, 不同开发者之间,因为格式不统一产生代码合并冲突,或者代码变更日志中因为格式不同引起的变更,掩盖了真正的逻辑变更。
设定项目组统一的行宽,建议120。
设定项目组统一的缩进方式(Tab或二空格,四空格均可),基于IDE自动转换。
VIP代码格式化模板:https://github.com/vipshop/vjtools/tree/master/standard/formatter
Rule 2. IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式,不要使用Windows格式 【*】
Rule 3. 用小括号来限定运算优先级
示例:if ((a == b) && (c == d))
Rule 4. 类内方法定义的顺序,不要“总是在类的最后添加新方法”
顺序依次是:构造函数 (公有方法保护方法私有方法) getter/setter方法。
当一个类有多个构造方法,或者多个同名的重载方法,这些方法应该放置在一起。其中参数较多的方法在后面。
作为调用者的方法,尽量放在被调用的方法前面。
Rule 5. 通过空行进行逻辑分段
不同组的变量之间,不同业务逻辑的代码行之间,插入一个空行,起逻辑分段的作用。
而联系紧密的变量之间、语句之间,则尽量不要插入空行。
Rule 6. 避免IDE格式化
对于一些特殊场景(如使用大量的字符串拼接成一段文字,或者想把大量的枚举值排成一列),为了避免IDE自动格式化,土办法是把注释符号//加在每一行的末尾,但这有视觉的干扰,可以使用@formatter:off和@formatter:on来包装这段代码,让IDE跳过它。
(三) 注释规约
Rule 1. 基本的注释要求
第一、能够准确反应设计思想和代码逻辑;
第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。
Rule 2. 通过更清晰的代码来避免注释
Rule 3. 删除空注释,无意义注释
《Clean Code》建议,如果没有想说的,不要留着IDE自动生成的,空的@param,@return,@throws 标记,让代码更简洁。
Rule 4. 避免创建人,创建日期,及更新日志的注释
Rule 5. 代码修改的同时,注释也要进行相应的修改。尤其是参数、返回值、异常、核心逻辑等的修改 【*】
Rule 6. 类、类的公有成员、方法的注释必须使用Javadoc规范,使用/** xxx */格式,不得使用 //xxx 方式 【*】
Rule 7. JavaDoc中不要为了HTML格式化而大量使用HTML标签和转义字符
Rule 8. 注释不要为了英文而英文
Rule 9. TODO标记,清晰说明代办事项和处理人
Rule 10. 合理处理注释掉的代码
如果后续会恢复此段代码,在目标代码上方用/// 说明注释动机,而不是简单的注释掉代码。
如果很大概率不再使用,则直接删除(版本管理工具保存了历史代码)。
(四) 方法设计
Rule 1. 方法的长度度量、
方法尽量不要超过100行,或其他团队共同商定的行数。
另外,方法长度超过8000个字节码时,将不会被JIT编译成二进制码。
Rule 2. 方法的语句在同一个抽象层级上 --- 将功能抽成函数,提高复用
Rule 3. 为了帮助阅读及方法内联,将小概率发生的异常处理及其他极小概率进入的代码路径,封装成独立的方法
Rule 4. 尽量减少重复的代码,抽取方法
Rule 5. 方法参数最好不超过3个,最多不超过7个
1)如果多个参数同属于一个对象,直接传递对象。
2)将多个参数合并为一个新创建的逻辑对象。
3)将函数拆分成多个函数,让每个函数所需的参数减少。
Rule 6. 下列情形,需要进行参数校验
1) 调用频次低的方法。
2) 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,代价更大。
3) 需要极高稳定性和可用性的方法。
4) 对外提供的开放接口,不管是RPC/HTTP/公共类库的API接口。
如果使用Apache Validate 或 Guava Precondition进行校验,并附加错误提示信息时,注意不要每次校验都做一次字符串拼接。
Rule 7. 下列情形,不需要进行参数校验
1) 极有可能被循环调用的方法。
2) 底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底层才会暴露问题。
3) 被声明成private,或其他只会被自己代码所调用的方法,如果能够确定在调用方已经做过检查,或者肯定不会有问题则可省略。
Rule 8. 禁用assert做参数校验
Rule 9. 返回值可以为Null,可以考虑使用JDK8的Optional类
Rule 10. 返回值可以为内部数组和集合
Rule 11. 不能使用有继承关系的参数类型来重载方法
Rule 12. 正被外部调用的接口,不允许修改方法签名,避免对接口的调用方产生影响
Rule 13. 不使用 @Deprecated 的类或方法
Rule 14. 不使用不稳定方法,如com.sun.*包下的类,底层类库中internal包下的类
(五) 类设计
Rule 1. 类成员与方法的可见性最小化
Rule 2. 减少类之间的依赖
Rule 3. 定义变量与方法参数时,尽量使用接口而不是具体类
Rule 4. 类的长度度量
Rule 5. 构造函数如果有很多参数,且有多种参数组合时,建议使用Builder模式
Rule 6. 构造函数要简单,尤其是存在继承关系的时候
Rule 7. 所有的子类覆写方法,必须加 @Override 注解
Rule 8. 静态方法不能被子类覆写。
Rule 9. 静态方法访问的原则
1、避免通过一个类的对象引用访问此类的静态变量或静态方法,直接用类名来访问即可
2、除测试用例,不要static import 静态方法
3、尽量避免在非静态方法中修改静态成员变量的值
Rule 10. 内部类的定义原则
内部类也常用作回调函数类,在JDK8下建议写成Lambda
内部类分匿名内部类,内部类,静态内部类
1) 匿名内部类 与 内部类,按需使用
2) 静态内部类 与 内部类,优先使用静态内部类
Rule 11. 使用getter/setter方法,还是直接public成员变量的原则。
使用getter/setter,好处是可以进一步的处理
1. 通过隐藏setter方法使得成员变量只读
2. 增加简单的校验逻辑
3. 增加简单的值处理,值类型转换等
例外:有些序列化框架只能从getter/setter反射,不能直接反射public成员变量。
Rule 12. POJO类必须覆写toString方法 【*】
Rule 13. hashCode和equals方法的处理,遵循如下规则
只要重写equals,就必须重写hashCode。 而且选取相同的属性进行运算。 【*】
只选取真正能决定对象是否一致的属性,而不是所有属性,可以改善性能。
对不可变对象,可以缓存hashCode值改善性能(比如String就是例子)。
类的属性增加时,及时重新生成toString,hashCode和equals方法。
Rule 14. 使用IDE生成toString,hashCode和equals方法。 【*】
Rule 15. Object的equals方法容易抛空指针异常,应使用常量或确定非空的对象来调用equals 【*】
Rule 16. 除了保持兼容性的情况,总是移除无用属性、方法与参数【*】
Rule 17. final关键字与性能无关,仅用于下列不可修改的场景
1)定义类及方法时,类不可继承,方法不可覆写;
2)定义基本类型的函数参数和变量,不可重新赋值;
3) 定义对象型的函数参数和变量,仅表示变量所指向的对象不可修改,而对象自身的属性是可以修改的。
Rule 18. 得墨忒耳法则,不要和陌生人说话
(六) 控制语句
曾经试过合并代码时,因为没加括号,单条语句合并成两条语句后,仍然认为只有单条语句,另一条语句在循环外执行。
Rule 2. 少用if-else方式,多用哨兵语句式以减少嵌套层次
Rule 3. 限定方法的嵌套层次
Rule 5. 简单逻辑,善用三目运算符,减少if-else语句的编写
Rule 6. 减少使用取反的逻辑
Rule 7. 表达式中,能造成短路概率较大的逻辑尽量放前面,使得后面的判断可以免于执行
Rule 8. switch的规则
1)在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;
2)在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
Rule 9. 循环体中的语句要考量性能,操作尽量移至循环体外处理
1)不必要的耗时较大的对象构造;
2)不必要的try-catch(除非出错时需要循环下去)。
Rule 10. 能用while循环实现的代码,就不用do-while循环
(七) 基本类型与字符串
Rule 1. 原子数据类型(int等)与包装类型(Integer等)的使用原则
1、 需要序列化的POJO类属性使用包装数据类型
2、RPC方法的返回值和参数使用包装数据类型
3、局部变量尽量使用基本数据类型
分支主题
Rule 2. 原子数据类型与包装类型的转换原则
1、自动转换(AutoBoxing)有一定成本,调用者与被调用函数间尽量使用同一类型,减少默认转换
2、自动拆箱有可能产生NPE,要注意处理
Rule 3. 数值equals比较的原则
1、所有包装类对象之间值的比较,全部使用equals方法比较 【*】
2、BigDecimal需要使用compareTo() 【*】
3、Atomic* 系列,不能使用equals方法 【*】
4、 double及float的比较,要特殊处理 【*】
Rule 4. 数字类型的计算原则
1、数字运算表达式,因为先进行等式右边的运算,再赋值给等式左边的变量,所以等式两边的类型要一致 【*】
2、数字取模的结果不一定是正数,负数取模的结果仍然负数【*】
3、double 或 float 计算时有不可避免的精度问题
尽量用double而不用float,但如果是金融货币的计算,则必须使用如下选择:
选项1, 使用性能较差的BigDecimal。BigDecimal还能精确控制四舍五入或是其他取舍的方式。
选项2, 在预知小数精度的情况下,将浮点运算放大为整数计数,比如货币以"分"而不是以"元"计算。
Rule 5. 如果变量值仅有有限的可选值,用枚举类来定义常量
Rule 6. 字符串拼接的原则
1、当字符串拼接不在一个命令行内写完,而是存在多次拼接时(比如循环),使用StringBuilder的append()
2、字符串拼接对象时,不要显式调用对象的toString()【*】
3、使用StringBuilder,而不是有所有方法都有同步修饰符的StringBuffer【*】
4、当拼接后字符串的长度远大于16时,指定StringBuilder的大概长度,避免容量不足时的成倍扩展
5、如果字符串长度很大且频繁拼接,可考虑ThreadLocal重用StringBuilder对象
6、字符操作时,优先使用字符参数,而不是字符串,能提升性能
Rule 7. 利用好正则表达式的预编译功能,可以有效加快正则匹配速度
(八) 集合处理
Rule 1. 底层数据结构是数组的集合,指定集合初始大小
Rule 2. 尽量使用新式的foreach语法遍历Collection与数组
Rule 3. 不要在foreach循环里进行元素的remove/add操作,remove元素可使用Iterator方式 【*】
Rule 4. 使用entrySet遍历Map类集合Key/Value,而不是keySet方式进行遍历【*】
Rule 5. 当对象用于集合时,下列情况需要重新实现hashCode()和equals()【*】
1) 以对象做为Map的KEY时;
2) 将对象存入Set时。
另外,对象放入集合后,会影响hashCode(),equals()结果的属性,将不允许修改。
Rule 6. 高度注意各种Map类集合Key/Value能不能存储null值的情况【*】
Rule 7. 长生命周期的集合,里面内容需要及时清理,避免内存泄漏
长生命周期集合包括下面情况,都要小心处理。
1) 静态属性定义;
2) 长生命周期对象的属性;
3) 保存在ThreadLocal中的集合。
另外,如果使用WeakHashMap保存对象,当对象本身失效时,就不会因为它在集合中存在引用而阻止回收。但JDK的WeakHashMap并不支持并发版本,如果需要并发可使用Guava Cache的实现。
Rule 8. 集合如果存在并发修改的场景,需要使用线程安全的版本
1) 著名的反例,HashMap扩容时,遇到并发修改可能造成100%CPU占用。
推荐使用java.util.concurrent(JUC) 工具包中的并发版集合,如ConcurrentHashMap等,优于使用Collections.synchronizedXXX()系列函数进行同步化封装(等价于在每个方法都加上synchronized关键字)。
例外:ArrayList所对应的CopyOnWriteArrayList,每次更新时都会复制整个数组,只适合于读多写很少的场景。如果频繁写入,可能退化为使用Collections.synchronizedList(list)。
2) 即使线程安全类仍然要注意函数的正确使用。
例如:即使用了ConcurrentHashMap,但直接是用get/put方法,仍然可能会多线程间互相覆盖。
Rule 9. 正确使用集合泛型的通配符
0 条评论
回复 删除
下一页