仓颉编程规范

[TOC]

命名与声明:

  1. 能使用const声明的常量一律使用const声明,使用const声明的标识符代替字面值(魔鬼数字)。

    • 如果是对整个模块、整个包、当前文件所有代码生效的const应当做顶级声明,由不同的可见性修饰
    • const声明的常量一律使用下划线分隔全大写英文单词。
    • const声明的局部变量使用小驼峰命名法。
    • 只有顶级const采用帕斯卡命名法。
    • 代码中不要出现基本类型字面值,一律使用const声明的常量代替。
  2. 不强制要求必须使用const声明传统意义上的常量,大多数复合类型除非是注解或者作为实例化注解的实参,其它情况没有必要为它们声明const构造函数。

    • const函数一律使用小驼峰命名法。
  3. 所有的类、接口一律采用帕斯卡(Pascal)命名法,例:

public class Sample{}
public interface FooBar{}
  1. 所有属性、函数、局部变量、顶级变量一律采用驼峰命名法,例:
let fooBoo = 0
let foo = 0
  1. 所有命名做到见名知义,不要有大段注释说明函数或类型能做什么,尽量做到意义明确的名字和简洁的注释,做到言简意赅。

  2. package命名一律采用蛇型命名,按功能模块划分包名

  3. 下划线不能出现在非const的常量命名中

  4. 数字最好不要出现在命名中

  5. 所有命名一律使用英文,绝对不要用拼音和汉字命名,类名、函数名、属性名、常量名不要有数字,局部变量尽量不要有数字。如果不知道用什么词,有个网站是www.iciba.com http://unbug.github.io/codelf/#欺骗。

  6. 局部变量尽量做到何处使用何处声明

  7. 所有service实现都以ServiceImpl结尾,所有dao、service接口定义都是业务模块名以DAO/Service结尾,类中的DAO Service属性也以DAO/Service结尾。接口不以大写字母I开头,除非业务名称的英文描述以I开头;也不要包含interface这样的单词或缩写,比如inter Inter Interface等。

  8. 抽象类以Abstract开头

  9. 所有异常类名以Exception结束

  10. 关于设计模式

    1. 工厂类名以Factory结尾
    2. 适配器类名以Adaptor结尾
    3. 门面模式类名以Facade结尾
  11. 可以使用约定俗成的缩写命名,比如:conf config cfg fn props attr等

代码风格

  1. 避免不必要的嵌套
  2. 不要出现空的分支,比如下面的代码不能有
if(x < 1){
}else{
    println(x)
}
  1. 不使用@Deprecated标记的api
  2. 不要使用;分隔代码
  3. 每行不要太长,尽量保证一眼看全一行代码。如果代码行太长可在操作符处截断配合缩进标明代码行之间的关系
  4. 一个类完成一类业务逻辑,不同类型的业务逻辑在不同类里实现。
  5. 一个函数完成一件事情。
  6. 尽量实现短小的函数体,大段函数不利于编译优化编译,也不利于阅读。保证一屏能大致显示一个函数实现即可,不要翻好多屏也见不到函数头尾。
  7. 函数体内各个更细致的逻辑之间用空行分割,但是函数体、循环体、match、if/else、类体、结构体、枚举声明代码块内不要出现连续三行以上的空行,否则会增加阅读难度。左花括号与类、结构体、枚举或函数的声明、if、else、for、while,紧接它们的上一行和下一行不要是空行。
  8. 复杂逻辑和曾经出错的地方有注释说明 eg. // fixed by wujingrun, on yyyy-MM-dd HH:mm, 简单描述问题或bug造成的后果
  9. 功能性或API级的类和函数开头有注释说明类的功能和实现人
     /**
       *   本类实现了XXXX,用于XXXX,解决XXX问题
       *   如果是算法实现类,详细说明原理、数据结构、设计思想、参考URL
       */
     public class ClassName{
     /**
       *  本函数实现了XXXX,用于XXXX
       *  @author wujingrun
       *  @param String 参数的意义
       *  @param Int64 参数的意义
       *  @return String
       *  @throws XxxxxException
       */
     public func method(arg0: String, arg1: Int64): Unit {
           …..//不要长久的保留TODO注释,
           …..//如果此处踩过坑,请在此处简单说明
     }
  1. 常量声明前面有注释说明常量的意义
     /**
       *   本常量的业务意义,eg.
       *   当前应用程序版本过低,请您升级应用程序
       */
    public const E0001 = "E0001";
  1. 算法复杂的类或函数,每段逻辑都有简单注释说明原理
/*
 *  implemented by wujingrun
 *  采用的算法、数据结构、设计思想、参考文章的URL
*/
  1. 左花括号与类、结构体、枚举或函数的声明、if、else、for、while、match在同一行,右花括号与它们左对齐
  2. 函数体比函数声明向内缩进四个空格、循环体比for/while向内缩进四个空格、if else代码块比if/else向内缩进四个空格。
  3. 遵守最少知识原则,定义一个函数只接收这个函数所能完成任务的参数
  4. 对于必须调用close()或shutdown()才能释放资源的对象,遵守谁创建对象谁关闭的原则,作为局部变量,在try块外声明变量,在try块内创建并使用对象,在finally块内close()或shutdown()。如果需要释放资源的对象作为属性,相应的类必须实现close/end/shutdown等相关的public函数供对象使用方调用以便释放相关资源。对于实现了Resource接口的类型要使用try(....){….}。对于自定义的需要释放资源的类型也要实现Resource,以便使用方能够使用try(....){.....}方式释放资源。
    try(file = File('/test.txt', Read)
        out = File('/test.txt", Write)){
        ….//do sth
    }
  1. && ||两边要有一个空格,或者代码行以它们结尾,后面的条件与它前面的条件左对齐。例:
a == b && b < c

a == b &&
b < c
  1. 多个逻辑表达式使用圆括号表示逻辑优先级。例:(a == b && b < c) || a > d

  2. 在需要性能和线程安全之间取舍时建议多使用std.collection.concurrent包下的集合。

  3. 最好不用锁,尽量避免竞态条件或多线程同时修改同一个对象。尽量做到一个对象属性由且只由一个线程修改,尽量不要发生线程争用。

  4. 不要采用返回字面值或常量值的方式控制代码流程,此处可以根据不同情况返回同一接口不同实现类的对象,在调用方执行返回的对象的函数,用这种方式完成不同的逻辑流程;而不要在调用方根据函数返回值执行大量if else或match。做到这一条有点难,需要在开发过程中持续改进。

  5. 对于连接字符串的场景,除非能够在一个表达式构造完成的情况可以使用+以外,其它情况一律使用StringBuilder,或者本工具库的StringGenerator。

关于日志

  1. 禁止使用中文记日志,有些操作系统里面没有中文库,没法检索。而且中文也会占用更多字节,浪费带宽。
  2. 禁止使用println记日志
  3. 除了做调试使用输出到控制台的内容,其它情况不要在正式代码中使用println print eprintln eprint等函数,也不要使用其它控制台API。

关于异常与错误

  1. 禁止在提交的代码里面出现printStackTrace()如果需要记录异常堆栈代码,请使用日志API。
  2. 对于应用开发由于参数错误导致的问题,属于业务逻辑范畴,为这些问题定义错误码。模块内部使用副作用管理控制业务执行过程,模块之间使用错误码。
  3. 对于工具库开发,遇到程序无法处理或不知如何处理的错误,都抛出异常。
  4. 公共代码的异常一律抛出,交给业务代码和公共拦截器处理异常,除非当前发生的异常不需要处理否则不要把异常隐藏起来,否则不易查错。
  5. 所有业务处理过的异常都要记日志。log.warn('simple exception description or business message, english only: ',e);