java开发规范(进阶版)

代码规范

1️⃣、遵循代码整洁之道里面的规范。

一、函数要短小

  1. 一个函数多小才算短小了?
  2. 20行封顶是最好的,最长不要超过一屏。
  3. 怎么让函数变短小,归纳几点:
    1. 一个函数只做一件事情,从逻辑上减小函数功能
    2. if else for while 语句等,其中的代码块最理想是只有一行,就是一个函数调用语句。
    3. 一个函数里面的缩进层次不应该多于2层,for循环只能有一个,不要嵌套for循环,if嵌套不能多于两层。

二、一个函数只做一件事无副作用

  1. 把一个复杂的流程逐一拆分
  2. 拆分成最小单元的单一功能的函数
  3. 然后在组装起来,完成复杂的功能
  4. 无副作用其实就是只做一件事情,不要你说好做这件事情顺便把另外一件事情也做了。
  5. 这样会让别人误解,也是容易引起bug的地方。主要体现在公共接口上,别人调用的时候不知道你自己偷偷干了另外一件事情。

三、每个函数一个抽象层级

举个栗子:

  1. 我们在外面看见一栋房子的时候,看的是房子这个整体。这个是最大的层级
  2. 然后当我们进入房子之后,我们是看见的整个房间 房间属于第二个抽象层级
  3. 然后我们看见的是房子里面的每一个物品每一个物品就是最小的抽象层级

四、switch语句

Switch语句天生要做N件事情,怎么让函数里的switch语句只做一件事情了?

  1. switch语句尽量放到比较低的抽象层级去处理,这样会简化你的代码复杂度,减小switch的影响力度。
  2. 能不使用就不使用switch语句,因为它会让你的代码出现多个分支,如果是在很高的抽象层级,分支里面再出现分支,代码一下就变得杂乱无章。

五、使用描述性的名称

  1. analysisResult :只知道是解析结果,但是解析完的值怎么处理不知道,
  2. analysisResult2DeveloperAttributeNaturals
  3. 一目了然,解析结果,并把解析完的数据放到对象集合里面。如果是集合或者数组,一定要加s或者list等字样,不然不知道具体是一个对象还是一个对象集合。
  4. 名称别怕长。长而具有描述性的名称,要比短而令人费解的名称好,比写长而描述性的注释好。
  5. 取名规则:动词加名词

六、函数参数个数要尽量少

  1. 最理想的函数参数个数是零,函数参数不要多于三个。
  2. 因为参数不容易让人理解,特别是标识符boolean型参数
  3. 如果一个函数看来需要两个,三个或三个以上的参数,说明其中一些参数需要封装为类了。
  4. 可变参数使用

七、分割指令与询问

  1. 书中讲的是if语句的条件只能去作一个判断,而不要再if条件里面去做一些操作。
  2. 个人理解和一个函数只做一件事情类似

八、Try Catch

  1. 使用异常代替返回错误码
  2. try/catch代码块,一个函数如果有try/catch那么它的第一行代码最好就是trycatchfinally之后最好没有其他代码。
  3. 遵循函数只处理一件事情
  4. 处理错误就是一件事情
  5. 不要再去做其他事情。

、别重复自己

  1. 对于大函数而言,当两个函数有超过50%的相同之处,就需要拆分出来一个公共的接口。
  2. 对于字符串常量(就是指一个写死的字符串,非字符串变量),如果多于两处使用到了相同的字符串常量,就需要定义为公共的字符串常量,这样需要修改的时候你就只需要修改一处了。
  3. 对于小函数而言,本来就已经很小了,都是一个单一功能的函数,大多可以作为公共函数使用。
  4. 现在的编译工具会提示重复代码,当你看见的时候千万不要忽视他们。

​​​​​​​十、结构化编程

    当我们遵循上面的这些规则去编程的时候,其实我们就是在结构化编程。

    不要写大而臃肿的函数,那会让你回头去修改自己代码的时候都会头疼不已。

    大函数阅读性差,改一处牵动全身,你需要更多的时间去确认是否会影响到函数里的其他功能。

    所以让我们养成一个好习惯,写出让别人称赞的好代码。

2️⃣、Controller层代码

    1、Controller层不能书写业务逻辑代码,只能书写一些参数判断和Service调用代码。

    2、Controller层建议接入Swagger接口生成插件,为API写上良好的注释,方便接口联调。

    3、Controller层参数判断建议使用注解方式,因为注解方式代码更整洁,也可以直接抛异常到前端或者调用方,不需要去做业务逻辑的判断和处理。

        3.1、注解建议使用spring的注解,对象的校验使用@Validated ,因为这个注解会抛出一个ConstraintViolationException异常,比较好进行全局异常处理。

        3.2、Controller层还可以抛出一些统一的业务异常,这个需要自己去定义一个全局的业务异常类,然后通过里面的枚举类型,描述具体的什么业务场景下的什么错误。

    4、Controller层接收的业务对象里面都可以使用注解去对字段做一些必填,长度和边界值的校验,比直接写代码校验方便。

    5、Controller层建议都返回JSON对象,避免写EL和FreeMarker语法,不方便前后端分离和API抽取。

3️⃣、Service层代码

    1、Service层代码主要书写业务逻辑。

    2、规范:Service层需要面向接口编程,每个Service都需要编写对应的接口类。

    3、规范:Service层只能调用对应的DAO层,不能跨模块调用其他的DAO,如果需要调用,建议在对应的模块Service层做一个封装,然后调用对应的Service接口,而不是DAO的方法。

    4、建议:原子Service定义:不调用其他Service的Service;复合Service定义:调用多个Service的Service。建议把Service区分为原子Service和复合Service。

    5、规范:Service层的方法里面建议不要出现字符串代码和数字书写,所有的字符串和数字需要定义为常量,字符串定义为常量便于修改,数字定义为常量可读性会很好。

    6、建议:第三方接口统一放到一个包下不同的类管理,一系列同一个第三方的接口放在一个类里面,避免在Service代码里面出现重复的调用第三方代码,当第三方修改接口的时候,对应本项目需要修改的地方出现多处可能会有遗漏。如果统一管理就只需要修改一个地方就好,方便管理。

    7、建议:Service层代码public方法放在最上面,private方法放在下面,按照调用逻辑,被调用方需要在调用方代码下面。

    8、规范:每个Service层都需要有日志输出,输出格式为待定。

    9、建议:属性注入放在Service代码最上面,或者自定义的常量放在最上面,二选一,统一一下,然后是方法。

    10、建议:比较复杂的方法需要写上注释,代码逻辑比较复杂也需要加上一定的注释。建议使用英文写注释,因为中文和会出现乱码情况。

4️⃣、DAO层代码

    1、DAO层主要写持久层和数据库交互的逻辑,不能写业务逻辑。

    2、DAO层的方法必须抽取为共有方法,尽量不要写专用方法,比如写了一个通过某个字段查询一个对象的方法,其实应该抽象为传递多个条件,动态拼装条件求查询对象,这样就可以避免,再来一个属性过滤,还得在写一个方法去过滤新的属性字段。

    3、DAO层尽量不要写SQL语句,除非特殊要求,比如多表关联,hibernate支持就不太好,可以自己写SQL。

    4、DAO层的查询,如果数据是有是否有效字段的(默认大多数都需要,除非特殊表),一定要加上有效字段的过滤,最好是能够抽取到共有接口或者方法里面去实现。很多时候都是忘记写该字段的过滤,导致查出一些本不应该显示的数据。抽取成共有方法可以避免每个DAO层都去添加过滤字段,产生很多重复代码。 

5️⃣、工具类

    1、工具类按照不同功能区分成N个类,相似的两个工具类可以合并。不要出现功能类似但是却有两个工具类,有时候不知道使用哪个工具类。

    2、工具类里面的方法一定要是公用的,不能出现特殊为某一个接口服务的方法,这样的方法可以在基础工具方法上再封装一层,来处理特殊的逻辑。

    3、工具类方法尽量不要修改原有传入的参数,应该通过返回值的方式来返回修改后的内容,不然有时候会出现一些很难定位的问题;还有一种方法就是传递一个参数去接收修改后的内容。常见的做法是在工具方法的参数上加final字段,来限制修改参数内容。

6️⃣、单元测试

    1、所有的Service都必须要有对应的单元测试。

    2、Controller层代码也需要写单元测试,和Service层 稍有不同,需要先判断HTTPStatus,然后判断返回值。

    3、单元测试需要测试边界值,分支测试等

    4、单元测试需要能够重复执行,不能影响其他业务。

 

 

 

 

数据库相关规范

 

数据库修改评审流程。

    1、设计到创建数据库和表的时候需要提交评审

    2、区分开DDL和DML语句,每次修改线上数据库的SQL必须评审。

    3、维护一份数据库的修改记录。最好加上注释,为什么要修改。

 

数据库索引

    1、针对查询频繁的字段,如果不是可枚举的,区分度很小的字段,都建议建立索引。

    2、针对多条件过滤的查询,建议建立多字段的联合索引,不要单独去建立索引,减少不必要的索引。

    3、索引命名规范为idx_字段名称【_number】,索引名称建议都以idx开头加上字段名称,联合所以按照索引左匹配规则,从左到右依次写上对应的字段名称。

 

数据库设计

数据库名称使用下划线连接,千万不要用横线,中划线连接,不然使用数据库名加表名插入数据的时候会报错。

1、字段名称:全部使用小写加下划线的方式命名,每个单词独立,两个单词使用下划线连接,不能两个单词直接连接,单词过长可以使用国际化的简写,如果没有,那就全部书写,不要自己去简写,别人会看不懂。

2、字段类型:boolean类型的一律使用tinyint;

                       enum类型的也使用tinyint

                      主键ID一律使用bigint

                      数字类型的不确定长度的一律使用bigint,只有确定不超过int长度的,使用int

                      日期类型建议使用DateTime,特殊情况下日期类型可以使用varchar存储

                      字符串类型使用varchar,名称、标题等字段建议长度为128;人名建议64;description建议512;content文本内容建议2048及以上,建议的都是2的n次方。

3、 尽量冗余一些前端需要显示的字段

       一张表里经常会关联其他表的信息,正常都会存一个关联ID,但是页面展示的时候需要的是中文名称,这就会导致需要再去查询对应的关联表,获取名称信息,导致多查询一张表,如果返回的是一个列表,那么和数据库的交互次数和关联的表成正比增加,会导致查询延时高。

       特别是一个字段里存了多个关联ID的情况下,显示的时候需要一个一个去查询翻译,很浪费资源,会导致接口延时比较高。

4、多表关联查询

      多表关联查询的时候,关联的表较多情况下,千万不要自己通过service调用主表查询,然后根据外键,循环去查询每一个关联表,速度会很慢,建议通过持久层架构去实现多表关联查询,如果支持不太好,可以自定义返回对象通过原生SQL去查询。                     

5、公共字段定义

公共字段包括 ID,创建时间created_time,更新时间updated_time,创建人,修改人,数据状态字段state,如果是任务类型需要status状态字段。

公共字段定义如下:

字段名称 java字段类型 数据库字段类型 备注
id Long bigint 主键ID
created_time Date DateTime 创建时间
updated_time Date DateTime 修改时间
creator String/Long varchar/bigint 创建人
modifier String/Long varchar/bigint 修改人
state boolean tinyint 数据状态,是否有效:1有效,0无效
status String/enum varchar/tinyint 数据流转状态

 

 

数据库的第一个字段id,只能作为唯一主键,不能作为有特殊意义的字段。比如,不能作为定义的标签的id,标签id应该是有寓意的,必须单独用一个字段来标识。

所有数据都必须有state字段作为数据状态的标识,用来实现逻辑删除,即该条数据是否有效,1有效,0无效。

所有任务类型的数据状态字段请使用status字段作为任务状态字段。

 

6、定义一个公共类,实现公共字段,然后继承该类。

@MappedSuperclass
public class BaseModel {
    @Id
    @GeneratedValue
    @Column(name = "id")
    private Long id;

    @Column(name = "creator")
    private String creator = "";

    @Column(name = "modifier")
    private String modifier = "";

    @Column(name = "status")
    private String status = "";

    @Column(name = "state")
    private Boolean state = true;

    @Column(name = "updated_time")
    private Date updatedTime;

    @Column(name = "created_time")
    private Date createdTime;

}

 

 

 

展开阅读全文

没有更多推荐了,返回首页