前言
参考阿里巴巴开发手册,结合实际情况,对项目常用开发规范进行总结,持续更新中。。。
1、命名
阿里巴巴开发手册中有详细的命名规范,建议遵守。
1.1、严禁使用拼音(国际通用除外)。
1.2、遵从驼峰命名。
1.3、常量命名全部大写,用下划线分隔。
1.4、命名类型
抽象类使用Abstract或Base开头,异常类使用Exception结尾,测试类Test结尾,接口使用Impl结尾或者I开头 (整个项目中,两者选其一:IUserService,UserServiceImpl)。
1.5、缩写规范
正例:number>num,document>doc,string>str。反例:AbstractClass>AbsClass。
1.6、如使用了设计模式,建议在命名时体现出具体的模式。
1.7、Service/DAO层方法命名规约
1)获取单个对象的方法用get做前缀。(getUser)
2)获取多个对象的方法用list做结尾。(getUserList)
3)获取统计值的方法用count做前缀。(countUser)
4)插入方法用insert做前缀。(insertUser)
5)删除方法用delete做前缀。(deleteUser)
6)修改方法用update做前缀。(updateUser)
1.8、领域模型命名规约
1)数据对象:xxxDO,xxx即为数据表名。
2)数据传输对象:xxxDTO,xxx为业务领域相关的名称。
3)展示对象:xxxVO,xxx一般为网页名称。
4)业务对象:xxxBO,xxx一般为一类业务名称
额外建议:命名以能够清晰表达含义为前提,越短越好,合理利用缩写,命名要尽量可读、可搜索、可联想。
2、代码风格
2.1、统一format
public static void main(String[] args) {
String say = "hello";
int flag = 0;
if (flag == 0) {
System.out.println(say);
}
if (flag == 1) {
System.out.println("world");
} else {
System.out.println("ok");
}
}
2.2、一个类、方法有多大?
一般按照sonar中圈复杂度来看,建议一个方法的复杂度不要超过5(复杂度一般就是当前这个方法要覆盖测试用例的个数,比如一个if分支就是1个复杂度),有些公司直接按照代码行数来看,如果一个方法代码超过50,则认为这个方法需要拆分,一般电脑一整屏能够显示的行数差不多也就50行,不过也不能一概而论,结合实际情况,以保证良好的代码阅读性为原则。
2.3、一行代码的长度
单行代码,建议不要超过100个字符,这同样以电脑一整屏能够显示的宽度来看,超过则需要换行。
1) 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2) 运算符与下文一起换行。
3) 方法调用的点符号与下文一起换行。
4) 方法调用时,多个参数,需要换行时,在逗号后进行。
5) 在括号前不要换行,见反例
StringBuffer sb = new StringBuffer();
sb.append("a").append("b")...
.append("c")...
.append("d")...
.append("e");
StringBuffer sb = new StringBuffer();
sb.append("a").append("b")...append
("c");
method(args1, args2, args3, ...
, argsX);
2.4、一个方法中的参数数量
建议一个方法参数数量不要超过5个,参数过多,会影响到代码的可读性、可维护性,考虑到单一职责,建议拆分多个方法来减少参数的数量,如果职责已经单一,但任然需要多个参数,建议封装成对象。
2.5、屏蔽细节
一个方法中太多的细节会影响代码的可读性,可维护性,屏蔽掉细节,isLastDayOfMonth()要做的事情让人一目了然,不至于让别的看代码的同事掉进太细节的具体实现上。
LocalDate today = LocalDate.now();
LocalDate lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
boolean isLastDayOfMonth = Period.between(today, lastDayOfMonth).getDays() == 0;
isLastDayOfMonth();
private static boolean isLastDayOfMonth() {
LocalDate today = LocalDate.now();
LocalDate lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
return Period.between(today, lastDayOfMonth).getDays() == 0;
}
3、常量
3.1、魔法值
用常量来取代魔法值,常量的名称要定义的有意思,使用解释性变量命名,才是解决魔法值存在的目的,否则就是耍流氓。
public static final int LIMIT = 100;
public static final int ORDER_EXPORT_MAX_LIMIT = 100;
3.2、不要使用大而全的常量
不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。
缓存常量:CacheConstant,配置类:ConfigConstant
3.3、常量目录
常量不要到处定义。
1)跨应用共享常量:定义在core包中。
2)应用内共享常量:定义在统一目录中,通常是 modules 中的 constant 目录下。
3)类内共享常量:直接在类内部 private static final 定义。
3.4、枚举
如果变量值带有名称之外的延伸属性,定义为枚举类。
public Enum {
MONDAY(1),
TUESDAY(2),
WEDNESDAY(3),
THURSDAY(4),
FRIDAY(5),
SATURDAY(6),
SUNDAY(7);
}
3.5、集合、数组常量定义规范
public static final List<Integer> CONST_LIST = Collections.unmodifiableList(Arrays.asList(1, 2, 3));
public static final Set<Integer> CONST_SET = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(1, 2, 3)));
public static final Map<String, String> CONST_VALUE_MAP;
static {
Map<String, String> valueMap = new HashMap<>();
valueMap.put("k1", "v1");
valueMap.put("k2", "v2");
valueMap.put("k3", "v3");
CONST_VALUE_MAP = Collections.unmodifiableMap(valueMap);
}
4、统一工具类
项目中用到的工具类,统一提供,比如有:日期工具类、集合工具类、数值计算工具类。
5、并发相关
5.1、线程池
需要使用线程的地方,一律通过线程池提供,线程池不要使用Executors创建,必须通过ThreadPoolExecutor创建,并指定有意义的线程名。
5.2、事务
禁止直接在service方法上定义@Transactional,方法上定义注意会导致事务失效的情况,原则上尽量缩小事务的范围。
5.3、锁
1)一个原则,能无锁就不要用锁,能用乐观锁就不用悲观锁(如果每次访问冲突概率小于 20%,推荐使用乐观锁),能锁代码块就不锁整个方法体,能锁对象就不锁类。
2)注意加锁、释放锁的顺序,避免死锁。
5.4、线程安全
数值类型使用:AtomicXXX
日期类型使用:可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat。
其他常见线程安全类:
StringBuilder(线程不安全)、StringBuffer(线程安全)。
Map、Set、List等线程安全的类。
6、注释
1)注释的主要内容建议包含三个方面:做什么、为什么、怎么做。
2)注释要起到总结性作用,要能够让代码结构看起来更清晰。
3)TODO 标记待办事项,需要确认处理时间
4)FIXME 标记错误,需要确认处理时间
7、日志
1)统一使用日志框架SLF4J。
2)注意记录日志级别,按照debug、info、warn、error级别合理记录。
由其是error级别的应该只用来记录系统逻辑出错,产生异常等重要错误信息。遇到过乱记录error级别的情况,结果在检索时问题时,根据error关键字搜索,全是各种业务校验等类型的错误,严重影响了正常的问题排查。
3)不要在同一个上下文中出现重复的日志打印,不要无脑的对日志内容进行额外转换输出。
4)大量、无效的日志输出,一定会影响系统的性能,对于自己记录的日志,要搞清楚这些日志有人看吗?看到后有什么作用?对你定位、排查问题有什么帮助?
5)在catch中记录的日志,注意记录异常堆栈信息。
异常
1)在三层架构中一般抛到controller层的异常,可以用AOP的方式来处理,避免大量的重复代码。 2)业务类的异常建议自定义,并使用类似枚举类的方式定义异常码。 3)不要用异常来做流程控制,比如在A方法调用B方法,如果B方法抛出xxx异常,则A方法执行xxx逻辑。这种使用方式违背了异常设计的初衷。 4)自定义的异常最好能分门别类,比如,校验类定义:VerificationException,参数解析定义:ParseException,状态问题定义:StatusException,还是熟悉的类似于MyBatis中多个结果集异常:TooManyResultsException。
如果不清楚具体自定义以及规范的使用方式,你可以直接找个优秀的项目源码,看看别人是如何处理的。
5)如果在有事务的方法中catch了异常,请注意是否需要事务的回滚,避免造成事务失效。 6)如非必要,不要在一段循环代码中,进行try catch。
|