随着《阿里巴巴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、 【强制】需要有统一的后缀或者前缀。为了一看类名,就知道这个类干什么的。
前缀列表:
- 抽象类(Abstract)
- 接口(I)正例:
接口:IViewTag抽象类:AbstractViewTag 具体实现类:UserViewTag复制代码
后缀列表:
- 实体(Entity)。数据库持久对象。
- 表单(Form)。用于封装、校验http参数。
- 数据传输对象(DTO)。用于暴露接口的返回数据
- 基础服务(BaseService)。单实体可以自描述的服务。
- 业务服务(BusinessService)。集合多个单实体的服务。
- 页面服务(ViewService)。涉及到视图页面的服务。
- 模块(Module)。http入口模块。
- 异常(Exception)
- 工具(Util)
- 枚举(Enum)
- 视图标签(ViewTag)
- ....(其他的,比如:Filter之类)正例:
实体: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和setReadprivate boolean isRead复制代码
正例:
# 对应的getter和setter为:isRead和setReadprivate 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 Listlist(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
方法命名格式:- is+动词|形容词
- 动词【+名词|形容词】
例子:
public void isSucess();public void on();public void sendEmail();复制代码
统一命名列表:
- add 新增
- update 修改
- delete 删除
- get 获取单个对象
- list 获取集合对象
- getMap 获取map数据
- count 数量
方法前缀后缀命名说明:
原则上不添加后缀,如果添加后缀的话,如果有添加,命名格式为:updatexxxx4yyyyByzzzz- xxxx:表示对象的属性
- yyyy:表示查询的条件(根据自描述属性查询)
- zzzz:表示查询的条件(根据其他描述属性查询)
例子:
--xxxx情况:用户public void updateName();();public void updateNickName();--yyyy情况:资讯public Listlist4Latest();public List list4Top(); --zzzz情况:课程public List listByTeacher();public List listByKnowledge();--综合使用public List 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层
可以通过以下的方法来消除:- 设计模式
- 抽取方法
- 使用return
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);Mapmap = FormUtil.list(dbDao, sql, pager);List grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());List 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);Mapmap = FormUtil.list(dbDao, sql, pager); List grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());List infoOrigins = dictBaseService.listDictmap.put("queryForm", latentCustomerQueryForm);map.put("grades", grades);map.put("infoOrigins", infoOrigins);return map;复制代码
14、 【推荐】不参与计算的变量不要定义变量
long userId = fetchUser.getCurrentUserId();Sql sql = latentCustomerQueryForm.pager(sqlManager);Mapmap = FormUtil.list(dbDao, sql, pager); List grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());List 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);Mapmap = 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,让架构统一处理
你自己catch,肯定不希望用户看到错误日志,那么 从用户那边看到是正常业务。catch了什么都没干,用户往往看到的是什么都没发生,他会以为网站挂了或者功能快。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、 【推荐】公开的接口,一旦发布成稳定版,禁止修改方法签名(方法名,参数)如果要修改,需要提供新的接口,老的不能修改。因为一修改方法签名。比如:js调用可能就报错了,功能就没办法使用;工具类接口一变,其他项目就会报错了,没办法向下兼容。19、 【推荐】方法放置顺序:public-->protected-->private。一个类,往往使用者更关注的是public的。构造方法、重载方法、雷同方法,按顺序放在一起