最近,感觉身边见到的代码质量真是一言难尽。有些大厂回沈的同事啥的,但是代码写的我也是无语了,并且还不接受意见,感觉没法交流。列举下我最不能接受的几段代码,真实发生在身边的代码。
并不简单的格式问题
大厂同事提交的代码,就提交了一行@DataPermission
这个注解:
@DataPermission(userAlias = "p", deptIdColumn = "create_dept",deptAlias = "p",userIdColumn = "create_user")
List<BizVo> findList(BizQueryDto queryDto);
是不影响功能,但是这个注解用的真是…
- 属性后面的逗号没有统一的空格,并不要求非得有空格,但是自己的风格是不得一致啊,要不都不加,要不都加。
- user相关的属性和dept相关的属性交替出现。是不是应该把user属性相关的放在一起,dept相关的属性放在一起才更易于理解和维护啊,非要拆开可以把alias放在一起,column相关放在一起啊,这个交替放是什么逻辑…
重点是这个注解的使用已经给出了如下的参考代码,但是大厂同事并没有理会示例代码的精髓:
@DataPermission(
userAlias = "p", userIdColumn = "create_user",
deptAlias = "p", deptIdColumn = "create_dept"
)
List<BizVo> findList(BizQueryDto queryDto);
但是大厂同事强调这个无所谓,并且他们之前都是借助格式化工具来自动完成代码格式化的。
自动化没有问题,但是是不应该保证提交到代码仓库上的代码已经格式化过啊,但是代码库上的代码就是我最上面列出的那样。
我的观点是,合格的程序员应该保证自己代码风格、格式的一致性,说简单了就手里的活干的立正些。格式并不仅仅只是简单的问题,是能侧面反应出一个程序员思维的严谨性、责任心的,并且对后续的维护也是有帮助的。
if和Optional的嵌套使用
大厂同事提交的代码,if
和Optional
嵌套在一起:
if (StringUtils.isNotBlank(eventType)) {
setEventType(Optional.ofNullable(AuditEventTypeEnum.codeOf(eventType))
.map(AuditEventTypeEnum::getMessage)
.orElse(eventType));
}
如上代码既有if
又有Optional
,但其实Optional
就是做if null
判断的,并且嵌套在一起真的有些层级过多(臃肿)、可读性差。
我给出了如下的改进建议,仅使用Optional
便可替换掉原来的if
:
Optional.ofNullable(eventType)
.map(AuditEventTypeEnum::codeOf)
.map(AuditEventTypeEnum::getMessage)
.ifPresent(this::setEventType);
大厂同事看了一眼就又是一顿掰扯,然后说我给的代码之后是不还得再调用一次setEventType这个方法,我都无语了,压根就没看懂就跟我一顿掰扯。然后看懂代码了,又说AI说Optional
可读性没有if
好,我就又无语了,原来的代码是if
和Optional
都用了,并且嵌套在一起用了,都把Optional
用成这样了,还跟我说Optional
可读性不好,他要是不用Optional
只用if
,我都不会给他提这个优化建议了,既然都用了Optional
了,是不就该把它用明白了,别半吊子啊…接不接收的无所谓,但是别人给提的建议是不需要好好理解一下啊。
魔法变量
继续,大厂同事的新代码:
if (StringUtils.isNotBlank(eventSuccess)) {
setEventSuccess(AUDIT_EVENT_SUCCESS.equals(eventSuccess) ? "成功" : "失败");
}
这个"成功"
、"失败"
是什么鬼,就不能定义个枚举、常量啥的的吗。
并且,工程里是有相关的常量定义的,就不能找一下吗…
代码变量名和数据库列名非得不一致
比如数据库里按照parentCode查询,代码里的参数名就叫parentId,
数据库里照层级level查询,代码里的参数名就叫deep,
这个是要故意给后续的维护人员增加理解上的的困难吗。
然后大厂同事跟我掰扯DDD中的统一语言,代码可以和数据库中的列名不一致,但是也不用这么刻意地不一致吧…
接口参数的隐藏规则
大厂同事给出的REST接口定义如下:
GET /tree?parentId=111
正常人应该都会理解为查询parentId下的子节点。
但是还有隐藏规则,参数parentId
的值为0
时会查询前N层的节点。
这是给维护人员挖坑吗,这种隐藏的规则鬼知道啊,时间长了谁会记得啊。
我给的建议是拆分成2个参数:level
, parentId
,即按层查询传输level
参数,查询子节点则传输parentId
参数。
SVG的字体的掩耳盗铃
大厂同事实现了后端生成文本SVG的功能,即生成SVG格式(XML格式)的图片,图片的内容是一段文字。
大厂同事集成了一堆组件,一顿生成SVG图片,并集成了自定义的字体来计算文字的宽度及SVG图片的宽度。
但是我实际测试SVG图片的宽度不对,文字显示不全。我提出了这个问题,大厂同事又是一顿掰扯,说我没更新代码、他自己测试是好用的…
SVG本质上就是一段XML定义,大多数前端开发者应该都能手写下面这段代码:
<svg xmlns='http://www.w3.org/2000/svg' width='400' height='80'>
<text x='0' y='60' font-size='25' fill='rgba(200,200,200,0.25)'>QuickStart自定义登录页面</text>
</svg>
我的大厂同事为了计算SVG的宽度,硬给text
塞了一个font-family
属性,属性值是后端引入的一个字体:
<svg xmlns='http://www.w3.org/2000/svg' width='400' height='80'>
<text x='0' y='60' font-size='25' font-family="我是你不认识的字体" fill='rgba(200,200,200,0.25)'>QuickStart自定义登录页面</text>
</svg>
关键这个字体只有后端服务认识,真正集成到用户的浏览器中,用户的浏览器环境根本不认识这个字体,根据这个字体计算的宽度对真实的用户并不起作用啊,掩耳盗铃吗…
我给的建议:
- 移除多余的组件,手写SVG XML为一个配置属性,仅是替换SVG XML中的文本和宽度值。没必要引入多余的依赖。
- 移除字体,根据font-size计算宽度值(并未实测,理论上font-size可以计算多文字的叠加宽度,哪怕加些buffer值)。
最终,移除了后端生成SVG这个功能,由前端根据文字生成SVG…
但是,后端有问题的SVG生成代码还依旧保留在代码库中,使用者可以看到并使用相应的接口。这个是我最不推荐的,没用的代码就该被删除,不要保留在代码库中,想要找回代码可通过git提交记录找回。废弃的代码、有问题的代码还保留在代码库中,会给后续的维护带来负担、引入不必要的麻烦,况且有问题的代码还要给用户使用吗,给用户挖坑吗?
但现实是,它就这么一直在代码库中…
结论
还有好多例子,下次有机会再分享吧。
不爱掰扯了,改不改无所谓了,爱咋写咋写吧…
准备下班了,别耽误我回家带娃了。