阿里巴巴Java开发规范学习——编程规约(1)

阿里巴巴Java开发规范

一、编程规约

(一) 命名风格

1.【强制】类名使用UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO /

PO / UID 等。
正例 :MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion

  1. DO (Data Object):用于表示领域模型中的数据对象,通常包含业务相关的属性和行为。例如,CustomerDO, ProductDO。
  2. BO (Business Object):用于封装业务逻辑和规则的类,可能包含对多个DO的操作。例如,OrderBO, InvoiceBO。
  3. DTO (Data Transfer Object):主要用于在不同层(如表现层与业务层、服务层之间)传递数据的简单对象,只包含数据成员及简单的getter/setter方法。例如,UserDTO, AddressDTO。
  4. VO (Value Object):在领域驱动设计(DDD)中,用于表示一个不变的、具有语义概念的对象,其属性值共同定义了对象的完整状态,如 ColorVO, MoneyVO。
  5. AO (Application Object):在某些架构中,用于表示应用程序层的实体,负责协调业务逻辑和数据访问。例如,UserAO, RoleAO。
  6. PO (Plain Old Object):通常指那些没有继承任何特殊框架或库提供的基类、未实现特定接口、未被注解标记、不含有任何门面方法(如getter/setter以外的方法)的简单对象。例如,CustomerPO, ItemPO。
  7. UID (Unique Identifier):用于表示唯一标识符的类或接口,如全局唯一ID生成器 UUIDGenerator 或特定类型ID的类 UserId, OrderId。
2. 【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

正例:MAX_FILE_SIZE

反例:maxFileSize

3. 【强制】抽象类命名使用 Abstract 或Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾。
  1. 抽象类命名使用 Abstract 或 Base 开头 :

    这个规则建议在命名抽象类时,以 Abstract 或 Base 作为前缀,以明确表示此类是一个抽象类,即不能被实例化,主要用于定义通用行为和接口,供子类继承实现。这样命名有助于开发者一眼识别出这是一个抽象类,了解其设计意图和用途。例如:AbstractAnimal 或 BaseUserService。

  2. 异常类命名使用 Exception 结尾 :

    异常类通常用于表示程序运行过程中遇到的错误情况。遵循这一规则,将 Exception 作为后缀添加到异常类名中,可以清晰地标识出此类是一个异常类,便于理解和管理程序中的错误处理逻辑。例如:InvalidInputException 或 ResourceNotFoundException。

"抽象类中,可以以 Abstract 结尾吗?"
虽然在技术层面上,以 Abstract 结尾命名抽象类并不违反任何编程语言的语法规定,但在实践中,以 Abstract 或 Base 作为前缀已经是一种广泛接受且易于理解的抽象类命名约定。将 Abstract 放在类名末尾可能会导致以下问题:

  • 可读性降低 :阅读代码时,开发人员习惯于从左到右扫描类名,将表示类性质的关键字放在开头有助于快速识别。将 Abstract 放在末尾可能需要额外的时间和注意力去判断类的性质。
  • 一致性受损 :大部分编程社区和项目倾向于遵循将 Abstract 或 Base 放在抽象类名开头的约定。若您的项目中出现以 Abstract 结尾的抽象类,可能会与团队内的其他代码或业界普遍做法产生不一致,增加理解成本。

综上所述,尽管没有严格禁止以 Abstract 结尾命名抽象类,但从可读性、一致性以及遵循最佳实践的角度考虑,建议遵循将 Abstract 或 Base 放在抽象类名开头的命名规则。

4. 【强制】 POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。

反例:定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),RPC框架在反向解析的时候,"误以为"对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

正例: 避免使用 is 前缀命名布尔类型的变量 :

直接使用描述状态的词汇作为变量名,如 deleted、enabled、visible 等。相应的 getter 方法可以保持 JavaBean 规范,即对于布尔类型使用 is 前缀(如 isDeleted()),但对于属性名本身,则不带 is。

问题背景:

在 Java 中,布尔类型的变量通常用来表示某种状态或属性的真假值。按照习惯,对于表示"是否具有某特性"的布尔变量,有时会采用 is 前缀来命名,如 isDeleted、isEnabled、isVisible 等。这种命名方式符合自然语言习惯,能够直观地传达变量的意义。

然而,在涉及序列化和反序列化的场景中,特别是与某些框架(如 RPC 框架、ORM 框架等)交互时,这种命名可能会导致问题。这是因为部分框架在自动处理对象的序列化和反序列化时,依赖于特定的命名约定来正确映射对象属性到外部格式(如 JSON、XML 或数据库字段)。

问题原因:

  1. JavaBean 规范与框架预期的差异 :

    JavaBean 规范规定,布尔类型的 getter 方法可以使用 is 前缀(如 isDeleted()),而其他类型的属性则使用 get 前缀(如 getName())。当框架尝试根据 getter 方法名反推属性名(去掉 is 或 get 并将首字母小写)时,如果遇到以 is 开头的方法,可能会误以为对应的属性名为 deleted 而不是 isDeleted。这会导致框架在反序列化时找不到实际存在的 isDeleted 属性,从而引发错误。

  2. 外部格式映射规则 :

    在某些框架中,可能直接基于属性名进行序列化或反序列化。如果外部格式(如 JSON)要求属性名遵循特定的命名约定(如全小写、下划线分隔等),那么带有 is 缀的属性名在转换时可能会与预期不符,导致映射失败或错误。

5. 【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。

正例:应用工具类包名为 com.alibaba.ai.util、类名为MessageUtils (此规则参考spring

的框架结构)

6. 【推荐】如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。

说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。

正例:

  • public class OrderFactory;
  • public class LoginProxy;
  • public class ResourceObserver;
7. 【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加)

保持代码的简洁性,并加上有效的Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。

正例:

  • 接口方法签名 void commit();
  • 接口基础常量 String COMPANY = "alibaba";
8. 接口和实现类命名的两套规则:

1)【强制】对于 Service 和DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部

的实现类用Impl 的后缀与接口区别。

正例:CacheServiceImpl 实现CacheService 接口。

2) 【推荐】 如果是形容能力的接口名称,取对应的形容词为接口名 (通常是--able 的形式)。

正例:AbstractTranslator 实现 Translatable 接口。

9. 【参考】枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

说明:枚举其实就是特殊的类,域成员均为常量,且构造方法被默认强制是私有。

正例:枚举名字为ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。

10 .【参考】各层命名规约:

A) Service/DAO 层方法命名规约

1) 获取单个对象的方法用 get 做前缀。

2) 获取多个对象的方法用 list 做前缀,复数形式结尾如:listObjects。

3) 获取统计值的方法用 count 做前缀。

4) 插入的方法用save/insert 做前缀。

5) 删除的方法用remove/delete 做前缀。

6) 修改的方法用update 做前缀。
B) 领域模型命名规约

1) 数据对象:xxxDO,xxx 即为数据表名。

2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。

3) 展示对象:xxxVO,xxx 一般为网页名称。

4) POJO 是DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。

(二) 常量定义

1. 【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。

反例:

java 复制代码
String key ="Id#taobao_" + tradeId;
cache.put(key, value);
2. 【强制】在long 或者Long 赋值时,数值后使用大写的L,不能是小写的 l,小写容易跟数字1 混淆,造成误解。

说明:Long a = 2l; 写的是数字的21,还是 Long 型的2?

3. 【推荐】不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。

说明: 大而全的常量类, 杂乱无章, 使用查找功能才能定位到修改的常量,不利于理解和维护。

正例:缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类 ConfigConsts 下

4. 【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。

1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的constant 目录下。

2) 应用内共享常量:放置在一方库中,通常是子模块中的constant 目录下。

反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示"是"的变量:

类A 中:public static final String YES = "yes"

类B 中:public static final String YES = "y";

A.YES.equals(B.YES),预期是true,但实际返回为false,导致线上问题。

3) 子工程内部共享常量:即在当前子工程的 constant 目录下。

4) 包内共享常量:即在当前包下单独的 constant 目录下。

5) 类内共享常量:直接在类内部 private static final 定义。

5. 【推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。

枚举(Enum)类型的概念与特性:

enum(枚举)是一种特殊的类型,它允许定义一组命名的常量,这些常量通常是相互独立且互斥的。枚举类型在很多编程语言中都有支持,如 Java、C#、Python(通过枚举模块实现)等。枚举类型的优点包括:

  • 类型安全:枚举类型的变量只能被赋予其定义范围内预设的值,不允许赋值为其他未定义的值,增强了代码的健壮性和安全性。
  • 可读性强:枚举成员以具名常量的形式存在,使用有意义的名称代替原始值(如整数或字符串),提高了代码的可读性和自文档化能力。
  • 易于维护:当枚举成员有所增减或值发生变化时,只需修改枚举定义一处,所有引用该枚举的地方都会自动更新,降低了维护成本。

何时使用枚举来定义变量值范围:

当变量的取值仅在一个固定的、有限的范围内变化,并且这些值具有特定的语义时,非常适合使用枚举类型来定义。以下是一些典型的应用场景:

  1. 状态标识 :
    例如,订单的状态可能只有几个固定选项:PENDING(待处理)、PROCESSING(处理中)、COMPLETED(已完成)、CANCELLED(已取消)等。使用枚举可以清晰地表示这些状态,并确保状态值不会被错误地设置为无效值。
java 复制代码
public enum OrderStatus {
    PENDING,
    PROCESSING,
    COMPLETED,
    CANCELLED
}
  1. 星期、月份等预定义集合 :
    例如,表示一周中的星期或一年中的月份,这类值都是固定的且具有明确语义。使用枚举可以使代码更具可读性,并防止拼写错误或使用非法值。
java 复制代码
public enum DayOfWeek {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

public enum Month {
    JANUARY,
    FEBRUARY,
    MARCH,
    APRIL,
    MAY,
    JUNE,
    JULY,
    AUGUST,
    SEPTEMBER,
    OCTOBER,
    NOVEMBER,
    DECEMBER
}
  1. 方向、颜色、角色类型等具有固定选项的分类 :
    例如,表示二维平面上的方向(NORTH, SOUTH, EAST, WEST),用户角色类型(ADMIN, MODERATOR, USER),或者颜色(RED, GREEN, BLUE 等)。这些情况都可以使用枚举来限制变量的取值范围。
java 复制代码
public enum Direction {
    NORTH,
    SOUTH,
    EAST,
    WEST
}

public enum UserRole {
    ADMIN,
    MODERATOR,
    USER
}

public enum Color {
    RED,
    GREEN,
    BLUE,
    // ...
}

总结来说,当变量的取值范围固定且具有明确语义时,使用枚举类型来定义不仅可以提高代码的可读性和安全性,还能简化维护工作。遵循"如果变量值仅在一个固定范围内变化,用 enum 类型来定义"这一规范,有助于提升代码质量,符合良好的编程实践。

相关推荐
鱼宵8 分钟前
Spring AI 流式输出:Flux + SSE 打字机,回答不再干等三秒
java·人工智能·spring·sse·springai·流式输出
shaibdoio23 分钟前
AI主动获客的意图识别工程实现:从规则匹配到模型微调
java·开发语言·人工智能·长沙瞬维ai
for_ever_love__35 分钟前
MySQL 事务隔离级别讲透:MVCC、幻读与四个级别怎么选
java·数据库·mysql·事务·mvcc·不可重复读·幻读
CoderYanger1 小时前
一轮复习——F.动态规划模型总结(入门篇)
java·开发语言·数据结构·算法·leetcode·职场和发展·动态规划
她的男孩1 小时前
打印模板草稿能保存,一点发布就报主从关系:我们把校验拆成了两档
java·spring boot·后端
小鹿的周先生1 小时前
第19章-Agent
java·spring·ai
溪语流沙1 小时前
【Web全栈进阶】FastAPI工程化:APIRouter拆分 + 配置 + 依赖注入
java·前端·fastapi
写后端的胖头鱼1 小时前
Redis 的 RDB 和 AOF
java·数据库·redis
cxoptics2 小时前
LBO 激光损伤阈值 LIDT 典型数值?如何避免晶体打坏?
java
逐米时代2 小时前
远程运维诊断减少到场率
java·服务器·前端