开发规范
格式
使用 IDE 格式化代码,消除各种空格、回车、缩进不规范问题。
采用 4 个空格缩进,禁止使用 Tab 字符。
单行字符数限制不超过 120 个,超出需要换行。
不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以提升可读性。
注意
任何情形,没有必要插入多个空行进行隔开。
命名
基本
为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组合来表达。
正例
对某个对象引用的 volatile 字段进行原子更新的类名为 AtomicReferenceFieldUpdater。
反例
常见的方法内变量为 int a;的定义方式。
杜绝完全不规范的缩写,避免望文不知义。
反例
AbstractClass“缩写”成 AbsClass;condition“缩写”成 condi;Function 缩写”成 Fu,此类随意缩写严重降低了代码的可阅读性。
代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
反例
name / name / $name / name / name$ / name
所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
反例
DaZhePromotion [打折] / getPingfenByName() [评分] / String fw[福娃] / int 某变量 = 3
类名使用 UpperCamelCase 风格,常用对象缩写同样如此:Bo / Dto / Vo 等。
正例
ForceCode / HtmlDto / XmlService / TcpUdpDeal / TaPromotion
反例
forcecode / HTMLDTO / XMLService / TCPUDPDeal / TAPromotion
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格。
正例
localValue / getHttpMessage() / inputUserId
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
正例
MAX_STOCK_COUNT / CACHE_USER_PASSWORD_EXPIRED_TIME
反例
MAX_COUNT / EXPIRED_TIME
抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾;测试方法名以test开头。
类型与中括号紧挨相连来表示数组。
正例
定义整形数组 int[] arrayDemo。
反例
String args[]
类中的任何布尔类型的变量名,不得以 is 开头,否则部分框架解析会引起序列化错误。
包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式。类名可以使用复数形式。
正例
应用工具类包名为 com.hzcc.common.util、类名为 MessageUtils
注意
自研项目包名必须以 com.hzcc 开始。
避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名,使可理解性降低。
提示
子类、父类成员变量名相同,即使是 public 类型的变量也能够通过编译, 另外,局部变量在同一方法内的不同代码块中同名也是合法的,这些情况都要避免。 对于非 setter/getter 的参数名称也要避免与成员变量名称相同。
在常量与变量的命名时,表示类型的名词放在词尾,以提升辨识度。
正例
startTime / workQueue / nameList / TERMINATED_THREAD_COUNT
反例
startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD
如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。
说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
正例
public class OrderFactory; public class LoginProxy; public class ResourceObserver;
接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的注释。
正例
接口方法签名 void commit(); 接口基础常量 String COMPANY = “alibaba”;
反例
接口方法定义 public abstract void f();
接口的实现类命名用 Impl 的后缀与接口区别。
正例
CacheServiceImpl 实现 CacheService 接口。
如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形容词)。
正例
AbstractTranslator 实现 Translatable 接口。
Service
服务模块方法命名规范:
- 获取单个对象的方法用 get 做前缀。
- 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects。
- 获取统计值的方法用 count 做前缀。
- 插入的方法用 save/insert/add 做前缀。
- 删除的方法用 remove/delete 做前缀。
- 修改的方法用 update 做前缀。
Model
领域模型命名规范:
- 数据实体:xxx,xxx 即为数据表名。如果表名有统一前缀,则实体名应去除前缀。
- 数据传输对象(入参):xxxDto,xxx 为业务领域相关的名称。
- 展示对象(出参):xxxVo,用来封装输出数据。
- 中间业务对象:xxxBo,用来包装多个Vo或临时存储中间数据。
常量
不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。
反例
String key = "Id#taobao_" + tradeId;
在 long 或者 Long 赋值时,数值后使用大写字母 L。小写容易跟数字混淆,造成误解。
反例
Long a = 2l; 写的是数字的 21,还是 Long 型的 2?
不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。
说明:大而全的常量类,杂乱无章,不利于理解,也不利于维护。
正例
缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类SystemConfigConsts 下。
如果变量值仅在一个固定范围内变化用 enum 类型来定义。 枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
正例
正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。
注释
注释力求精简准确、表达到位。 能够准确反映设计思想和代码逻辑; 能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。 避免出现过多过滥的注释。
反例
java// put elephant into fridge put(elephant, fridge);方法名 put,加上两个有意义的变量名 elephant 和 fridge,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。
类、类属性、类方法的注释必须使用 Javadoc 规范,使用
/**内容*/格式,不得使用// xxx方式。提示
在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释; 在 IDE中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、 除了
返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。 对子类的实现要求,或者调用注意事项,请一并说明。所有的类都必须添加创建者和创建日期,推荐通过 IDE 的代码模板实现。
正例
java/** @author wangdachui @date 2020/10/31 */方法内部单行注释,在被注释语句上方另起一行,使用
//注释。方法内部多行注释使用/* */注释,注意与代码对齐。注释的双斜线与注释内容之间有且仅有一个空格。
正例
java// 这是示例注释,请注意在双斜线之后有一个空格 String commentString = new String();所有的枚举类型字段必须要有注释,说明每个数据项的用途。
与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持英文原文即可。
反例
“TCP 连接超时”解释成“传输控制协议连接超时”,理解反而费脑筋。
代码修改的同时,注释也要进行相应的修改,尤其是
参数、返回值、异常、核心逻辑等的修改。注意
代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
在类中删除未使用的任何字段、方法、内部类;在方法中删除未使用的任何参数声明与内部变量。
对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方说明注释掉代码的理由。
提示
代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。
前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉,假如需要查阅历史代码,登录代码仓库即可。
正例
javapublic static void hello() { // 业务方通知活动暂停 // Business business = new Business(); // business.active(); System.out.println(“it’s finished”); }TODO标记 表示需要实现,但目前还未实现的功能。需要添加 待办内容、标记时间、标记人
java//TODO 优化执行效率 20200208 王大锤
语句
在
if/else/for/while/do语句中必须使用大括号。反例
javaif (condition) statements; // 即使只有一行代码,也必须使用大括号。当某个方法的代码总行数超过 10 行时,return / throw 等中断逻辑的右大括号后均需要加一个空行。 这样做逻辑清晰,有利于代码阅读时重点关注。
尽量避免采用取反逻辑运算符。取反逻辑不利于快速理解,并且取反逻辑写法一般都存在对应的正向逻辑写法。
正例
正例:使用 if (x < 628) 来表达 x 小于 628。
反例
反例:使用 if (!(x >= 628)) 来表达 x 小于 628。
表达异常的分支时,少用 if-else 方式,可以改写成 if-return。
正例
正例:逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句示例如下:
javapublic void findBoyfriend (Man man) { if (man.isUgly()) { System.out.println("......"); return; } if (man.isPoor()) { System.out.println("......"); return; } if (man.isBadTemper()) { System.out.println("......"); return; } System.out.println("good"); }除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
很多 if 语句内的逻辑表达式相当复杂,与、或、取反混合运算,甚至各种方法纵深调用,理解成本非常高。如果赋值一个非常好理解的布尔变量名字,则是件令人爽心悦目的事情。
正例
javafinal boolean existed = (file.open(fileName, "w") != null) && (...) || (...); if (existed) { ... }反例
javapublic final void acquire ( long arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) { selfInterrupt(); } }
HTTP
请求路径使用 restful 风格。
- URL 尽量使用名词而不是动词,因为请求方法已经表达动作意义。推荐使用复数。
- URL 路径不能使用大写,多个单词之间统一使用“-”分隔。
- 路径禁止携带表示请求内容类型的后缀,比如”.json”,”.xml”,通过 accept 头表达即可。
- 使用合适的请求方法:
- GET:从服务器取出资源。
- POST:在服务器新建一个资源。
- PUT:在服务器更新资源。
- DELETE:从服务器删除资源。
请求内容:URL 带的参数必须无敏感信息或符合安全要求;body 里带参数时必须设置 Content-Type。
数据列表相关的接口返回如果为空,则返回空数组[]或空集合{}。
提示
此条约定有利于数据层面上的协作更加高效,减少前端很多琐碎的 null 判断。
服务端发生错误时,返回给前端的响应信息必须包含 HTTP 状态码,code、msg。
在前后端交互的 JSON 格式数据中,所有的 key 必须为 lowerCamelCase 风格,符合英文表达习惯,且表意完整。
正例
errorCode / errorMessage / assetStatus / menuList / orderList / configFlag
反例
ERRORCODE / ERROR_CODE / error_message / error-message / errormessage / ErrorMessage
对于需要使用超大整数的场景,服务端一律使用 String 字符串类型返回,禁止使用Long 类型。
提示
后端如果直接返回 Long 整型数据给前端,JS 会自动转换为 Number 类型(注:此类型为双精度浮点数,表示原理与取值范围等同于 Java 中的 Double)。 Long 类型能表示的最大值是 2 的 63 次方-1,在取值范围之内,超过 2 的 53 次方(9007199254740992)的数值转化为 JS 的 Number 时,有些数值会有精度损失。
反例
通常在订单号或交易号大于等于 16 位,大概率会出现前后端单据不一致的情况,比如,”orderId”: 362909601374617692,前端拿到的值却是:362909601374617660。
HTTP 请求通过 URL 传递参数时,不能超过 2048 字节。
提示
说明:不同浏览器对于 URL 的最大长度限制略有不同,并且对超出最大长度的处理逻辑也有差异,2048字节是取所有浏览器的最小值。
反例
某业务将退货的商品 id 列表放在 URL 中作为参数传递,当一次退货商品数量过多时,URL 参数超长,传递到后端的参数被截断,导致部分商品未能正确退货。
HTTP 请求通过 body 传递内容时,必须考虑控制长度,超出最大长度后,后端解析会出错。
提示
nginx 默认限制是 1MB,tomcat 默认限制为 2MB,当确实有业务需要传较大内容时,可以通过调大服务器端的限制。
后端响应的时间格式统一为 “yyyy-MM-dd HH:mm:ss”。
在接口路径中不要加入版本号,版本控制在 HTTP 头信息中体现,有利于向前兼容。