概述
好的代码就是让人读起来很爽的代码。逻辑清晰,扩展方便,对外封装复杂性。
好的代码,即使写的时候可能不爽,感觉很麻烦(通病):
- 但是写只要写一次,读却要被读很多次。
- 写的人只有你一个,而且是最了解这个方法的功能,但是看的人会有很多。垃圾代码是只有你自己知道,别人都看不懂。
- 写的时候只是痛苦一会儿,而且是理解最深的时候,但是看的可能是几个月,甚至一两年后还有人要看这个代码。垃圾代码,写完过了一个月,自己都不认识了。
所以:一人受苦,一时受苦,造福自家兄弟们。
好的代码
什么是读起来很爽的代码?
- 有注释且清晰
- 最好的注解是没有注解,取一个准确表达方法功能的名字,这才是最优的注解
- 为了保证代码的简洁优雅,建议注释统一写在方法上面,不要把代码和注释混杂在一起,除非是特别复杂,或者重点要注意的注释,写在代码里面
/**
* 先把老的数据全部逻辑删除,再插入新的信息
* 更新完 origin的数据之后,再根据 Organization_workday的数据,更新 Organization_hierarchy数据
*
*/
@Override
public Boolean updateOrganizationToWD(String term){
String category=DataTypeWorkDayEnum.ORGANIZATION.getType();
int total=hrService.getOrganizationTotalCount();
if (total == 0){
return false;
}
hrLogService.createSyncLog(term, category, total, pullAction);
organizationRepository.deleteAllOlderOrganizationWDInfoInLogic();
boolean result=updateLatestOrganization(total, term);
hrLogService.updateResultLog(category, term, pullAction, result);
return result;
}
- 数据库表的创建注释、字段的注释(包括枚举值)。
这个是最重要的啊(可以通过 show create table test_sync_log;来查看表结构和注释)
CREATE TABLE `test_sync_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`term` varchar(255) NOT NULL COMMENT '任务批次号',
`category` varchar(255) DEFAULT '' COMMENT '业务类型: worker、position、profile、organizaition、organizaition_hierarchy',
`start_time` datetime DEFAULT NULL COMMENT '任务开始时间'
...
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='这张表记录 HR数据从...'
- 废弃不用的表,要么删掉,要么用_bk做后缀啊,误导性太强了。比如position_role_relation_bk
-
方法名字一定要能准确表达出该方法的作用,这个误导性也很强,哪怕是花点时间百度翻译一下,也不能为了省事随便取一个名字。或者方法的功能变了,方法名字还是以前的那个。
比如一个方法 validateParams(User user),那这个方法只能校验参数,不能做任何的写的操作,比如set 比如 remove,要不然太坑爹了。 -
主方法的结构一定要简洁清晰,特别是核心的方法。
不要一个方法堆个几百行甚至上千行,自己看也觉得恶心啊。而且拆分方法的另外一个好处是方便复用。
比如一个新增的方法:适合抽象成3个子方法
- validateUser()
- saveUser()
- 封装返回值
这样写的好处是:
- 如果我关心程序是如何校验用户的,只需要看第一个方法。
- 如果我只关心他是怎么存储到数据库的,只需要看第三个方法。
- 如果我想了解它的返回码是什么意思,那么我只要看第三个方法。
- 写一个方便复用的方法
比如现在有一个根据 身份证号码查询用户姓名的功能,然后mybatis的xml里面多了一个select查询。
或者update的方法,也可以参考这种写法
垃圾代码:
select name from user where id_card=#{idCard};
问题:
- 如果还要返回家庭住址,则要修改这个sql,所以不如写成select * ,因为和数据库交互查询少量数据,查询一个字段和查询多个字段,没有什么差别的
- 如果要新增一个功能,根据员工工号查询用户信息,或者根据员工邮箱查询用户信息,那么又多了一个写法。
- 如果扩展之后,一个条件可能包含多条数据,所以用一个list去接返回值这样代码的可扩展性就很强了。整个Mapper文件就很清爽啦
<select id="find" resultMap="BaseResultMap" parameterType="OrganizationPO">
select * from organization
where valid = 1 and available =1
<if test="name != null" >
AND name = #{name,jdbcType=VARCHAR}
</if>
<if test="corporationId != null" >
AND corporation_id = #{corporationId,jdbcType=BIGINT}
</if>
<if test="parentId != null" >
AND parent_id = #{parentId,jdbcType=BIGINT}
</if>
</select>
- 对外开放接口(http接口或者 dubbo接口)一定要对结果值做一层封装
经常出现的一个业务场景是,调用别人的接口,然后页面报错,返回一坨报错的信息。
- 第一不安全,可能会导致别人的主流程失败,同时会暴露服务提供方的内部代码信息
- 第二不友好,人家看到一串报错信息,也不能准确的知道到底是出了什么问题
比如查询用户信息的一个功能,核心的信息是 姓名、邮箱、家庭住址、余额、订单等信息,附属的一个信息是淘宝积分的信息,通过Dubbo接口调用积分系统 获取淘宝积分信息。
如果查询结构未做封装,直接报错,而调用方没有做异常处理,结果整个用户查询的功能就崩溃了。或者即使调用方 try catch了,保住了这个功能可用,但是看到日志中一串报错信息也没有头绪
好的做法是:谁提供服务,谁负责封装调用结果和异常信息的解释
public class ClientBaseResponse<T> implements Serializable {
private String code;
private String msg;
private T data;
}
调用方通过
- 先check ResponseCode来确定调用是否成功,如果失败则不展示 积分信息,主流程不会崩溃,核心功能仍然可用
- 如果服务提供方返回码异常,也可以读取 responseMsg,来比较直观的感知到哪里出了问题
另外如果是前端核心地方调取http接口获取数据失败,直接报错导致页面停止渲染的,就非常可怕了,所以好的代码,一定要做好封装
- 降低和数据库交互的次数
比如要新增一个用户,但是必须保证 身份证号码、code、邮箱唯一性,并且返回错误的详细信息。
辣鸡代码就是
- 先根据身份证号码查一下,看身份证号码是否重复
- 再根据code查一下,看code是否重复
- 再根据邮箱查一下,看邮箱是否重复
好的代码能结合业务写,辣鸡代码的实现中,不管你新增什么用户,都要先去查3次数据库,才完成检验。但是一般业务场景是用户的信息都是唯一的,极少数情况会重复,所以奔放一点,数据库上面3个字段分别做唯一性索引,然后try catch直接插入,报错的话,再去拼接3个条件做联合or查询。查询返回一个list,遍历这个list,看邮箱、code、身份证号,到底是哪些参数重复了。而不是身份证号码重复了,用户纠正了身份证号码,插入的时候,你又报错code重复了,然后修改完在之后,你又说邮箱冲突,很不友好的一种交互方式。
-
restful API风格
真的很棒的一种接口规范。 -
枚举是个好东西
枚举和常量相比:
-
虽然写起来麻烦一点(好代码的通病),但是读起来很清爽
-
枚举可以限制某一类型数据的类型,避免错误的枚举值,比如type只能是String,这个常量是做不到的
-
枚举可以很方便的知道该枚举值有哪些类型,这是常量做不到的,可能不同的枚举值写在不同的代码段上面
-
枚举值可以附加更多的额外信息,方便使用者了解枚举值
public enum SignTypeEunm {
PAPER("PAPER","纸质签名"),
ELECTRONIC("ELECTRONIC","电子签名");
private String type;
private String cName;
public String getType() {
return type;
}
public String getcName() {
return cName;
}
SignWayEunm(String type, String cName) {
this.type = type;
this.cName = cName;
}
}
网友评论