一、开发规范
本开发规范大部分是基于《阿里巴巴泰山版java开发手册》进行的内容修改,规范大都无正确与否,更多的是结合自身实际制定并严格遵守即可
(一)命名风格
- 【强制】代码中类名采用首字母大写的驼峰标识,方法和变量则采用驼峰标识,所有命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束
- 反例:name / name / $name / name / name$ / name
(二)代码格式
-
【强制】如果是大括号内为空,则简洁地写成{}即可,大括号中间无需换行和空格;如果是非空代码块则:
1)左大括号前不换行
2)左大括号后换行
3)右大括号前换行
4)右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行
-
【强制】左小括号和右边相邻字符之间不出现空格;右小括号和左边相邻字符之间也不出现空格;而左大括号前需要加空格。详见第 5 条下方正例提示。
- 反例:if (空格 a == b 空格)
- 【强制】if/for/while/switch/do 等保留字与括号之间都必须加空格
- 【强制】任何二目、三目运算符的左右两边都需要加一个空格 说明:包括赋值运算符=、逻辑运算符&&、加减乘除符号等
- 【强制】采用 4 个空格缩进,禁止使用 tab 字符
- 说明:如果使用 tab 缩进,必须设置 1 个 tab 为 4 个空格。IDEA 设置 tab 为 4 个空格时,请勿勾选 Use tab character;而在 eclipse 中,必须勾选 insert spaces for tabs
- 正例:(涉及 1-5 点)
public static void main(String[] args) {
// 缩进 4 个空格
String say = "hello";
// 运算符的左右必须有一个空格
int flag = 0;
// 关键词 if 与括号之间必须有一个空格,括号内的 f 与左括号,0 与右括号不需要空格
if (flag == 0) {
System.out.println(say);
}
// 左大括号前加空格且不换行;左大括号后换行
if (flag == 1) {
System.out.println("world");
// 右大括号前换行,右大括号后有 else,不用换行
} else {
System.out.println("ok");
// 在右大括号后直接结束,则必须换行
}
}
- 【强制】单个类文件行数不超过 1000 行,每个类文件尽量职责单一,超过则需要进行功能拆分或者设计模式进行解耦设计
- 【建议】单个方法的总行数不超过 80 行。
- 说明:除注释之外的方法签名、左右大括号、方法内代码、空行、回车及任何不可见字符的总行数不超过 80 行
-
【强制】单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
1)第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2)运算符与下文一起换行。
3)方法调用的点符号与下文一起换行。
4)方法调用中的多个参数需要换行时,在逗号后进行。
5)在括号前不要换行,见反例。
- 正例:
StringBuilder sb = new StringBuilder();
// 超过 120 个字符的情况下,换行缩进 4 个空格,并且方法前的点号一起换行
sb.append("zi").append("xin")...
.append("huang")...
.append("huang")...
.append("huang");
- 反例:
StringBuilder sb = new StringBuilder();
// 超过 120 个字符的情况下,不要在括号前换行
sb.append("you").append("are")...append
("lucky");
// 参数很多的方法调用可能超过 120 个字符,逗号后才是换行处
method(args1, args2, args3, ...
, argsX);
- 【强制】IDE 的 text file encoding 及 project encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不要使用 Windows 格式。
(三)常量定义
- 【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中,全局常量必须在 cloud.apposs.XXXDef 类中定义
- 反例
if (data.isEmpty()) {
return 1; // 新购
}
- 正例
if (data.isEmpty()) {
return ConstType.NEW_BUY;
}
(四)控制语句
- 【强制】在 if/else/for/while/do 语句中必须使用大括号。
- 说明:即使只有一行代码,禁止不采用大括号的编码方式:if (condition) statements;
- 反例
if (condition) System.out.println("true");
- 正例
if (condition) {
System.out.println("true");
}
- 【强制】if-else分支流程超过5层采用设计模式,多采用策略模式、状态模式、工厂方法等来实现
- 说明:if()...else if()...else...方式表达逻辑,在代码后续的业务开发流程如果不加制止,代码功能耦合严重,一个类文件可能达到上万甚至几万行,你绝对不想看到
- 正例(工厂方法,业务逻辑由各Handler接口实现类实现,不会所有功能都在一个类上)
public class final ProductFactory {
// 具体业务逻辑由工厂根据配置类型获取,如果有新运营商接口进来,再实现一个接口并添加到工厂方法即可
public IProductHandler getProductHandler(int type) {
switch(type) {
case ProductType.Type.Wanwang:
return new BookHandler();
default:
return new PhoneHandler();
}
}
}
- 正例(异常分支支持退出,逻辑只处理正常分支)
int usrid = request.getUsrid();
// usrid参数传递有异常,退出
if (usrid == 0) {
Logger.error("usrid args error");
return;
}
// 非正常逻辑数据,退出
boolean isLocal = LocalType.isLocal();
if (!isLocal) {
Logger.error("local args error");
return;
}
// 处理接下来的正常分支
- 正例(基于map的策略模式,动态扩展服务功能,不会动到其他业务)
// 先注册各个Command业务实现接口
Map commands = new Map();
commands.put("helo", new HeloCommand());
commands.put("ehlo", new EhloCommand());
commands.put("auth", new AuthCommand());
commands.put("expn", new ExpnCommand());
public int handleCommand(Request request, Response response) {
String commandString = request.getString("cmd");
// 各个Command有自身的业务实现,需要新的指令再实现Command接口并添加到map即可,都不用动到其他代码块
Command command = commands.get(commandString);
if (command == null) {
Logger.error("unknow command error");
String sendBody = "550 Unknow command.\r\n";
response.write(sendBody);
return;
}
return command.execute(commandString, response);
}
(五)注释规范
- 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用// xxx 方式,一个类文件公共方法都必须加上注释
- 说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释;在 IDE中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
- 【强制】方法中注释采用// xxx,即注释说明前加一个空格
- 反例
// 产品下单
public void orderPay() {
//判断订单类型
...
}
- 正例(涉及 1-2 点)
/**
* 产品下单
*/
public void orderPay() {
// 判断订单类型
...
}
- 【强制】任何核心分支代码都要加注释说明,尤其是某段方法或者代码中如果有一些历史遗留或者政策问题的必须加上,以便于后续代码问题追踪,不会让人懵逼
- 正例
// 20xx.10 - 因为业务需求需要发送特殊邮件
int sendActiveMail = mailCli.send(info)
- 【建议】如果一段代码被注释了并且确认短时间内不会使用,直接删除该注释代码,保证整体代码整洁,即使找回通过svn、git历史对比也可以直接找回
- 【强制】任何对外服务的公用类,必须加上该类的功能说明,方便让人一眼看出该类的功能,类注释只添加功能说明,无需添加作者和日期
- 正例
/**
* 订单服务异步处理,包括各产品下单处理及失败订单重试,系统启动时定期检查
*/
public class OrderSync {
...
}
(六)其他
- 【强制】类的包引用不要存在多余的包引用
- 说明:在 IDE 编辑器中,Eclipse 通过Ctrl+Shift+O来格式化包引用,IDEA 通过Ctrl+Alt+O来格式化包引用,养成和Ctrl+S一样的习惯
- 【强制】一个方法的调用只有当需要的时候再调用,不要在一开始不需要调用的地方执行,节省机器资源
- 反例
if (!handler.init()) {
return false;
}
Map info = handler.getInfo();
// 各种业务逻辑处理
......
// 前面都还在处理各种逻辑,info又只在此处使用,那就等所有业务处理完成了,再获取info信息,需要调用时再执行
int type = info.get(ProductType.Info.Type);
- 正例
if (!handler.init()) {
return false;
}
// 各种业务逻辑处理
......
// handler在此处才调用
Map info = handler.getInfo();
int type = info.get(ProductType.Info.Type);
- 【强制】一个方法的调用如果多次执行,若上下文状态一致,用变量先缓存,避免方法的重复执行
- 反例
Node node1 = ConnectionPool.get(serviceId);
String result = node1.getString(url);
// 没必要,上面获取一次缓存到变量就行
node1 = ConnectionPool.get(serviceId);
result = node1.getString(url);
二、日志规范
- 【强制】禁止在线上服务日志输出Map/List的json数据,一量json数量量一大,屏幕输出便已断点,还极其影响机器性能,占用大量磁盘空间,调试也没任何意义,注意只在必要的地方输出少量必要数据
- 反例
List productList = getProductList();
Logger.info("test handsome boy xxxx %s", productList);
- 正例
Map productInfo = getProductInfo();
Logger.info("req;prodcuctId=%d;", productInfo.get(ProductDef.Info.Id));
三、安全规范
- 【强制】所有涉及用户登录、获取信息等相关操作,传递的关键参数必须是经过加密处理(例如TOKEN、Ticket、SessionId等)防止被抓出规律进行暴力破解,账号验证失败超过一定次数则禁止该IP访问
- 【强制】所有涉及敏感数值的加减乘除(如:价格、库存)必须使用 cloud.apposs.util.DecimalUtil.sum( ... ) 方法实现,不允许直接使用+、-、*、/,避免精度损失造成公司、客户金额损失
- 获取客户信息、投票等敏感高频接口调用必须接入服务防刷,错误一定次数出图形验证码,超过一定次数锁定IP并报警
四、建表规范
- 【强制】公共服务数据,禁止新增和业务相关的表字段,一个原则是公共数据服务只提供基础的数据信息提供,和业务关联的(例如权限、积分、业务开通时间等)均由业务自己定义并和公共数据相连
- 反例
public class StaffTable {
public static final class Info {
public static final String ID = "id";
public static final String MID = "mid";
...
// 禁用,该员工服务本身是服务于多个业务,提供最基础的信息即可
// authAli放在跟支付相关业务上面,让业务自己维护
public static final String AUTH_ALI = "authAli";
}
}
五、组件规范
六、规范考核
- 【强制】除T2-3以上职级组员之外,其他职级员工均需要有review机制,每段代码由对应的导师/Leader进行review