写出出色Java代码,详解阿里编程规约二

本文章难度:『简单』

建议阅读场景:通勤路上

『强制』抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾

解释:在命名时务必做到能『望文生义』,尽量让自己的代码有『自解释性』

『强制』类型与中括号紧挨相连来表示数组

正例:定义整形数组 int[] userArray;

反例:在 main 参数中,使用 String args[]来定义。务必使用String[] args

解释:这样书写便于阅读,一眼就知道是数组;int[] 表示定义一个『int数组』类型,不要理解成『一个存int对象的数组』

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

在 『MySQL 规约』中的『建表约定』第一条,表达『是与否』的值要采用 is_xxx 的命名方式,因此,需要在诸如mybatis框架设置从 is_xxx 到 xxx 的映射关系。

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

解释:这是开发中的一个坑,如果不知道,这个点,很难发现问题。很多时候,我们传入的参数如下:

{  "num":1,  "isDeleted":true}

但是controller收到isDeleted的却是false;

@Setter@Getterpublic class SystemUserVO extends BaseVO {    // 框架误认为这个字段是 deleted    private Boolean isDeleted;}

这就是因为,在反序列化的时候,框架将isDeleted的值误以为是 deleted。

我们为了不踩此类坑,从一开始就规范字段命名。将所有的Boolean类型都不要加is前缀。

防微杜渐,不管是在生活中,还是在写程序时,都是一个好的原则

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

反例:com.mycompay.systemUser / com.mycompay.Controller

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

解释:一点分割一个单词,不要写在一起,com.mycompay.systemUser这种名称,果断拆分成com.mycompay.system.user,不要怕太多单词组成一个包名。如果一开始不这样强制,到后面包名称起名就是一个头疼的东西。

java命名原则:不怕长,怕不清晰。

『强制』避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名,使可读性降低。

说明:子类、父类成员变量名相同,即使是 public 类型的变量也是能够通过编译,而局部变量在同一方法内的不同代码块中同名也是合法的,但是要避免使用。对于非 setter/getter 的参数名称也要避免与成员变量名称相同。

// 反例:public class ConfusingName {    public int age;    public string alibaba;    // 非 setter/getter 的参数名称,不允许与本类成员变量同名    public void getData(String alibaba) {        if(condition) {            final int money = 531;            // ...        }        for (int i = 0; i < 10; i++) {            // 在同一方法体中,不允许与其它代码块中的 money 命名相同            final int money = 615;            // ...        }    }}class Son extends ConfusingName {    //允许与父类的成员变量名称相同    public int age;}

解释:子类,父类成员变量名称相同,相当于是重写了

一句话概括就是:一个类中,属性,参数,变量名称不要重复,类与父类的属性不要重复。

该条原则还是为了代码阅读方便而做的规范。

『强制』杜绝完全不规范的缩写,避免望文不知义。

反例:AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成 condi,此类随意缩写严重降低了代码的可阅读性。

解释:你懂但是别人不懂。

相信只要做过老代码维护的程序员,很大情况都会碰到各种骚缩写,我们尽量不干这种事情,『能写全称就不要缩写』,如果缩写,必须是『行业统一』的缩写。

即使没有codeview的压力,我们敲代码时,也要时刻反省,自己写的代码会不会被骂。

『推荐』为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组合来表达其意。

正例:在 JDK 中,表达原子更新的类名为:AtomicReferenceFieldUpdater。

反例:int a 的随意命名方式。

说明:在jdk的juc的源码中,Doug Lea老爷子写代码,含有大量i k j cnt之类的命名,导致其juc框架可读性极差,我们不要效仿这种命名模式。随便拿ForkJoinPool的一段代码,是不是看着就头疼呢?

final ForkJoinTask<?>[] growArray() {    ForkJoinTask<?>[] oldA = array;    int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;    if (size > MAXIMUM_QUEUE_CAPACITY)        throw new RejectedExecutionException("Queue capacity exceeded");    int oldMask, t, b;    ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];    if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&        (t = top) - (b = base) > 0) {        int mask = size - 1;        do { // emulate poll from old array, push to new array            ForkJoinTask<?> x;            int oldj = ((b & oldMask) << ASHIFT) + ABASE;            int j    = ((b &    mask) << ASHIFT) + ABASE;            x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);            if (x != null &&                U.compareAndSwapObject(oldA, oldj, x, null))                U.putObjectVolatile(a, j, x);        } while (++b != t);    }    return a;}

特例:如果你的代码『不会被扩展和维护』,也『绝对不会有人来读』,也『不会被codeview』,或者你想写只能自己看的懂的有『核心竞争力』的代码,使用这种简单的命名方式,也未尝不可。

『推荐』在常量与变量的命名时,表示类型的名词放在词尾,以提升辨识度。

正例:startTime / workQueue / nameList /TERMINATED_THREAD_COUNT

反例:startedAt/ QueueOfWork / listName /

解释:再次加深印象,命名时,最后一个单词一定是名称。命名的时候,只要你是正常在思考,都不会太离谱!

扩展:建议将通用的常量放在一个常量类文件里,比如

/** * System 字典类型的枚举类 */public interface DictTypeConstants {    String USER_TYPE = "user_type";    String COMMON_STATUS = "common_status";    // ========== SYSTEM 模块 ==========    String USER_SEX = "system_user_sex";    String OPERATE_TYPE = "system_operate_type";    String LOGIN_TYPE = "system_login_type";    String LOGIN_RESULT = "system_login_result";}

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

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

正例:

public class OrderFactory; public class LoginProxy; public class ResourceObserver;

解释:还是为了『望文生义』的要求,减少代码理解难度。

『推荐』接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。

正例:接口方法签名 void commit(); 接口基础常量 String COMPANY = "alibaba";

反例:接口方法定义 public abstract void f();

说明:JDK8 中接口允许有默认实现,那么这个 default 方法,是对所有实现类都有价值的默认实现。

解释:

接口变量:默认就是public static final。

接口方法:默认就是public abstract。

大家都知道的,就没必要累赘。『不要重复』也是编程的第一准绳。

『强制』对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。

例:CacheServiceImpl 实现 CacheService 接口。UserDaoImpl实现UserDao接口。

注意:Manager类不需要有接口定义。

解释:SOA:Service-Oriented Architecture,面向服务架构。服务间只通过接口进行沟通,不关心对方的实现细节。

SOA 的特征(面试常问):

  1. 封装性(明确定义的接口):将服务封装成用于业务流程的『可重用组件』的『应用程序函数』。即使实现方式变化,服务的API保持不变,用户不受影响。
  2. 可重用:一个服务创建后『能用于多个应用和业务流程』。
  3. 松耦合:服务请求者到服务提供者的绑定与『服务之间应该是松耦合』的。因此,服务请求者『不需要知道服务提供者实现的技术细节』,例如程序语言、底层平台等等。
  4. 无状态的服务设计:服务应该是『独立的』、在实现时它不需要获取从一个请求到另一个请求的『信息或状态』。『服务不应该依赖于其他服务的上下文和状态』。当产生依赖时,它们可以定义成通用业务流程、函数和数据模型。
  5. 基于开放标准:当前SOA的实现形式是Web服务,基于的是公开的W3C及其他公认标准.采用第一代Web服务定义的SOAP、WSDL和UDDI以及第二代Web服务定义的WS-*来实现SOA。
  6. 位置透明:用户完全不必知道响应自己需求的服务的位置,甚至不必知道具体是哪个服务参与了响应。
  7. 自治:服务是由组件组成的组合模块,是『自包含和模块化』的,SOA 非常强调架构中提供服务的功能实体的完全独立自主的能力。

『推荐』如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形容词)。

正例:AbstractTranslator 实现 Translatable 接口。

解释:望文生义。

『参考』枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

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

public enum ClientTypeEnum {    // 全部大写,用下划线隔开    PC_MAC(1), H5(2), PC_WIN(1),ALL(0);    private final Integer code;}

『参考』各层命名规约

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。

解释:领域模型是一个分析模型,帮助系统分析人员、用户认识现实业务的工具,描述的是业务中涉及到的实体及其相互之间的关系,它是需求分析的产物,与问题域相关。

DO( Data Object):与数据库表结构一一对应,通过DAO层向上传输数据源对象。

DTO( Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。

BO( Business Object):业务对象。由Service层输出的封装业务逻辑的对象。

VO( View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。

建议做为强制的规约执行

到这里,关于命名风格的规约就已经说完了,纵观这些规则,我们可以得出一个大的原则:

减少代码差异性,便于阅读和沟通

本手册将命名规范放在第一章,正说明了,『命名一致性』的重要程度,万事开头难,在我们项目启动时,专业人士建议第一件事就是,统一命名规范。


非专业的专业人士 原创文章

原创不易请【关注】【点赞】【收藏】

如需转载,请回复说明

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章