穿梭在银河的火箭队——β冲刺代码规范

穿梭在银河的火箭队 团队 2022-06-04 11:43:17
这个作业属于哪个课程2022年福大-软件工程;软件工程实践-W班
这个作业要求在哪里团队作业——beta冲刺+事后诸葛亮
这个作业的目标代码规范
其它参考文献

目录

  • 1.1 前端代码规范
  • 1.1.1 代码风格
  • 1.1.2 语言特性
  • 1.1.3 浏览器环境
  • 1.2 后端代码规范
  • 1.2.1 编程规约
  • 1.2.1.1 命名风格
  • 1.2.1.2 常量定义
  • 1.2.1.3 代码格式
  • 1.2.1.4 OOP规约
  • 1.2.1.5 控制语句
  • 1.2.1.6 注释规约
  • 1.2.1.7 其它
  • 1.2.2 异常处理
  • 1.2.3 单元测试
  • 1.2.4 MySQL数据库
  • 1.2.4.1 建表规约
  • 1.2.4.2 索引规约
  • 1.2.4.3 SQL语句
  • 1.2.4.4 ORM映射

1.1 前端代码规范

1.1.1 代码风格

  1. 文件

    • JavaScript 文件使用无 BOMUTF-8 编码。
    • 在文件结尾处,保留一个空行。
  2. 结构

    • 缩进
      • 使用 4 个空格做为一个缩进层级,不允许使用 2 个空格 或 tab 字符。
      • switch 下的 casedefault 必须增加一个缩进层级。
    • 空格
      • 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
      • 用作代码块起始的左花括号 { 前必须有一个空格。
      • if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。
      • 在对象创建时,属性中的 : 之后必须有空格,: 之前不允许有空格。
      • 函数声明、具名函数表达式、函数调用中,函数名和 ( 之间不允许有空格。
      • ,; 前不允许有空格。如果不位于行尾,,; 后必须跟一个空格。
      • 在函数调用、函数声明、括号表达式、属性访问、if / for / while / switch / catch 等语句中,()[] 内紧贴括号部分不允许有空格。
      • 单行声明的数组与对象,如果包含元素,{}[] 内紧贴括号部分不允许包含空格.
      • 行尾不得有多余的空格。
    • 换行
      • 每个独立语句结束后必须换行。
      • 每行不得超过 120 个字符。
      • 运算符处换行时,运算符必须在新行的行首。
      • 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 ,; 前换行。
      • 不同行为或逻辑的语句集,使用空行隔开,更易阅读。
      • 在语句的行长度超过 120 时,根据逻辑条件合理缩进。
      • 对于 if...else...try...catch...finally 等语句,推荐使用在 } 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。
    • 语句
      • 不得省略语句结束的分号。
      • if / else / for / do / while 语句中,即使只有一行,也不得省略块 {...}
      • 函数定义结束不允许添加分号。
      • IIFE 必须在函数表达式外添加 (,非 IIFE 不得在函数表达式外添加 (
  3. 命名

    • 变量 使用 Camel命名法
    • 常量 使用 全部字母大写,单词间下划线分隔 的命名方式。
    • 函数 使用 Camel命名法
    • 函数的 参数 使用 Camel命名法
    • 使用 Pascal命名法
    • 类的 方法 / 属性 使用 Camel命名法
    • 枚举变量 使用 Pascal命名法枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式。
    • 命名空间 使用 Camel命名法
    • 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。
    • 类名 使用 名词
    • 函数名 使用 动宾短语
    • boolean 类型的变量使用 ishas 开头。
    • Promise对象动宾短语的进行时 表达。
  4. 注释

    • 单行注释

      必须独占一行。// 后跟一个空格,缩进与下一行被注释说明的代码一致。

    • 多行注释

      避免使用 /*...*/ 这样的多行注释。有多行注释内容时,使用多个单行注释。

    • 文档化注释

      为了便于代码阅读和自文档化,以下内容必须包含以 /**...*/ 形式的块注释中。

      文档注释前必须空一行。

      自文档化的文档说明 what,而不是 how。

    • 类型定义

      • 类型定义都是以 { 开始, 以 } 结束。
      • 对于基本类型 {string}, {number}, {boolean},首字母必须小写。
    • 文件注释

      • 文件顶部必须包含文件注释,用 @file 标识文件说明。
      • 文件注释中可以用 @author 标识开发者信息。
    • 命名空间注释

      命名空间使用 @namespace 标识。

  • 类注释

    • 使用 @class 标记类或构造函数。
    • 使用 @extends 标记类的继承信息。
    • 使用包装方式扩展类成员时, 必须通过 @lends 进行重新指向。
    • 类的属性或方法等成员信息不是 public 的,应使用 @protected@private 标识可访问性。
  • 函数/方法注释

    • 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。
    • 参数和返回值注释必须包含类型信息,且不允许省略参数的说明。
    • 当函数是内部函数,外部不可访问时,可以使用 @inner 标识。
    • 对 Object 中各项的描述, 必须使用 @param 标识。
    • 重写父类方法时, 应当添加 @override 标识。如果重写的形参个数、类型、顺序和返回值类型均未发生变化,可省略 @param@return,仅用 @override 标识,否则仍应作完整注释。
  • 事件注释

    • 必须使用 @event 标识事件,事件参数的标识与方法描述的参数标识相同。
    • 在会广播事件的函数前使用 @fires 标识广播的事件,在广播事件代码前使用 @event 标识事件。
    • 对于事件对象的注释,使用 @param 标识,生成文档时可读性更好。
  • 常量注释

    常量必须使用 @const 标记,并包含说明和类型信息。

  • 复杂类型注释

    对于类型未定义的复杂结构的注释,可以使用 @typedef 标识来定义。

  • AMD 模块注释

    • AMD 模块使用 @module@exports 标识。
    • 对于已使用 @module 标识为 AMD模块 的引用,在 namepaths 中必须增加 module: 作前缀。
    • 对于类定义的模块,可以使用 @alias 标识构建函数。
    • 多模块定义时,可以使用 @exports 标识各个模块。
    • 对于 exports 为 Object 的模块,可以使用@namespace标识。
    • 对于 exports 为类名的模块,使用 @class@exports 标识。

1.1.2 语言特性

  1. 变量

    • 变量、函数在使用前必须先定义。
    • 每个 var 只能声明一个变量。
    • 变量必须 即用即声明,不得在函数或其它形式的代码块起始位置统一声明所有变量。
  2. 条件

    • 在 Equality Expression 中使用类型严格的 ===。仅当判断 nullundefined 时,允许使用 == null
    • 尽可能使用简洁的表达式。
    • 按执行频率排列分支的顺序。
    • 如果函数或全局中的 else 块后没有任何语句,可以删除 else
  3. 循环

    • 不要在循环体中包含函数表达式,事先将函数提取到循环体外。
    • 对循环内多次使用的不变值,在循环外用变量缓存。
  4. 类型

    • 类型检测

      类型检测优先使用 typeof。对象类型检测使用 instanceofnullundefined 的检测使用 == null

    • 类型转换

      • 转换成 string 时,使用 + ''
      • 转换成 number 时,通常使用 +
      • string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt
      • 使用 parseInt 时,必须指定进制。
      • 转换成 boolean 时,使用 !!
      • number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt
  5. 字符串

    • 字符串开头和结束使用单引号 '
    • 使用 数组+ 拼接字符串。
    • 使用字符串拼接的方式生成HTML,需要根据语境进行合理的转义。
    • 复杂的数据到视图字符串的转换过程,选用一种模板引擎。
  6. 对象

    • 使用对象字面量 {} 创建新 Object
    • 对象创建时,如果一个对象的所有 属性 均可以不添加引号,建议所有 属性 不添加引号。
    • 对象创建时,如果任何一个 属性 需要添加引号,则所有 属性 建议添加 '
    • 不允许修改和扩展任何原生对象和宿主对象的原型。
    • 属性访问时,尽量使用 .
    • for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。
  7. 数组

    • 使用数组字面量 [] 创建新数组,除非想要创建的是指定长度的数组。
    • 不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort 方法。
    • 清空数组使用 .length = 0
  8. 函数

    • 函数长度

      一个函数的长度控制在 50 行以内。

    • 参数设计

      • 一个函数的参数控制在 6 个以内。
      • 通过 options 参数传递非数据输入型参数。
    • 闭包

      • 在适当的时候将闭包内大对象置为 null
      • 使用 IIFE 避免 Lift 效应
    • 空函数

      • 空函数不使用 new Function() 的形式。
      • 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。

9.面向对象

  • 类的继承方案,实现时需要修正 constructor
  • 属性在构造函数中声明,方法在原型中声明。
  • 自定义事件的 事件名 必须全小写。
  • 自定义事件只能有一个 event 参数。如果事件需要传递较多信息,应仔细设计事件对象。
  • 设计自定义事件时,应考虑禁止默认行为。
  1. 动态特性

    • eval

      • 避免使用直接 eval 函数。
      • 尽量避免使用 eval 函数。
    • 动态执行代码。

      使用 new Function 执行动态代码。

    • with

      尽量不要使用 with

    • 对象属性

      避免修改外部传入的对象。

1.1.3 浏览器环境

  1. 模块化

    • AMD

      • 使用 AMD 作为模块定义。
      • 模块 id 必须符合标准。
    • define

      • 定义模块时不要指明 iddependencies
      • 使用 return 来返回模块定义。
    • require

      全局运行环境中,require 必须以 async require 形式调用。

  2. DOM

    • 元素获取

      • 对于单个元素,尽可能使用 document.getElementById 获取,避免使用document.all
      • 对于多个元素的集合,尽可能使用 context.getElementsByTagName 获取。其中 context 可以为 document 或其他元素。指定 tagName 参数为 * 可以获得所有子元素。
      • 遍历元素集合时,尽量缓存集合长度。如需多次操作同一集合,则应将集合转为数组。
    • 样式获取

      获取元素实际样式信息时,应使用 getComputedStylecurrentStyle

    • 样式设置

      • 尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置。
      • 通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位。
    • DOM 操作

      • 操作 DOM 时,尽量减少页面 reflow
      • 尽量减少 DOM 操作。
    • DOM 事件

      • 优先使用 addEventListener / attachEvent 绑定事件,避免直接在 HTML 属性中或 DOM 的 expando 属性绑定事件处理。
      • 使用 addEventListener 时第三个参数使用 false
      • 在没有事件自动管理的框架支持下,应持有监听器函数的引用,在适当时候(元素释放、页面卸载等)移除添加的监听器。

1.2 后端代码规范

1.2.1 编程规约

1.2.1.1 命名风格

  1. 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
  2. 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
  3. 类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO
  4. 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。

例:localValue / getHttpMessage() / inputUserId

  1. 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
  2. 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾。
  3. 中括号是数组类型的一部分,数组定义如下:String[] args;
  4. POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
  5. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
  6. 杜绝完全不规范的缩写,避免望文不知义。
  7. 为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组合来表达其意。
  8. 如果模块、接口、类、方法使用了设计模式,在命名时体现出具体模式。
  9. 接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是 与接口方法相关,并且是整个应用的基础常量。
  10. 接口和实现类的命名
  • 对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。

    例:CacheServiceImpl 实现 CacheService 接口。

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

    例:AbstractTranslator 实现 Translatable。

  1. 枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
  2. 各层命名规约:
    • Service/DAO 层方法命名规约
      • 获取单个对象的方法用 get 做前缀。
      • 获取多个对象的方法用 list 做前缀。
      • 获取统计值的方法用 count 做前缀。
      • 插入的方法用 save/insert 做前缀。
      • 删除的方法用 remove/delete 做前缀。
      • 修改的方法用 update 做前缀。
    • 领域模型命名规约
      • 数据对象:xxxDO,xxx 即为数据表名。
      • 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
      • 展示对象:xxxVO,xxx 一般为网页名称。
      • POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。

1.2.1.2 常量定义

  1. 不允许任何魔法值(即未经定义的常量)直接出现在代码中。
  2. long或者Long初始赋值时,使用大写的L,不能是小写的l,小写容易跟数字1混 淆,造成误解。
  3. 不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。
  4. 常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包 内共享常量、类内共享常量。
    • 跨应用共享常量:放置在二方库中,通常是client.jar中的constant目录下。
    • 应用内共享常量:放置在一方库中,通常是modules中的constant目录下。
    • 子工程内部共享常量:即在当前子工程的constant目录下。
    • 包内共享常量:即在当前包下单独的constant目录下。
    • 类内共享常量:直接在类内部private static final定义。
  5. 如果变量值仅在一个范围内变化,且带有名称之外的延伸属性,定义为枚举类。下面 正例中的数字就是延伸信息,表示星期几。

例:public Enum { M0NDAY(1), TUESDAY(2), WEDNESDAY©), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);}

1.2.1.3 代码格式

  1. 大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果 是非空代码块则:
    • 左大括号前不换行。
    • 左大括号后换行。
    • 右大括号前换行。
    • 右大括号后还有else等代码则不换行;表示终止的右大括号后必须换行。
  2. 左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格。
  3. if/for/while/switch/do等保留字与括号之间都必须加空格。
  4. 任何二目、三目运算符的左右两边都需要加一个空格。 说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等。
  5. 采用4个空格缩进,禁止使用tab字符。

说明:如果使用tab缩进,必须设置1个tab为4个空格。IDEA设置tab为4个空格时, 请勿勾选 Use tab character;而在 eclipse 中,必须勾选 insert spaces for tabs

例:(涉及1-5点)

public static void main(String[] args) {



 



//缩进4个空格



 



String say = "hello";



 



//运算符的左右必须有一个空格



 



int flag = 0;



 



//关键词if与括号之间必须有一个空格,括号内的f与左括号,0与右括号不需要空格



 



if (flag == 0)    (



 



System.out.println(say);



 



)



 



//左大括号前加空格且不换行;左大括号后换行



 



if (flag == 1)    (



 



System.out.println("world");



 



//右大括号前换行,右大括号后有else,不用换行



 



) else ( System.out.println("ok");



 



//在右大括号后直接结束,则必须换行



 



)



 



)
  1. 单行字符数限制不超过120个,超出需要换行
  2. 方法参数在定义和传入时,多个参数逗号后边必须加空格。
  3. IDE的text file encoding设置为UTF-8
  4. 方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。

1.2.1.4 OOP规约

  1. 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
  2. 外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。
  3. 当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起, 便于阅读。
  4. 类内方法定义顺序依次是:公有方法或保护方法 > 私有方法> getter/setter方法。
  5. final可以声明类、成员变量、方法、以及本地变量,下列情况使用final关键字:
    • 不允许被继承的类,如:String类。
    • 不允许修改引用的域对象,如:POJO类的域变量。
    • 不允许被重写的方法,如:POJO类的setter方法。
    • 不允许运行过程中重新赋值的局部变量。
    • 避免上下文重复使用一个变量,使用final描述可以强制重新定义一个变量,方便更好 地进行重构。
  6. 慎用Object的clone方法来拷贝对象。
  7. 类成员与方法访问控制从严:
    • 如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
    • 工具类不允许有public或default构造方法。
    • 类非static成员变量并且与子类共享,必须是protected。
    • 类非static成员变量并且仅在本类使用,必须是private。
    • 类static成员变量如果仅在本类使用,必须是private。
    • 若是static成员变量,必须考虑是否为final。
    • 类成员方法只供类内部调用,必须是private。
    • 类成员方法只对继承类公开,那么限制为protectedo

1.2.1.5 控制语句

  1. 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程 序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且 放在最后,即使它什么代码也没有。
  2. 在if/else/for/while/do语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式:if (condition) statements;
  3. 表达异常的分支时,少用if-else方式
  4. 除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复 杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
  5. 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、 获取数据库连接,进行不必要的try-catch操作(这个try-catch是否可以移至循环体外)。
  6. 接口入参保护,这种场景常见的是用于做批量操作的接口。
  7. 下列情形,需要进行参数校验:
    • 调用频次低的方法。
    • 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参 数错误导致中间执行回退,或者错误,那得不偿失。
    • 需要极高稳定性和可用性的方法。
    • 对外提供的开放接口,不管是RPC/API/HTTP接口。
    • 敏感权限入口。
  8. 下列情形,不需要进行参数校验:
    • 极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。
    • 底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底 层才会暴露问题。一般DAO层与Service层都在同一个应用中,部署在同一台服务器中,所 以DAO的参数校验,可以省略。
    • 被声明成private只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参 数已经做过检查或者肯定不会有问题,此时可以不校验参数。

1.2.1.6 注释规约

  1. 类、类属性、类方法的注释必须使用Javadoc规范,不得使用 // xxx方式。
  2. 所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。
  3. 所有的类都必须添加创建者和创建日期。
  4. 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
  5. 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
  6. 与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持 英文原文即可。
  7. 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑 等的修改。
  8. 谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。 说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没 有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
  9. 对于注释的要求:
    1. 能够准确反应设计思想和代码逻辑;
    2. 能够描述业务含 义,使别的程序员能够迅速了解到代码背后的信息。
  10. 好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的 一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
  11. 特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描, 经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
  • 待办事宜(TODO):(标记人,标记时间,[预计处理时间])

    表示需要实现,但目前还未实现的功能。这实际上是一个Javadoc的标签,目前的Javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个Javadoc标签)。

  • 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])

1.2.1.7 其它

  1. 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
  2. velocity调用POJO类的属性时,建议直接使用属性名取值即可,模板引擎会自动按 规范调用POJO的getXxx(),如果是boolean基本数据类型变量(boolean命名不需要加is 前缀),会自动调用isXxx()方法。
  3. 后台输送给页面的变量必须加$!{var}—中间的感叹号。 说明:如果var = null或者不存在,那么${var}会直接显示在页面上。
  4. 注意Math . random()这个方法返回是double类型,注意取值的范围0<x<1 (能够 取到零值,注意除零异常),如果想获取整数类型的随机数,不要将x放大10的若干倍然后 取整,直接使用Random对象的nextInt或者nextLong方法。
  5. 获取当前毫秒数 System.currentTimeMillis();而不是 new Date() .getTime(); 说明:如果想获取更加精确的纳秒级时间值,使用System.nanoTime()的方式。在JDK8中, 针对统计时间等场景,推荐使用Instant类。
  6. 不要在视图模板中加入任何复杂的逻辑。
  7. 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。
  8. 及时清理不再使用的代码段或配置信息。

1.2.2 异常处理

  1. Java类库中定义的一类RuntimeException可以通过预先检查进行规避,而不应该 通过 catch 来处理。
  2. 对大段代码进行try-catch,这是不负责任的表现。catch时请分清稳定代码和非稳 定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分 异常类型,再做对应的异常处理。
  3. 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请 将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的 内容。
  4. 有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回 滚事务。
  5. finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。
  6. 不能在finally块中使用return,finally块中的return返回后方法结束执行,不 会再执行try块中的return语句。
  7. 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。 说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。
  8. 方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分 说明什么情况下会返回null值。调用方需要进行null判断防止NPE问题。
  9. 防止NPE,是程序员的基本修养,注意NPE产生的场景:
    • 返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE。
    • 数据库的查询结果可能为null。
    • 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
    • 远程调用返回对象时,一律要求进行空指针判断,防止NPE。
    • 对于Session中获取的数据,建议NPE检查,避免空指针。
    • 级联调用obj.getA().getB().getC(); 一连串调用,易产生NPE。
  10. 定义时区分unchecked / checked异常,避免直接抛出new RuntimeException(), 更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常。推荐业界已定义 过的自定义异常,如:DAOException / ServiceException 等。
  11. 避免出现重复的代码。

1.2.3 单元测试

  1. 好的单元测试必须遵守AIR原则。
    • A: Automatic (自动化)
    • I: Independent (独立性)
    • R: Repeatable (可重复)
  2. 单元测试应该是全自动执行的,并且非交互式的。测试框架通常是定期执行的,执行 过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测 试中不准使用System.out来进行人肉验证,必须使用assert来验证。
  3. 保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间 决不能互相调用,也不能依赖执行的先后次序。
  4. 单元测试是可以重复执行的,不能受到外界环境的影响。
  5. 对于单元测试,要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级 另U, 一般是方法级别。
  6. 核心业务、核心应用、核心模块的增量代码确保单元测试通过。
  7. 单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。 说明:源码构建时会跳过此目录,而单元测试框架默认是扫描此目录。
  8. 单元测试的基本目标:语句覆盖率达到70%;核心模块的语句覆盖率和分支覆盖率都 要达到100%
  9. 编写单元测试代码遵守BCDE原则,以保证被测试模块的交付质量。
    • B: Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
    • C: Correct,正确的输入,并得到预期的结果。
    • D: Design,与设计文档相结合,来编写单元测试。
    • E: Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得 到预期的结果。
  10. 对于数据库相关的查询,更新,删除等操作,不能假设数据库里的数据是存在的, 或者直接操作数据库把数据插入进去,请使用程序插入或者导入数据的方式来准备数据。
  11. 和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或者 对单元测试产生的数据有明确的前后缀标识。
  12. 对于不可测的代码建议做必要的重构,使代码变得可测,避免为了达到测试要求而 书写不规范测试代码。
  13. 在设计评审阶段,开发人员需要和测试人员一起确定单元测试范围,单元测试最好 覆盖所有测试用例(UC)。
  14. 单元测试作为一种质量保障手段,不建议项目发布后补充单元测试用例,建议在项 目提测前完成单元测试。
  15. 为了更方便地进行单元测试,业务代码应避免以下情况:
    • 构造方法中做的事情过多。
    • 存在过多的全局变量和静态方法。
    • 存在过多的外部依赖。
    • 存在过多的条件语句。

1.2.4 MySQL数据库

1.2.4.1 建表规约

  1. 表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint (1表示是,0表示否)。
  2. 表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只 出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
  3. 表名不使用复数名词。
  4. 禁用保留字,如desc、range、match、delayed等,请参考MySQL官方保留字。
  5. 主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名则为idx_字段名。 说明:pk_ 即 primary key; uk_ 即 unique key; idx_ 即 index 的简称。
  6. 小数类型为decimal,禁止使用float和double。
  7. 如果存储的字符串长度几乎相等,使用char定长字符串类型。
  8. varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长 度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。
  9. 表必备三字段:id, gmt_create, gmt_modified。
  10. 表的命名最好是加上“业务名称_表的作用”。
  11. 库名与应用名称尽量一致。
  12. 如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
  13. 字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
    • 不是频繁修改的字段。
    • 不是varchar超长字段,更不能是text字段。
  14. 单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。
  15. 合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检 索速度。

1.2.4.2 索引规约

  1. 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
  2. 超过三个表禁止join。需要join的字段,数据类型必须绝对一致;多表关联查询时, 保证被关联的字段需要有索引。
  3. 在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据 实际文本区分度决定索引长度即可。
  4. 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
  5. 如果有order by的场景,请注意利用索引的有序性。order by最后的字段是组合 索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查询性能。
  6. 利用覆盖索引来进行查询操作,避免回表。
  7. 利用延迟关联或者子查询优化超多分页场景。
  8. SQL性能优化的目标:至少要达到range级别,要求是ref级别,如果可以是consts 最好。
  9. 建组合索引的时候,区分度最高的在最左边。
  10. 防止因字段类型不同造成的隐式转换,导致索引失效。
  11. 创建索引时避免有如下极端误解:
    • 宁滥勿缺。认为一个查询就需要建一个索引。
    • 宁缺勿滥。认为索引会消耗空间、严重拖慢更新和新增速度。
    • 抵制惟一索引。认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。

1.2.4.3 SQL语句

  1. 不要使用count(列名)或count(常量)来替代count(),count()是SQL92定义的 标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。

  2. count(distinct col)计算该列除NULL之外的不重复行数,注意count(distinct col1, col2)如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。

  3. 当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为 NULL,因此使用sum()时需注意NPE问题。

  4. 使用ISNULL()来判断是否为NULL值。

  5. 在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。

  6. 不得使用外键与级联,一切外键概念必须在应用层解决。

  7. 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。

  8. 数据订正时,删除和修改记录时,要先select,避免出现误删除,确认无误才能执 行更新语句。

  9. in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控 制在1。。。个之内。

  10. 如果有全球化需要,所有的字符存储与表示,均以utf-8编码,注意字符统计函数 的区别。

    如果需要存储表情,那么选择utfmb4来进行存储,注意它与utf-8编码的区别。

  11. TRUNCATE TABLE比DELETE速度快,且使用的系统和事务日志资源少,但TRUNCATE 无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。

1.2.4.4 ORM映射

  1. 在表查询中,一律不要使用星号作为查询的字段列表,需要哪些字段必须明确写明。

  2. POJO类的布尔属性不能加is,而数据库字段必须加is_,要求在resultMap中进行 字段与属性之间的映射。

  3. 不要用resultclass当返回参数,即使所有类属性名与数据库字段一一对应,也需 要定义;反过来,每一个表也必然有一个与之对应。

  4. sql.xml配置参数使用:#{},#param#不要使用${}此种方式容易出现SQL注入。

  5. iBATIS 自带的 queryForList(String statementName, int start, int size)不推 荐使用。

  6. 不允许直接拿HashMap与Hashtable作为查询结果集的输出。

  7. 更新数据表记录时,必须同时更新记录对应的gmt_modified字段值为当前时间。

  8. 不要写一个大而全的数据更新接口。传入为POJO类,不管是不是自己的目标更新字 段,都进行叩date table set c1=value1,c2=value2, c3=value3;这是不对的。执行 SQL 时,不要更新无改动的字段,一是易出错;二是效率低;三是增加binlog存储。

  9. @Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需 要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。

    <isEqual>中的compareValue是与属性值对比的常量,一般是数字,表示相等时带 上此条件;<isNotEmpty>表示不为空且不为null时执行;<isNotNull>表示不为null值时 执行。

...全文
509 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

142

社区成员

发帖
与我相关
我的任务
社区描述
2022年福大-软件工程;软件工程实践-W班
软件工程 高校
社区管理员
  • FZU_SE_teacherW
  • 丝雨_xrc
  • Lyu-
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧