WIP:新员工手册——编码规范(2020年第二季度)

2020-03-25  本文已影响0人  扁圆柱体

曲突徙薪亡恩泽,焦头烂额为上客?

数据库设计准则

  1. 表名用统一的前缀,不要在表名中使用定制项目的甲方名字
  2. 字段名不要求统一前缀,尽量做到准确表达意思,不要有太多的分段(下划线分割)
  3. 数据库各个实体的定义,不允许使用拼音,包括并不限于:表名,字段名,索引名等
  4. 数据库脚本所有字段和表必须写comment
  5. 合理的命名外键,和对应的表要一目了然
  6. 表字段可以存在适当冗余,但要注意更新数据的难度
  7. 表字段尽量添加不为空属性(not null),int型必须不为空。这样一来可以简化操作(insert,update),二来可以减少对null的检查。不为空属性尽量配置default值,int型根据业务配置0或1等
  8. 能用数字类型,不用varchar类型,varchar类型的宽度要尽量考虑合理
  9. 如果能确定字符串宽度(例如货币/国家缩写),要使用固定宽度的char
  10. 数字类型要区分tinyint, smallint, bigint(java的long型),并在设计时给出合理的宽度(括号内的数字只是一种显示时的宽度,可以结合预期的实际进行填写)
  11. 在java端映射到boolean值的字段,如果肯定是T/F,要使用tinyint(1)
  12. 产品设计给出的字段缺省值,要用default进行定义,例如启用与否,如果缺省是启用,则要置1
  13. 数据库字段只存原始数据,不存计算类数据
  14. 由于精度问题,字段类型尽量不使用浮点数
  15. 所有业务表包含以下通用信息(一般主键和多租户在最前,其余几项在最后),可用于在Java模型层面和各种接口做对应。包括并不限于
    • t_uid(主键)
    • org_code(多租户)
    • version(乐观锁),int(11) not null default 0
    • 审计信息(创建人,创建时间,最后更新人,最后更新时间),not null,确保第一次创建即有值
    • deleted(软删除标记,出于性能考虑,不要使用delete_time是否为空判断),tinyint(1) not null default 0

Java实体对象设计准则

数据库映射对象(Entity)

  1. 实体类名为某种意思表达+entity后缀,和数据库表进行对应
  2. 实体类必须继承自AbstractEntity,保证可以接入通用信息。JPA的方式未来应该不会用,如果用的话,可能要改为实现多个接口的方式(例如IBaseEntity, IAuditable, IOrgCodeable, ISoftDeleteable
  3. 属性名和数据库字段一一对应,用驼峰命名法,属性名必须要写JavaDoc(依据阿里规约插件提示)
  4. varchar,char字段对应String类型;除了bigint对应long以外,其他均对应int类型;not null类型的字段,必须映射为基本类型的属性
  5. 该实体使用的类枚举类型(例如状态,类型等),如果不需要定义枚举的话,必须用final int或final string提前定义,在引用时禁止使用magic number
  6. 实体类对外提供各种getInstance方法重载,原则上不允许业务代码(service层)进行new对象,这样可以更好的控制通用属性,降低产生bug的风险
  7. 实体类尽量提供内部计算/判断方法,对外部隐藏实现,充分实现封装。

接口参数及返回值(Request & Response)

暂略

代码实现准则

基础

  1. 安装阿里规约插件,充分重视idea高亮背景提示和插件提示,并尽量按要求修改
  2. 原则上不允许使用拼音;禁止任何单词拼写错误,关注idea的绿色波浪线;变量定义要有意义,不能使用诸如list,map,set等通用词汇,ijk只能用于for循环;如果用缩写必须符合一般认知,例如不能用add作为address的缩写,要使用addr
  3. 非javaDoc的注释,目的只能是3个,一是对比较复杂的算法进行描述,二是对非常规操作(注意并不一定是不合理)进行解释,三是TODO。注释原则上用中文
  4. 接口必须用javaDoc描述干什么的和怎么用,实现原则上要用javaDoc描写具体实现,特别是复杂的算法
  5. 日志不反对用中文,但鼓励用英文,因为有些终端对中文支持不好;返回给前端的提示,包括异常提示,全部要使用中文
  6. 不使用的代码,或者删除,或者进行详细注释
  7. 明确有改进或未完成的代码,必须使用TODO标记注释(idea用亮黄色以示突出)
  8. 最大化的限制magic number
  9. 变量定义并有所指代后,在其生命周期内,如未发生变化,不能再使用其指代的那段代码/数据
  10. 能用基本类型,不要使用包装类型,有效降低NPE风险
  11. 类枚举字段多代表type,status等含义,无法穷举的字符串多使用name,code等,要合理命名;当使用枚举概念时,必须选择以下两个方案:一是在实体类中用final变量指代其所有含义(适用于较简单的概念),二是使用枚举值(适用于较复杂的概念)
  12. 包装类和对象要使用equals比较,而不是==;基本类型和枚举类可以安全的使用==
  13. 无特殊情况,变量定义应紧贴使用的地方
  14. 避免无意义的continue,break等控制语句,或空程序体,函数体,或永真,永假的判断,特别注意idea的提示(非错误)。如需保留,用注释进行明确说明
  15. 理解“卫语句”的概念。能提前退出方法时,要先实现,避免天梯似的代码缩进
  16. if/else代码块只能是有区分的部分,原则上不允许将公共部分写入,如有需要,请注释理由

优雅的或更强的替代品

  1. Date.from(Instant.now())替换new Date()
  2. 空字符串用StringUtils.EMPTY代替;一个空格的字符串用StringUtils.SPACE代替
  3. StringUtils.subString替换String本身的subString方法,后者不够安全,容易NPE
  4. SimpleDateFormat线程不安全,使用String.format获取字符串的转换
  5. String.valueOf做字符串转换,而不是拼接一个空字符串;用Integer.parseInt做数字转换
  6. 要对预知为数字类型的变量进行cast,最好使用Number这个类,然后在用其intValue, doubleValue做转换,防止castException
  7. 字符串判空要使用StringUtils.isBlank,可实现trim,isEmpty不具备该功能
  8. 利用optional的orElse和orElseGet降低NPE风险。前者适用于固定数据,后者适用于方法
  9. 利用stream替换老式的循环用以转换数据结构的方式
  10. equals的常量翻转使用
  11. 集合类型要使用CollectionUtils.isEmpty()判空,以防止NPE

集合类型

  1. 确定为单个对象,不要使用集合类型进行操作

关注性能

  1. For循环的字符串拼接性能较差,改用StringBuilder或StringUtils的join方法,前者把joiner加在最前面,输出字符串只需要subString(1)即可;后者不用处理多余的joiner
  2. 变量定义的初始值不能随便new一个对象,更不能马上又赋值给另外一个对象。那么这个new出来的对象只能等待垃圾回收
  3. 慎用findAll,一方面是否考虑过多租户下的数据泄露,另一方面要确保不会获取极其大量的数据导致oom
  4. 尽量合并多个for循环,通过合理的设计,避免或减少o(n)复杂度的运算
  5. List等集合类型,在其外部实现for循环时会造成o(n^2)的复杂度,要提前用Map等结构进行转换
  6. 数据检查要先内存(例如参数本身),再类内存环境(缓存,Redis等),最后是数据库或文件IO。在较理想情况下可有效的降低耗费
  7. 如不使用集合类型下标,尽量使用增强型for循环
  8. 不要在集合类型或数组中做无谓的转换,浪费性能。要明确各个集合类型的特点及适用场景
  9. 绝大多数情况下禁止循环访问数据库,要特别注意方法中没有,但循环调用了方法的情况

代码结构

  1. 写代码前一定要脑子清楚,状态不好就休息,不要生产混乱的代码
  2. 保证每个代码块的逻辑单一性,不要穿插着写代码
  3. 对第三方http接口进行封装,禁止直接使用httpClient或restTemplate
  4. 资源访问也进行封装,例如ftp,fastdfs,文件系统等
  5. 一个方法中逻辑判断尽量保持标准的一致,例如判断某种状态,尽管有两个字段或数据结构都可以判断,也要使用其一,尽管两者可能等价
  6. 同样的业务逻辑,即使在不同的类和方法中进行实现,标准或算法要保持一致,甚至要抽象成一个方法。保证在需要变化的时候可有效的降低修改量和bug产生概率
  7. 每个方法要有明确的目的,如果步骤较多,要使用注释STEP n来标记步骤;同一个方法要完成两件事,则要统筹考虑代码执行顺序,不要两件事混着向前推进。如果无关则拆分,如果有关,要注意顺序耦合的问题
  8. 正确区分增删改等操作型方法,和查的只读型方法,不同的操作,对方法返回值的定义也不同
  9. 随着业务拓展,对核心表要做垂直拆分,在代码层体现为新的entity,不要频繁修改核心表及其对应业务逻辑
  10. 多个if/else,要使用策略模式,方法模板模式,工厂模式进行重构
上一篇下一篇

猜你喜欢

热点阅读