命名规范
好的代码本身就是注释, 所以我们需要统一命名风格。
在本文中,将从大到小,从外到内,总结Java编程中的命名规范。文中将会涉及到日常工作中常见的命名示例,如包命名,类命名,接口命名,方法命名,变量命名,常类命名,抽象类命名,异常类命名以及扩展类命名等。我将按照项目工程目录结构,从包,类(接口,抽象类,异常类),方法,变量和常量的顺序展开介绍。
常量命名
1.命名说明
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
2.命名技巧
1、业务含义清晰。
在具名常量时,应该根据该常量所表示的含义,而不是该常量所具有的数值为该抽象事物命名。FIVE 是个很糟的常量名(不论它所代表的值是否为 5.0)。CYCLES_NEBDED 是个不错的名字。CYCLES_NEEDED 可以等于 5.0 或者 6.0。而 FIVE=6.0 就显得太可笑了。出于同样原因,BAKERS_DOZEN 就是个很糟的常量名;而DONUTS_MAX 则很不错。
2、允许任何魔法值(即未经定义的常量)直接出现在代码中。
3、long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字 1 混淆,造成误解。
说明: Long a = 2l; 写的是数字的 21,还是 Long 型的 2?
4、不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。如: 缓存相关的常量放在类: CacheConsts 下;系统配置相关的常量放在类: ConfigConsts 下。
说明 : 大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
5、常量的复用层次有五层: 跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。
- 跨应用共享常量(两个不同的应用): 放置在二方库中,通常是 client.jar 中的 constant 目录下。
- 应用内共享常量(同一个应用): 放置在一方库的 modules 中的 constant 目录下。
反例 : 易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示“是”的变量:
类 A 中: public static final String YES = "yes";
类 B 中: public static final String YES = "y";
A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。
- 同一个应用下的子工程内部共享常量: 即在当前子工程的 constant 目录下。
- 包内共享常量: 即在当前包下单独的 constant 目录下。
- 类内共享常量: 直接在类内部 private static final 定义。
变量命名
1.命名说明
变量名是名词,要正确和清晰地描述业务语义。
2.命名技巧
1、业务含义清晰。避免无业务语义的命名,如:list、val、a...;
2、语境范围精准。
避免小范围词套大范围数据,反之亦然,不使用过于宽泛的名词,如:date 看上去不错,但经过最后推敲它也只是个坏名字,因为这里所说的日期并不是所有的日期均可,而只是特指当前日期;而 date 本身并未表达出这层含义。
3、名词复数。统一风格,加s或List尾缀,变量名建议使用s尾缀,函数名建议使用List尾缀;
4、前置对仗词。位于变量前置部分,用于修饰后面的名词。
- begin/end:beginTime,endTime。
- first/last
- locked/unlocked
- min/max
- next/previous
- old/new
- opened/closed
- visible/invisible
- source/target
- source/destination
- up/down
5、后置限定词。描述名词的作用范围属性,例如:
- 请求入参:xxxQuery/xxxRequest
- 返回结果:xxxResponse/xxxResult
- 传参数据:xxxDTO/xxxVO/xxxInfo
- 运算结果:xxxTotal(总和)/xxxMax(最大值)/xxxAverage(平均值)
6、变量长度控制。
当变量名的平均长度在10 到16 个字符的时候,调试程序所需花费的气力是最小的(1990)。平均名字长度在8到20个字符的程序也几乎同样容易调试。这项原则并不意味着你应该尽量把变量名控制在9到15或者10到16 个字符长。它强调的是,如果你查看自己写的代码时发现了很多更短的名字,那么你需要认真检查,确保这些名字含义足够清晰。
7、为特定类型的数据命名。
在为数据命名的时候,除了通常的考虑事项之外,为一些特定类型数据的命名还要求做出一些特殊的考虑。下面将讲述与循环变量、状态变量、临时变量、布尔变量、枚举类型和具名常量有关的考虑事项。
- 循环变量。无论循环体内还是循环体外,都应该给变量赋予更符合语义的名称,而不是用i、j、k潦草定义。
- 状态变量。为状态变量取一个比 flag 更好的名字 。最好是把标记(flag)看做状态变量,标记的名字中不应该含有 flag,因为你从中丝毫看不出该标记是做什么的。
-
- 坏的名字:statusFlag = 50。除非你亲自写了这段代码,或者有文档能告诉你 statusFlag 和 50的含义。
- 好的名字:dataReady = true;recalcNeeded = false。
- 临时变量。无。
- 布尔变量。
-
- 谨记典型的布尔变量名。done表示某件事情已经完成;error表示有错误发生;found表示已经找到值....
- 使用肯定语气的布尔变量名。否定的名字如 notFound、 notdone 以及 notSuccessful等较难阅读,特别是如果它们被求反:if not notFound。
- 为枚举变量命名。
-
- 常量池则添加组前缀。在使用枚举类型的时候,可以通过使用组前缀,如 Color_,Planet_或者Month_来明确表示该类型的成员都同属于一个组。下面举一些通过前缀来确定枚举类型元素的例子:Color_Red,Color_Blue...
- 枚举类则不添加前缀。 在有些编程语言里,枚举类型的处理很像类,枚举成员也总是被冠以枚举名字前缀,比如 Color.Color_Red 或者 Planet. Planet_Earth(java语言就是这样)。如果你正在使用这样的编程语言,那么重复上述前缀的意义就不大了,因此你可以把枚举类型自身的名字作为前缀,并把上述名字简化为 Color. Red 和 Planet. Earth。
- 具名常量。在具名常量时,应该根据该常量所表示的含义,而不是该常量所具有的数值为该抽象事物命名。FIVE 是个很糟的常量名(不论它所代表的值是否为 5.0)。CYCLES_NEBDED 是个不错的名字。CYCLES_NEEDED 可以等于 5.0 或者 6.0。而 FIVE=6.0 就显得太可笑了。出于同样原因,BAKERS_DOZEN 就是个很糟的常量名;而DONUTS_MAX 则很不错。
8、POJO、DO等实体类中的变量不要以is开头。有些框架解析的时候会出错。
9、RPC‘接口返回值’以及‘接口入参值’,禁止使用枚举;枚举在你的系统内部使用即可。推荐在这个字段上注释一个枚举类型。
3.基本数据类型
基本数据类型是构建其他所有数据类型的构造块 (building blocks )。本章包含了使用数(普遍意义上)、整数、浮点数、字符和字符串、布尔变量、枚举类型、具名常量以及数组的一些技巧。本章的最后一节将讲述如何创建自己的数据类型。
函数命名
1.命名说明
通常采用动词+名词。函数命名要体现做什么,而不是怎么做,要清楚表达出操作意图和业务语义。
2.命名技巧
1、动名词搭配,动词表达操作意图,名词表达业务语义。
public interface MemberFacade {
/**
* 查询会员信息
*
* 命名的问题:
* 1.没有表达操作意图:build是构建,没有表达查询的意图。
* 2.没有表达业务语义:没有表达build的目标对象是什么。
**/
SingleResult<MemberDTO> build(MemberBuildRequest request);
/**
* 更新会员信息
*
* 命名的问题:
* 1.没有表达业务语义:不知道具体更新会员的哪些信息
* 2.操作意图表达过于宽泛:update可以把所有会员信息更新都放在这个函数里。
**/
SingleResult update(MemberUpdateRequest request);
}
public interface MemberFacade {
/**
* 查询会员信息
**/
SingleResult<MemberDTO> queryMember(MemberQuery query);
/**
* 更新会员昵称
**/
SingleResult updateNick(MemberUpdateRequest request);
}
2、名词复数。变量名建议使用s尾缀,函数名建议使用List尾缀;
3、正反操作使用对仗词,例如:
- add/remove
- open/close
- begin/end
- insert/delete
- first/last
- min/max
类命名
1.命名说明
类是面向对象中最重要的概念,是一组关联数据的相关操作的封装,通常可以把类分为两种:
1)实体类:通常采用名词。承载业务的核心数据和业务逻辑,命名要充分体现业务语义,比如Order/Buyer/Item。
2)辅助类:通常采用名词+功能后缀。协调实体类完成业务逻辑,命名通常加后缀体现出其功能性,比如OrderQueryService/OrderRepository。
2.命名技巧
1、辅助类尽量避免用 Helper/Util 之类的后缀,因为其含义过于笼统,容易破坏单一职责原则。
2、针对某个实体的辅助操作过多,或单个操作很复杂,可通过 “实体 + 操作类型 + 功能后缀”来命名,同时符合职责单一和接口隔离的原则,比如OrderService:
- OrderCreateService:订单创建服务;
- OrderUpdateService:订单更新服务;
- OrderQueryService:订单查询服务。
3、如果使用了设计模式,类名中应该体现出具体的模式。将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
public class OrderFactory;
public class LoginProxy;
public class ResourceObserver;
接口命名
1.命名说明
接口(Interface)是一种表述某一类型对象动作的特殊类;简单来说,接口也是类(不太严谨),所以,接口的名称的书写也应该符合类名书写规范。
与普通类名不同的是,接口命名时通常采用形容词或动词来描述接口的动作行为,也可以配合名词一起使用。
2.命名技巧
1、可以interface后缀结尾。
2、Oracle Java中一些标准库的接口使用形容词命名示例:
public interface Closeable{
//...
}
public interface Cloneable{
//...
}
public interface RunnableP{
//...
}
public interface Comparable<T>{
//...
}
public interface CompletionService<V>{
//...
}
public interface Iterable<T>{
//...
}
public interface EventListener{
//...
}
3、在Spring Framework标准库中,通常采用名词+动词/形容词的组合方式来命名接口,下列是Spring Framework中一些接口命名示例:
public interface AfterAdvice{
//...
}
public interface TargetClassAware{
//...
}
public interface ApplicationContextAware{
//...
}
public interface MessageSourceResolvable{
//...
}
抽象类命名
1.命名说明
抽象类(Abstract Class)是一种特殊的类,其命名与普通类的命名规范相当。
一般地,为了将抽象类与普通类和接口做出区别,提高抽象类的可读性,在命名抽象类时,会以“Abstract”/“Base”作为类命的前缀。
2.命名技巧
1、以“Abstract”/“Base”作为类命的前缀。
2、编程中一些常规的命名示例:
public abstract class AbstractRepository<T>{
//...
}
public abstract class AbstractController{
//...
}
public abstract class BaseDao<T,ID>{
//...
}
public abstract class AbstractCommonService<T>{
//...
}
3、Spring Framework中常见的抽象类示例:
public abstract class AbstractAspectJAdvice{
//...
}
public abstract class AbstractSingletonProxyFactoryBean{
//...
}
public abstract class AbstractBeanFactoryPointcutAdvisor{
//...
}
public abstract class AbstractCachingConfiguration{
//...
}
public abstract class AbstractContextLoaderInitializer{
//...
}
异常类命名
1.命名说明
异常类(Exception Class)也是类的一种,但与普通类命名不同的是,异常类在命名时需要使用“Exception”作为其后缀。
另外,在Java中还有另外一类异常类,它们属于系统异常,这一类异常类的命名使用“Error”作为其后缀,以区分Exception(编码,环境,操作等异常)。
2.命名技巧
1、Exception或者Error结尾。
2、exception示例:
public class FileNotFoundException{
//...
}
public class UserAlreadyExistException{
//...
}
public class TransactionException{
//...
}
public class ClassNotFoundException{
//...
}
public class IllegalArgumentException{
//...
}
public class IndexOutOfBoundsException{
//...
}
3、error示例:
public abstract class VirtualMachineError{
//...
}
public class StackOverflowError{
//...
}
public class OutOfMemoryError{
//...
}
public class IllegalAccessError{
//...
}
public class NoClassDefFoundError{
//...
}
public class NoSuchFieldError{
//...
}
public class NoSuchMethodError{
//...
}
测试类命名
1.命名说明
在项目中,测试类采用被测试业务模块名/被测试接口/被测试类+“Test”后缀的方法进行书写,测试类中的测试函数采用“test”+用例操作_状态的组合方式进行书写。
2.命名技巧
1、测试类以Test结尾。
2、测试方法test+用例操作_状态。如:
public class UserServiceTest{
public void testFindByUsernameAndPassword(){
//...
}
public void testUsernameExist_notExist(){
//...
}
public void testDeleteById_isOk(){
//...
}
}
包命名
1.命名说明
通常,包命使用小写英文字母进行命名,并使用“.”进行分割,每个被分割的单元只能包含一个名词。
一般地,包命名常采用顶级域名作为前缀,例如com,net,org,edu,gov,cn,io等,随后紧跟公司/组织/个人名称以及功能模块名称。
package org.springframework.boot.autoconfigure.cloud
package org.springframework.boot.util
package org.hibernate.action
package org.hibernate.cfg
package com.alibaba.druid
package com.alibaba.druid.filter
package com.alibaba.nacos.client.config
package com.ramostear.blog.web
2.命名技巧
1、包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
2、包的命名要大小适中,不能太具体,也不能太抽象。
- 比如类Apple、Orange都是水果,可以收纳进fruit包内。
- 比如包名叫Apple,太具体导致类Orange放不进去。
- 又比如包名叫food,太抽象导致其他非水果也被放进来了。
3、实际工程中,常见的分类维度主要是两种,按功能性或业务域分类。
- 功能性分类:metaq、mapper、service、dao等;
- 业务域分类:user、item、order、promotion等。
4、同一层级的包,要严格保持分类维度的一致性,要么先按业务域分类,再按功能性分类;要么就先按功能性分类,再按业务域分类。
client
|----request
|----order
|----item
|----response
|----order
|----item
|----service
|----OrderQueryService.java
|----ItemQueryService.java
其他命名规范
泛型命名
在书写泛型类时,通常做以下的约定:
- E表示Element,通常用在集合中;
- ID用于表示对象的唯一标识符类型
- T表示Type(类型),通常指代类;
- K表示Key(键),通常用于Map中;
- V表示Value(值),通常用于Map中,与K结对出现;
- N表示Number,通常用于表示数值类型;
- ?表示不确定的Java类型;
- X用于表示异常;
- U,S表示任意的类型。
下面时泛型类的书写示例:
public class HashSet<E> extends AbstractSet<E>{
//...
}
public class HashMap<K,V> extends AbstractMap<K,V>{
//...
}
public class ThreadLocal<T>{
//...
}
public interface Functor<T,X extends Throwable>{
T val() throws X;
}
public class Container<K,V>{
private K key;
private V value;
Container(K key,V value){
this.key = key;
this.value = value;
}
//getter and setter ...
}
public interface BaseRepository<T,ID>{
T findById(ID id);
void update(T t);
List<T> findByIds(ID...ids);
}
public static <T> List<T> methodName(Class<T> clz){
List<T> dataList = getByClz(clz);
return dataList;
}
参考资料
- 《代码大全2》
- 《阿里巴巴开发规范》
- 《阿里技术》