随着《阿里巴巴 Java 开发手册》的公开,重新又掀起一股编码规范的风口。结合《华为 java 编程规范》以及团队内部的实践,我们也做了一段开发规范。不求最全,但求有效。
里面的规范,暂时只分两类。"强制",即如果违反就不能使用级别。比如说,在 codereview 有遇到 ,那就会直接把 pull request 打回去,拒绝合并到开发者稳定分支上。"推荐",即建议怎么做,但是不强制,根据不同的水平可以做一些参考。
所有的情况下都通用
1、 【强制】命名全部使用英文,禁止中文或者中英混合。项目名除外,因为有的项目是按域名来命名的,域名本身有可能是中文拼音。
例子:
- 域名:kecheng.xxx.com
- 项目名:xxx-web-kecheng
2、 【强制】禁止使用缩写,除非提供一个缩写列表
反例:
- # 这里的t到底是什么意思?topic_id?还是teacher_id?
- 字段:t_id
3、 【强制】禁止出现除了后缀或者前缀 3 个单词。如果超过 3 个,说明想表达的职责太多,可以拆分或者封装。
这里主要指的是 Java 语言,其他的语言也可以借鉴这些准则
1、 【强制】需要有统一的后缀或者前缀。为了一看类名,就知道这个类干什么的。
前缀列表:
正例:
- 接口:IViewTag
- 抽象类:AbstractViewTag
- 具体实现类:UserViewTag
后缀列表:
正例:
- 实体:UserEntity
- 基础服务:UserBaseService
- 业务服务:AuthorityBusinessService
2、 【强制】所有参与业务的类禁止使用内部类。
1、 【强制】常量必须是:大写 + 下划线,禁止多个单词连在一起
正例:
- private final static String PAGE_SIZE=10;
反例:
- private final static String PAGESIZE=10;
- private final static String pageSize=10;
2、 【强制】布尔类型禁止添加 "is" 前缀。部分框架解析会引起序列化错误。
反例:
- # 对应的getter和setter为:isRead和setRead
- private boolean isRead
正例:
- # 对应的getter和setter为:isRead和setRead
- private boolean read;
3、 【强制】计数器禁止使用复数
反例:
- private int readCounts;
正例:
- private int readCount;
4、 【强制】自描述属性里不要出现类名的描述
反例:
- #UserEntity类
- private String userName;
- private int userAge;
正例:
- #UserEntity类
- private String name;
- private int age;
5、【强制】关联其他实体的属性命名规则:对应的实体去掉后缀 + 用途
正例:
- 属性名:teacherId ,对应的实体是TeacherEntity
- 属性名:favorCount,对应的实体是FavorEntity
反例:
- 属性名:tId。根本不知道是哪个实体的外键。有可能是Teacher有可能是Topic,还得猜半天
6、 【强制】禁止通过定义定义成常量(1,2)来维护类型值,需要通过枚举
反例:
- private final static int SUCESS=1;
- private final static int FAIL=2;
正例:
1、 【强制】接口里的方法禁止有修饰符。
反例:
- #接口里的方法
- public void eat();
正例:
- #接口里的方法
- void eat();
2、 【推荐】方法参数必须使用 final 来修饰。final 可提高程序响应效率。可以通过 Eclipse 的 cleanup 来实现。
正例:
- public void eat(final int size);
反例:
- public void eat(int size);
3、 【强制】每一个方法参数都需要被处理。module 层的方法里的
可以不判空,因为架构已经做处理了,不可能为空。
- 对象参数
被处理指的是:
例子:
- public void add(long userId,String content){
- //异常验证
- ExceptionUtil.checkId(userId,"用户id")
- //直接返回
- if(Util.isEmpty(content)){
- return ;
- }
- }
- public List<CourseEntity> list(int type){
- Cnd cnd = Cnd.limit();
- //有对应的业务逻辑处理
- if(type>0){
- cnd.and("type","=",type);
- }
- return dbDao.query(CourseEntity.class,cnd,null);
- }
4、 【强制】同一个类里有多个一致的参数(3 个以上)的方法,需要抽取接口或者通过实体来承载
反例
- public FavorEntity add(int type,long sourceId,long userId);
- public FavorEntity delete(int type,long sourceId,long userId);
正例
public FavorEntity add(IFavor favor);
public FavorEntity delete(IFavor favor);
5、 【强制】方法名必须是动词或者动宾。http 接口需要知明达意,可以不按这个规则。比如:mycourse,home,banner
方法命名格式:
例子:
- public void isSucess();
- public void on();
- public void sendEmail();
统一命名列表:
方法前缀后缀命名说明:
例子:
- --xxxx情况:用户
- public void updateName();();
- public void updateNickName();
- --yyyy情况:资讯
- public List<NewsEntity> list4Latest();
- public List<NewsEntity> list4Top();
- --zzzz情况:课程
- public List<CourseEntity> listByTeacher();
- public List<CourseEntity> listByKnowledge();
- --综合使用
- public List<CourseEntity> listCourse4TopByTeacher();
6、【强制】一个方法里代码行数不能超过 1 屏(即 30 行)。一般来说超过 30 的行,业务关注点、复杂数比较高,很难维护。超过30行需要封装方法
7、 【强制】局部变量命名不能有连续的名称。连续的命名不具有可维护性。每个变量都需要有清晰的概念。
反例:
- String head1;
- String head2;
正例:
- String title;
- String content;
8、 【强制】禁止有任何魔鬼数据独立存在。可以定义一个有含义的变量来承载
反例:
- if(type ==1){
- //审核成功
- 下面15行代码
- }
正例:
- private final static int SUCESS=1;
- ....
- if(type ==SUCESS){
- 下面15行代码
- }
9、 【强制】判断表达式要使用布尔变量或者封装方法。表达式是变化点。在维护的时候,表达式不知名达意。
反例:
- if(user!=null&&!Util.isEmpty(user.name)&&!Util.isEmpty(user.provicne)){
- //下面15行代码
- }
正例:
- if(isFilledBaseInfo(user)){
- //下面15行代码
- }
10、【强制】if()...else if()...else 个数不能多于 4 个,嵌套不能深于 3 层
可以通过以下的方法来消除:
反例:
- if(isAdmin()){
- ...
- }else if(isTeacher()){
- ...
- }
正例:
- if(isAdmin()){
- ...
- return;
- }
- if(isTeacher()){
- ...
- return;
- }
11、 【推荐】采用防御式编程,先判断错误的业务,然后再写正确的业务。防御式编程结构清晰分明:先把所有错误穷举,然后集中处理正确逻辑。
反例:
- if(null!=user && user.hasAuth()){
- 正确逻辑
- }
正例:
- if(null==user || !user.hasAuth()){
- return;
- }
- 正确逻辑
12、 【推荐】for 里不建议写 io。io 包括:数据库、缓存,文件读写等
13、 【强制】多个不同的结构(业务相近的代码),需要有且只有一个空行
反例:
- long userId = fetchUser.getCurrentUserId();
- Sql sql = latentCustomerQueryForm.pager(sqlManager);
- Map < String,
- Object > map = FormUtil.list(dbDao, sql, pager);
- List < DictInfoEntity > grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
- List < DictInfoEntity > infoOrigins = dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey());
- map.put("queryForm", latentCustomerQueryForm);
- map.put("grades", grades);
- map.put("infoOrigins", infoOrigins);
- return map;
正例:
- long userId = fetchUser.getCurrentUserId();
- Sql sql = latentCustomerQueryForm.pager(sqlManager);
- Map<String, Object> map = FormUtil.list(dbDao, sql, pager);
- List<DictInfoEntity> grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
- List<DictInfoEntity> infoOrigins = dictBaseService.listDict
- map.put("queryForm", latentCustomerQueryForm);
- map.put("grades", grades);
- map.put("infoOrigins", infoOrigins);
- return map;
14、 【推荐】不参与计算的变量不要定义变量
反例:
- long userId = fetchUser.getCurrentUserId();
- Sql sql = latentCustomerQueryForm.pager(sqlManager);
- Map < String,
- Object > map = FormUtil.list(dbDao, sql, pager);
- List < DictInfoEntity > grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
- List < DictInfoEntity > infoOrigins = dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey());
- map.put("queryForm", latentCustomerQueryForm);
- map.put("grades", grades);
- map.put("infoOrigins", infoOrigins);
- return map;
正例:
- long userId = fetchUser.getCurrentUserId();
- Sql sql = latentCustomerQueryForm.pager(sqlManager);
- Map < String,
- Object > map = FormUtil.list(dbDao, sql, pager);
- map.put("grades", dictBaseService.listDict(DictInfoEnum.GRADE.stringKey()));
- map.put("infoOrigins", dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey()));
- return map;
15、 【强制】如果有捕获异常,必须有对应的处理业务。如果没有对应的处理业务,不要捕获,可以直接 throw,让架构统一处理
16、 【强制】禁止使用 exception.getMessge() 处理错误信息。应该使用 exception.toString()。因为 exception.getMessage(),在 npe 抛出异常的时候,什么信息都不显示。
反例:
- } catch (Exception e) {
- logger.error(e.getMessage());
- }
正例:
- } catch (Exception e) {
- logger.error(e.toString());
- }
17、 【强制】禁止使用 System.out.print。统一使用 Eclipse 的 log4e 插件生成日志(不要定义具体的日志实现,要定义的是 slf4j 的接口)
18、 【推荐】公开的接口,一旦发布成稳定版,禁止修改方法签名(方法名,参数)
如果要修改, 需要提供新的接口,老的不能修改。,
19、 【推荐】方法放置顺序:public-->protected-->private。一个类,往往使用者更关注的是 public 的。构造方法、重载方法、雷同方法,按顺序放在一起
1、 【强制】格式结构统一使用 eclipse 模板,禁止自定义。
2、 【强制】类、方法、属性都必须有注释。如果实在来不及,可以先生成 TODO。因为可以通过 TODO 视图,把注解补回来。
3、 【强制】类上必须要有作者,如果有修改,还要添加上修改者,如果有结队也要写上。要有用户名还要有邮箱
例子:
- /**
- * 字典
- *
- * @author ZhuangJunxiang(529572571@qq.com)
- * @version 2017-03-06
- */
4、 【强制】注释要直译,描述要写算法或者思路或者注意事项。不要在注释上代码里的每一行完全暴露出来,使用者根本不关注实现。
反例:
- /**
- * 学生分页查询 #方法里根据没有学生。。。。
- *
- * @param page 分页对象
- * @param studentForm 学生
- * @return 分页对象
- */
- public Page < Map < String,
- Object >> findPageList(Page < Map < String, Object >> page, StudentForm studentForm) {
5、 【强制】方法里禁止写注释。不要有多余的注释,让变量和属性自描述或者抽取方法。如果有算法写到方法注释上。
反例:
- //填充基本信息
- fillBaseInfo();
- //填充账号信息
- fillAccoutInfo();
6、 【强制】方法里禁止注释掉代码。统一通过版本控制软件(git) 来解决。逻辑是正确的,但是现在暂时不能使用,可以暂时注释,但是必须写上 TODO。
例子:
- //TODO 张三 当前用户还未处理,因为登录还没有调通
- //long userId = fetchUser.getCurrentUserId();
- long userId = 1L;
- ....
这里主要指的是 MySQL,其他的数据库也可以借鉴这些准则
1、【强制】统一使用表名命名规范
例子:
- 表名:bc_course 对应的信息:基础服务层,项目缩写为c里对应的CourseEntity实体对应的表
- 表名:sc_course_knowledge_rel 对应的信息:综合服务层,项目缩写为c里对应的CourseEntity实体和KnowledgeEntity实体的关系表
分层:
表的种类:
例子:
- bc_course_group -->CourseGroupEntity
- sc_coursegroup_coursepack_rel -->CourseGroupEntity和CoursePackEntity的关系表
2、 【强制】统一使用 innoDB 引擎。
3、 【推荐】表名不要关联其他表名信息
反例:
- bc_course
- bc_course_video
- bc_course_video_study_log
正例:
- bc_course
- bc_video
- bc_study_log
1、 【强制】业务上具有唯一特性的字段,即使是组合字段,必须使用唯一索引。比如: 用户名,编号等。如果没有添加唯一索引,即使在应用层做了非常完善的校验和控制,只要没有唯一索引,必然有脏数据
2、 【强制】唯一索引命名:uk_字段名, 普通索引命名:idx_字段名
3、 【强制】禁止对 text 定义索引。如果有对这类字段搜索的需求,可以通过全文索引方法来实现功能。
4、 【推荐】varchar 定义索引长度。一般有搜索的话,用户也不会输入太多字。长度统一 10 为倍数,不要超过 50
1、 【强制】主键禁止使用自增。不同库同步数据的时候,会出问题。影响插入性能。
2、 【强制】字段全部禁止为空。为空的话,很容易在使用的时候出现 npe。如果可以不填,通过默认值方法来处理。
3、 【强制】禁止使用外键,只能在概念和应用层次使用外键
4、 【强制】禁止使用枚举、集合类型
5、 【强制】禁止在数据库使用 blog 存在文件。数据库只存相对的 url 路径
6、 【强制】类型使用规范
7、 【强制】必有字段
8、 【推荐】可以适合添加冗余数据,这样可以增快查询数据。
冗余类型:
9、 【推荐】字段顺序:自描述 --> 关联其他表的描述 --> 功能性 --> 必有字段
1、 【强制】表名或者字段注释的格式:直译【(补充说明)】
反例:
- 表名:t_first_login,注释:本表用于处理河北联通卡的临时业务问题,记录已经使用web登录过的卡号
正例:
- 表名:t_first_login,注释:首次登录(用于处理河北联通卡的临时业务问题,记录已经使用web登录过的卡号)
2、 【强制】禁止带 "表","数据" 等多余的字眼
反例:
- 表名:aw_input_batch,注释:输入批次表
- 表名:t_admission_110000,注释:北京院校专业数据
3、 【推荐】如果类型字段,有变更,同步注释
例子:
- status 状态(0:成功,1:失败)
- //过了一段时间,又多了一个冻结的状态
- status 状态(0:成功,1:失败,2:冻结)
1、 【强制】禁止 select *。数据库查看执行时间性能没有响应。但是返回的数据量会变大,对网络开销有影响,最终还是会影响性能,而且也会影响数据库的二进步日志
2、 【强制】使用 select count(*)。select count(name) 不记录 null 的行数。而且官方已经澄清过,不影响性能 。
反例:
- select count(1)
- select count(id)
3、 【强制】使用 sum 函数时,必须使用 IFNULL(sum(),0)。如果 sum 函数没有查到结果返回 null,容易出现 npe。
4、 【推荐】禁止出现 or。可以通过使用 in 或者 unit all 来替换
5、 【推荐】order by 的场景,创建索引时 order by 后面的字段也必须是组合索引的一部分,并且放在索引顺序的最后,避免出现 file_sort
正例:
- where a=? and b=? order by c; 索引:a_b_c
6、 【推荐】创建组合索引时,区分度最高的放在最左边
正例:
- where a=? and b=? 如果a几乎接近唯一,那么只要建idx_a即可。
7、 【推荐】禁止更新表的所有字段,必须指定要更新的字段
原则上, 禁止采用这些技术。除非架构上有这些考虑。
语法:平台 - 分层职责 - 服务名称
例子:
- # uxuexi是平台,web是分层,course是服务名称(因为是web,所以对应的是子域名),对应的域名是course.uxuexi.com
- uxuexi-web-course
- # uxuexi是平台,business是分层,sso是服务名称
- uxuexi-business-sso
- # uxuexi是平台,base是分层,course是服务名称
- uxuexi-base-course
- # we是平台(因为是通用的,没有对应的域名,所以使用we),business是分层,sso是服务名称
- we-business-sso
- # we是平台(因为是通用的,没有对应的域名,所以使用we),core是分层,db是服务名称
- we-core-db
1、 平台
例子:
- 域名:www.uxuexi.com
- -------
- 子域名:www
- 平台名:uxuexi
- 组织:com
2、 分层职责
3、 服务名称
1、 根目录
例子:
- # web层项目,直接使用对应的域名倒置即可
- 项目名:uxuexi-web-course
- 根目录为:com.uxuexi.course
- # 其他层项目,使用:域名组织+项目名
- 项目名:uxuexi-business-sso
- 根目录为:com.uxuexi.business.sso
2、 java 包
分包清单:
例子:
- 包名:module.student.course ,类名:StudentCourseModule
- 包名:module.user,类名:UserModule
- 包名:util,类名:StringUtil
1、 根目录
2、 sql 包
例子:
- # java
- # com.uxuexi.www 根目录
- # module 分包
- # student.course 子模块
- # StudentCourseViewService.java 文件
- com.uxuexi.wwww.module.student.course.StudentCourseViewService.java
- # sql
- # resources 根目录
- # sql 分包
- # student.course 子模块
- # StudentCourseViewService.sql 文件
- resources.sql.student.course.StudentCourseViewService.sql
1、 根目录
2、 视图包
例子:
- # java
- # com.uxuexi.www 根目录
- # module 分包
- # student.course 子模块
- # StudentCourseModule 文件
- # list 方法
- com.uxuexi.wwww.module.student.course.StudentCourseModule.list()
- # 视图
- # WEB-INF 根目录
- # student.course 子模块
- # list.jsp 文件
- WEB-INF.student.course.list.jsp
完整例子:
来源: http://www.cnblogs.com/ansn001/p/6606749.html