Skip to content

开发规范

格式

  1. 使用 IDE 格式化代码,消除各种空格、回车、缩进不规范问题。

  2. 采用 4 个空格缩进,禁止使用 Tab 字符。

  3. 单行字符数限制不超过 120 个,超出需要换行。

  4. 不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以提升可读性。

    注意

    任何情形,没有必要插入多个空行进行隔开。

命名

基本

  1. 为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组合来表达。

    正例

    对某个对象引用的 volatile 字段进行原子更新的类名为 AtomicReferenceFieldUpdater。

    反例

    常见的方法内变量为 int a;的定义方式。

  2. 杜绝完全不规范的缩写,避免望文不知义。

    反例

    AbstractClass“缩写”成 AbsClass;condition“缩写”成 condi;Function 缩写”成 Fu,此类随意缩写严重降低了代码的可阅读性。

  3. 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。

    反例

    name / name / $name / name / name$ / name

  4. 所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。

    反例

    DaZhePromotion [打折] / getPingfenByName() [评分] / String fw[福娃] / int 某变量 = 3

  5. 类名使用 UpperCamelCase 风格,常用对象缩写同样如此:Bo / Dto / Vo 等。

    正例

    ForceCode / HtmlDto / XmlService / TcpUdpDeal / TaPromotion

    反例

    forcecode / HTMLDTO / XMLService / TCPUDPDeal / TAPromotion

  6. 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格。

    正例

    localValue / getHttpMessage() / inputUserId

  7. 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

    正例

    MAX_STOCK_COUNT / CACHE_USER_PASSWORD_EXPIRED_TIME

    反例

    MAX_COUNT / EXPIRED_TIME

  8. 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾;测试方法名以test开头。

  9. 类型与中括号紧挨相连来表示数组。

    正例

    定义整形数组 int[] arrayDemo。

    反例

    String args[]

  10. 类中的任何布尔类型的变量名,不得以 is 开头,否则部分框架解析会引起序列化错误。

  11. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式。类名可以使用复数形式。

    正例

    应用工具类包名为 com.hzcc.common.util、类名为 MessageUtils

    注意

    自研项目包名必须以 com.hzcc 开始。

  12. 避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名,使可理解性降低。

    提示

    子类、父类成员变量名相同,即使是 public 类型的变量也能够通过编译, 另外,局部变量在同一方法内的不同代码块中同名也是合法的,这些情况都要避免。 对于非 setter/getter 的参数名称也要避免与成员变量名称相同。

  13. 在常量与变量的命名时,表示类型的名词放在词尾,以提升辨识度。

    正例

    startTime / workQueue / nameList / TERMINATED_THREAD_COUNT

    反例

    startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD

  14. 如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。

    说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。

    正例

    public class OrderFactory; public class LoginProxy; public class ResourceObserver;

  15. 接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的注释。

    正例

    接口方法签名 void commit(); 接口基础常量 String COMPANY = “alibaba”;

    反例

    接口方法定义 public abstract void f();

  16. 接口的实现类命名用 Impl 的后缀与接口区别。

    正例

    CacheServiceImpl 实现 CacheService 接口。

  17. 如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形容词)。

    正例

    AbstractTranslator 实现 Translatable 接口。

Service

服务模块方法命名规范:

  1. 获取单个对象的方法用 get 做前缀。
  2. 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects。
  3. 获取统计值的方法用 count 做前缀。
  4. 插入的方法用 save/insert/add 做前缀。
  5. 删除的方法用 remove/delete 做前缀。
  6. 修改的方法用 update 做前缀。

Model

领域模型命名规范:

  1. 数据实体:xxx,xxx 即为数据表名。如果表名有统一前缀,则实体名应去除前缀。
  2. 数据传输对象(入参):xxxDto,xxx 为业务领域相关的名称。
  3. 展示对象(出参):xxxVo,用来封装输出数据。
  4. 中间业务对象:xxxBo,用来包装多个Vo或临时存储中间数据。

常量

  1. 不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。

    反例

    String key = "Id#taobao_" + tradeId;

  2. 在 long 或者 Long 赋值时,数值后使用大写字母 L。小写容易跟数字混淆,造成误解。

    反例

    Long a = 2l; 写的是数字的 21,还是 Long 型的 2?

  3. 不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。

    说明:大而全的常量类,杂乱无章,不利于理解,也不利于维护。

    正例

    缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类SystemConfigConsts 下。

  4. 如果变量值仅在一个固定范围内变化用 enum 类型来定义。 枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

    说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。

    正例

    正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。

注释

  1. 注释力求精简准确、表达到位。 能够准确反映设计思想和代码逻辑; 能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。 避免出现过多过滥的注释。

    反例

    java
    // put elephant into fridge
    put(elephant, fridge);

    方法名 put,加上两个有意义的变量名 elephant 和 fridge,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。

  2. 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用// xxx 方式。

    提示

    在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释; 在 IDE中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。

  3. 所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、 除了返回值参数异常说明外,还必须指出该方法做什么事情,实现什么功能。 对子类的实现要求,或者调用注意事项,请一并说明。

  4. 所有的类都必须添加创建者和创建日期,推荐通过 IDE 的代码模板实现。

    正例

    java
    /**
    @author wangdachui
    @date 2020/10/31
    */
  5. 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。

  6. 注释的双斜线与注释内容之间有且仅有一个空格。

    正例

    java
    // 这是示例注释,请注意在双斜线之后有一个空格
    String commentString = new String();
  7. 所有的枚举类型字段必须要有注释,说明每个数据项的用途。

  8. 与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持英文原文即可。

    反例

    “TCP 连接超时”解释成“传输控制协议连接超时”,理解反而费脑筋。

  9. 代码修改的同时,注释也要进行相应的修改,尤其是参数返回值异常核心逻辑等的修改。

    注意

    代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。

  10. 在类中删除未使用的任何字段、方法、内部类;在方法中删除未使用的任何参数声明与内部变量。

  11. 对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方说明注释掉代码的理由。

    提示

    代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。

    前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉,假如需要查阅历史代码,登录代码仓库即可。

    正例

    java
    public static void hello() {
    // 业务方通知活动暂停
    // Business business = new Business();
    // business.active();
    System.out.println(“it’s finished”);
    }
  12. TODO标记 表示需要实现,但目前还未实现的功能。需要添加 待办内容、标记时间、标记人

    java
    //TODO 优化执行效率 20200208 王大锤

语句

  1. if/else/for/while/do 语句中必须使用大括号。

    反例

    java
    if (condition) statements; // 即使只有一行代码,也必须使用大括号。
  2. 当某个方法的代码总行数超过 10 行时,return / throw 等中断逻辑的右大括号后均需要加一个空行。 这样做逻辑清晰,有利于代码阅读时重点关注。

  3. 尽量避免采用取反逻辑运算符。取反逻辑不利于快速理解,并且取反逻辑写法一般都存在对应的正向逻辑写法。

    正例

    正例:使用 if (x < 628) 来表达 x 小于 628。

    反例

    反例:使用 if (!(x >= 628)) 来表达 x 小于 628。

  4. 表达异常的分支时,少用 if-else 方式,可以改写成 if-return。

    正例

    正例:逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句示例如下:

    java
    public 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");
    }
  5. 除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。

    很多 if 语句内的逻辑表达式相当复杂,与、或、取反混合运算,甚至各种方法纵深调用,理解成本非常高。如果赋值一个非常好理解的布尔变量名字,则是件令人爽心悦目的事情。

    正例

    java
    final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
    if (existed) {
      ...
    }

    反例

    java
    public final void acquire ( long arg) {
      if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
        selfInterrupt();
      }
    }

HTTP

  1. 请求路径使用 restful 风格。

    • URL 尽量使用名词而不是动词,因为请求方法已经表达动作意义。推荐使用复数。
    • URL 路径不能使用大写,多个单词之间统一使用“-”分隔。
    • 路径禁止携带表示请求内容类型的后缀,比如”.json”,”.xml”,通过 accept 头表达即可。
    • 使用合适的请求方法:
      • GET:从服务器取出资源。
      • POST:在服务器新建一个资源。
      • PUT:在服务器更新资源。
      • DELETE:从服务器删除资源。
  2. 请求内容:URL 带的参数必须无敏感信息或符合安全要求;body 里带参数时必须设置 Content-Type。

  3. 数据列表相关的接口返回如果为空,则返回空数组[]或空集合{}。

    提示

    此条约定有利于数据层面上的协作更加高效,减少前端很多琐碎的 null 判断。

  4. 服务端发生错误时,返回给前端的响应信息必须包含 HTTP 状态码,code、msg。

  5. 在前后端交互的 JSON 格式数据中,所有的 key 必须为 lowerCamelCase 风格,符合英文表达习惯,且表意完整。

    正例

    errorCode / errorMessage / assetStatus / menuList / orderList / configFlag

    反例

    ERRORCODE / ERROR_CODE / error_message / error-message / errormessage / ErrorMessage

  6. 对于需要使用超大整数的场景,服务端一律使用 String 字符串类型返回,禁止使用Long 类型。

    提示

    后端如果直接返回 Long 整型数据给前端,JS 会自动转换为 Number 类型(注:此类型为双精度浮点数,表示原理与取值范围等同于 Java 中的 Double)。 Long 类型能表示的最大值是 2 的 63 次方-1,在取值范围之内,超过 2 的 53 次方(9007199254740992)的数值转化为 JS 的 Number 时,有些数值会有精度损失。

    反例

    通常在订单号或交易号大于等于 16 位,大概率会出现前后端单据不一致的情况,比如,”orderId”: 362909601374617692,前端拿到的值却是:362909601374617660。

  7. HTTP 请求通过 URL 传递参数时,不能超过 2048 字节。

    提示

    说明:不同浏览器对于 URL 的最大长度限制略有不同,并且对超出最大长度的处理逻辑也有差异,2048字节是取所有浏览器的最小值。

    反例

    某业务将退货的商品 id 列表放在 URL 中作为参数传递,当一次退货商品数量过多时,URL 参数超长,传递到后端的参数被截断,导致部分商品未能正确退货。

  8. HTTP 请求通过 body 传递内容时,必须考虑控制长度,超出最大长度后,后端解析会出错。

    提示

    nginx 默认限制是 1MB,tomcat 默认限制为 2MB,当确实有业务需要传较大内容时,可以通过调大服务器端的限制。

  9. 后端响应的时间格式统一为 “yyyy-MM-dd HH:mm:ss”。

  10. 在接口路径中不要加入版本号,版本控制在 HTTP 头信息中体现,有利于向前兼容。