01小节:牛券项目代码规范
作者:程序员马丁
热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。
牛券项目代码规范,元数据信息:
- 什么是牛券oneCoupon:https://t.zsxq.com/pAWgS
- 代码仓库:https://gitcode.net/nageoffer/onecoupon —— 申请项目权限参考上述牛券项目链接
- 章节难度:★☆☆☆☆ - 简单
- 视频地址:无
©版权所有 - 拿个offer-开源&项目实战星球专属学习项目,依据《中华人民共和国著作权法实施条例》和《知识星球产权保护》,严禁未经本项目原作者明确书面授权擅自分享至 GitHub、Gitee 等任何开放平台。违者将面临法律追究。
内容摘要:讲解在牛券项目编写过程中使用的代码规范,以帮助大家更好地理解代码并编写高质量代码。
由于本节内容主要涉及代码规范,没有特别复杂或隐晦的部分,因此没有录制视频。
课程目录如下所示:
- 方法命名
- 参数命名
- 领域模型命名规约
- 代码开发规约
- 注释规范
- 消息队列规约
方法命名
1. 获取单个对象的方法用 get 作前缀
例如:查询单个用户 getStudent,按照 ID 查询单个用户 getStudentById。
2. 获取多个对象的方法用 list 作前缀
例如:按照 IDS 查询多个用户,listStudentByIds。
3. 获取统计值的方法用 count 作前缀
例如:统计全量用户,countUser。
4. 插入的方法用 save 作前缀
例如:新增用户,saveUser。
5. 删除的方法用 remove 作前缀
例如:删除用户,removeUser。
6. 修改的方法用 update 作前缀
例如:修改用户,updateUser。
参数命名
1. 对象参数命名
许多同学在方法参数命名上较为随意,与其如此,不如制定统一的命名规范,以避免增加命名难度。针对 Controller、Service 和 Mapper 三层架构中的单个对象请求参数,建议统一使用 requestParam 进行命名。
示例如下:
/**
* 查询优惠券模板
*
* @param requestParam 请求参数
* @return 优惠券模板信息
*/
CouponTemplateQueryRespDTO findCouponTemplate(CouponTemplateQueryReqDTO requestParam);
2. 单参数命名
对于基础类型和引用类型,由于它们的语义较为单一,因此可以直接使用它们本身的语义单词进行命名。
/**
* 查询优惠券推送任务详情
*
* @param taskId 推送任务 ID
* @return 优惠券推送任务详情
*/
CouponTaskQueryRespDTO findCouponTaskById(String taskId);
领域模型命名规约
1. 数据对象
xxxDO,xxx 即为数据表名。比如学生数据对象:StudentDO。
2. 数据传输对象
xxxDTO,xxx 为业务领域相关的名称。
这里又分为两种,分别是请求入参和请求出参,以学生新增接口为例:
- 入参:StudentSaveReqDTO
- 出参:StudentSaveRespDTO
如果是分页查询学生接口,示例如下:
- 入参:StudentPageQueryReqDTO
- 出参:StudentPageQueryRespDTO
3. 项目配置类
xxxConfiguration,xxx 为配置类型。比如数据库持久层配置类:DataBaseConfiguration。
4. 常量类
xxxConstant,xxx 为常量领域。比如项目中公共 Redis 配置:RedisCommonConstant。
5. 上下文
xxxContext,xxx 为上下文类型。比如用户存储上下文:UserContext。
6. 枚举类
xxxEnum,xxx 表示什么类型的枚举。比如用户优惠券状态枚举:UserCouponStatusEnum。
7. 注意事项
POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
代码开发规约
【强制】类、方法和变量的命名要做到顾名思义,避免使用缩写。
【强制】静态变量使用大写,多个单词使用下划线连接。示例:MESSAGE_CENTER_SEND_TYPR。
【强制】捕获的异常名称命名为 ex ;捕获异常且不做任何事情,异常名称命名为 ignored。
【强制】返回值变量使用 result 命名;循环中使用 each 命名循环变量;map 中使用 entry 代替 each。
result 命名示范:
private void parseDate(String data) {
Result result = JSONUtil.parseObject(data, Result.class);
return result;
}
或采用 result 为前缀:
private void parseDate(String data) {
Result resultDate = JSONUtil.parseObject(data, Result.class);
return resultDate;
}
each 命名示范:
appNameLeaseMap.values().forEach(each -> appNameLeaseList.add(each));
或是 for 循环:
for (Lease<InstanceInfo> each : appNameLeaseMap.values()) {
appNameLeaseList.add(each);
}
【强制】业务系统中优先使用 Guava、HuTool、Common3 等工具类中的方法,不存在指定方法时再创建自定义工具类,禁止创建相同语义方法的工具类。
备注:定义组件项目时,尽量使用自定义工具类,避免因版本问题导致不确定的异常。
注释规范
1. 注释说明意图即可,无需补充冗余字段
【强制】Class、Interface、Enum、@interface 等文件类型,类上注释仅需说明类的意图即可。不需要补充时间和创建人,因为 往往开发代码的不止是一个人,容易造成信息干扰。需要的话,查看提交记录即可。
/**
* 适配第三方框架的线程池
*/
public interface ThreadPoolAdapter {
}
2. 方法上需要添加注释
【强制】方法上需添加注释,并说明清楚方法的意图(接口实现类无需注释);必要时描述 @param @return。
/**
* 适配第三方框架的线程池
*/
public interface ThreadPoolAdapter {
/**
* 修改框架线程池的核心参数
*
* @param threadPoolBaseInfo 修改线程池的基础参数
* @return 线程池核心参数修改结果
*/
boolean updateThreadPool(ThreadPoolBaseInfo threadPoolBaseInfo);
}
如果方法为内部引用方法,并且方法名称见名知意,无需方法注释。
3. 方法块内部注释规范
【强制】方法内部的注释,应该新起一行,而不是跟在代码后面。
正例:
// 刷新动态线程池参数
refreshDynamicPool(parameter, executor);
反例:
refreshDynamicPool(parameter, executor); // 刷新动态线程池参数
4. 方法命名说明方法本身意图
【强制】私有方法尽量通过方法命名说明方法语义。
消息队列规约
1. 命名规范
RocketMQ 相关命名强制使用英文小写。
1)【强制】Topic 命名:业务线_项目名_topic。业务线或项目包含多个单词,使用 - 分割,例如:common_message-center_topic
。
2)【强制】Tag 命名:业务线项目业务_tag,例如:common_message-center_send-message_tag
。
3)【强制】生产者组命名:业务线项目**业务_pg,例如:common_message-center_send-message_pg
。
4)【强制】消费者组命名:业务线项目**业务_cg,例如:common_message-center_send-message_cg
。
2. 申请规范
创建 Topic 需要走线上申请,申请时补充下述信息。
申请人:马丁
Topic:common_message-center_topic
Produce:
生产应用 | 生产者组 | Tag | 备注 | |
---|---|---|---|---|
1 | message-center | common_message-center_send-message_pg | common_message-center_send-message_tag | 流量削峰 |
2 | message-center | common_message-center_send-message_pg | insurance_trading-order_send-message-fail_tag | 微信模板消息发送失败通知保险项目 |
Consume:
消费应用 | 消费者组 | Tag | 消费模型 | 备注 | |
---|---|---|---|---|---|
1 | message-center | common_message-center_send-message_cg | common_message-center_send-message_tag | 集群模式 | 发送微信模板、短信、小程序等消息 |
2 | trading-order | insurance_trading-order_send-message-fail_cg | insurance_trading-order_send-message-fail_tag | 集群模式 | 微信模板消息发送失败通知保险项目 |
3. 使用规范
3.1 消息发送
1)【强制】消息生产者创建时,必须指定生产者组。
2)【强制】一个系统对应一个 Topic,系统下的不同业务根据 Tag 区分,参考申请规范-消费应用 Tag。
3)【强制】发送消息时,需设置 KEYS。KEYS 建议定义为业务唯一标识,比如订单 ID。
4)【强制】发送消息不管发送成功或失败,需打印 KEYS、Payload、执行时间以及 SendResult
5)【强制】发送消息时,需设置超时时间,避免应用被拖垮;建议超时时间设置为 2000ms 内。
6)【建议】针对可靠性较高的消息,发送失败后可以存储到 DB,开启定时任务扫描,并重新投递。
3.2 消息消费
1)【强制】消费端创建时,必须指定消费者组。
2)【强制】消费端需要保证数据幂等。
3)【强制】消费消息不管成功或失败,需打印 KEYS、MsgId、执行时间以及 Message。
4)【强制】不同的应用集群应使用不同的消费者组,如果不同的应用集群需要订阅同一消费者组,需保证 Topic Tag 订阅关系一致。
5)【强制】打印消息消费日志。
log.info("Execute result: {}, Keys: {}, Dispatch time: {} ms, Execute time: {} ms, Message: {}", ...);
6)【建议】消费时尽量不设置重试,大部分情况下,执行失败的消息重试后会再次失败,反而会影响消费进度。开发者应该针对特定场景在代码中设置重试逻辑。
7)【建议】消费者并发消费数量默认为 1,即串行化,应该基于不同系统场景来设置并发数,同时要考虑消费过程中其它组件的压力。
- 系统 CPU 任务少:
*CPU 核数 / (1 - 阻塞系数 0.8)*
。 - 系统 CPU 任务较多,建议
CPU 核数 + 1
即可。