前言
随着最近在群里看代码(摸🐟)次数越来越多,越来越感觉很多人都在写 一次性代码
。
什么是一次性代码?
一次性代码从广义上讲,是只使用一次的代码。如:数据库迁移脚本、临时活动页面、
kpi工程。但我指的一次性代码更倾向于写出来就 很难维护 的代码。
举个栗子:
我们在工作时会发现,大佬们写一次性代码也尤其优雅,而新手在写真正需要维护的代码时,也写的像依托答辩。
这是每个新码农的必经之路,我在带新人时,常常会想,这条路为什么在不同人之间,具现的长度不同?大家会发现,大厂的员工往往比小厂的员工成长的更快。
最后经我总结,导致 「路」 长短的原因有以下几点:
- 接触到的代码质量不同
- 代码审核不严格
- 没有重构思维
虽然我写过一篇 《重构「屎山」?你可能永远也不该做这件事!》,但你如果仔细看下来就会发现,我想表达的并不是不要重构(虽然评论区很多人曲解了我的意思),而是如何重构。
正文
问题
今天在群里有朋友发自己的工作状态,一眼就看到了这个经典(奇葩)的代码。
问题定位
我这里拿了比较经典的例子,这个代码的问题一眼就能看出来,是 三元嵌套
问题。
要谈 三元嵌套
、 if else嵌套
的问题,那么一定避不开的一张图就是经典的元气弹代码。
这样的代码最大的问题在于,无法准确的看出具体的执行逻辑是什么。往往阅读者需要抽丝剥茧的捋清代码逻辑,在定位具体的调整点。
解决思路
由于代码是别人的图片,我们这里先转换成伪代码,可以不必纠结变量名问题(我们此次不涉及变量、函数命名)。
matlab
function getSxClass() {
return function (sxData) {
var type =
sxData.specialCase & sxData.specialcase.length > 0 ? 'onShaixuan'
: sxData.screening & sxData.screening.length > 0 ? 'onShaixuan'
: sxData.publicSecurity & sxData.publicSecurity.length > 0 ? 'onShaixuan'
: sxData.peoplelive & sxData.peoplelive.length > 0 ? 'onShaixuan'
: sxData.professionalrdenlevel & sxData.professionalrdenlevel.length > 0 ? 'onShaixuan'
: sxData.remediation & sxData.remediation.length > 0 ? 'onShaixuan'
: sxData.dhcity & sxData.dhcity != '' ? 'onShaixuan'
: sxData.propertyattribute & sxData.propertyattribute != '' ? 'onShaixuan'
: sxData.housinguse & sxData.housinguse != '' ? 'onShaixuan'
: sxData.houserusefor & sxData.houserusefor != '' ? 'onShaixuan'
: sxData.structureType & sxData.structureType != '' ? 'onShaixuan'
: '';
return type
}
}
三元转换if
我们都知道,在简单判断的情况下,用 三元运算符
是好的选择。
但是在涉及到多层判断时,嵌套的三元表达式,会导致代码可读性降低。
我们首先把 三元表达式
转换 if else
,再进行重构会轻松的多。
因此,我们第一步将 三元表达式
转换为 if else
。
matlab
function getSxClass() {
return function (sxData) {
var type = ""
if (sxData.specialCase & sxData.specialcase.length > 0) {
type = 'onShaixuan'
} else {
if (sxData.screening & sxData.screening.length > 0) {
type = 'onShaixuan'
} else {
if (sxData.publicSecurity & sxData.publicSecurity.length > 0) {
type = 'onShaixuan'
} else {
if (sxData.peoplelive & sxData.peoplelive.length > 0) {
type = 'onShaixuan'
} else {
if (sxData.professionalrdenlevel & sxData.professionalrdenlevel.length > 0) {
type = 'onShaixuan'
} else {
if (sxData.remediation & sxData.remediation.length > 0) {
type = 'onShaixuan'
} else {
if (sxData.dhcity & sxData.dhcity != '') {
type = 'onShaixuan'
} else {
if (sxData.propertyattribute & sxData.propertyattribute != '') {
type = 'onShaixuan'
} else {
if (sxData.housinguse & sxData.housinguse != '') {
type = 'onShaixuan'
} else {
if (sxData.houserusefor & sxData.houserusefor != '') {
type = 'onShaixuan'
} else {
if (sxData.structureType & sxData.structureType != '') {
type = 'onShaixuan'
}
}
}
}
}
}
}
}
}
}
}
return type
}
}
卫语句优化if
优化完我们会发现,这不就是元气弹问题吗!!我们的问题看着是 三元嵌套
问题,但转换为 if else
时,就会看出,明显是元气弹问题。
那下面我们就这元气弹问题谈谈元气弹的解决方案。元气弹代码有经典的解决方案就是 卫语句 ,那么我们接下来通过卫语句的方式,解决元气弹代码。
kotlin
function getSxClass() {
return function (sxData) {
if (sxData.specialCase & sxData.specialcase.length > 0) {
return 'onShaixuan'
}
if (sxData.screening & sxData.screening.length > 0) {
return 'onShaixuan'
}
if (sxData.publicSecurity & sxData.publicSecurity.length > 0) {
return 'onShaixuan'
}
if (sxData.peoplelive & sxData.peoplelive.length > 0) {
return 'onShaixuan'
}
if (sxData.professionalrdenlevel & sxData.professionalrdenlevel.length > 0) {
return 'onShaixuan'
}
if (sxData.remediation & sxData.remediation.length > 0) {
return 'onShaixuan'
}
if (sxData.dhcity & sxData.dhcity != '') {
return 'onShaixuan'
}
if (sxData.propertyattribute & sxData.propertyattribute != '') {
return 'onShaixuan'
}
if (sxData.housinguse & sxData.housinguse != '') {
return 'onShaixuan'
}
if (sxData.houserusefor & sxData.houserusefor != '') {
return 'onShaixuan'
}
if (sxData.structureType & sxData.structureType != '') {
return 'onShaixuan'
}
return ''
}
}
这样我们的代码逻辑就清晰多了,只有清晰的代码逻辑,才能方便我们开发维护。
同时,我们也能更好的重构代码。
if 合并
那么基于我们的卫语句代码,我们可以看出,很多的条件判断,最后都指向同一件事情。
那么我们可以将条件判断合并,这样减少无用的代码。
matlab
function getSxClass() {
return function (sxData) {
if (
sxData.specialCase && (sxData.specialcase.length > 0) ||
sxData.screening && (sxData.screening.length > 0) ||
sxData.publicSecurity && (sxData.publicSecurity.length > 0) ||
sxData.peoplelive && (sxData.peoplelive.length > 0) ||
sxData.professionalrdenlevel && (sxData.professionalrdenlevel.length > 0) ||
sxData.remediation && (sxData.remediation.length > 0) ||
sxData.dhcity && (sxData.dhcity != "") ||
sxData.propertyattribute && (sxData.propertyattribute != "") ||
sxData.housinguse && (sxData.housinguse != "") ||
sxData.houserusefor && (sxData.houserusefor != "") ||
sxData.structureType && (sxData.structureType != "")
) {
return "onShaixuan";
}
return ''
}
}
实际上,通常工作中,重构到这一步就基本满足需求了。
但我们的重构教程不一样,我们要做就一定尽可能的做到最好。
那么,请接着往下看。
reduce 重构
我们通过上面的重构代码可以看出,函数实际上就做了一件事儿:当指定元素为空时,返回 onShaixuan
,否则返回 ''
。
同时,我们有两种需要判断是否为空的方式,一种是 空字符串
,一种是 空数组
。
那么我们通过 reduce
函数,默认返回 ''
,在遇到为空的数组、字符串时,改变返回为 onShaixuan
。
javascript
function A(obj) {
const needVerifyArr = ['specialCase', 'screening', 'publicSecurity', 'peoplelive', 'professionalrdenlevel', 'remediation']
const needVerifyStr = ['dhcity', 'propertyattribute', 'housinguse', 'houserusefor', 'structureType']
const arrVerifyRes = needVerifyArr.reduce((r, v) => obj[v] && obj[v].length > 0 ? 'onShaixuan' : r, '')
return needVerifyStr.reduce((r, v) => obj[v] && obj[v] !== "" ? 'onShaixuan' : r, arrVerifyRes)
}
奥,代码优雅起来了,那我们有没有办法更优雅一点呢?
提取空数组、字符串判断函数
那么再看下去,显而易见的,判断空时的代码 xx && xx.length > 0
,不能一眼看出要干什么。
那么,抽!
javascript
function A(obj) {
function isEmptyArr(arr) {
return arr && arr.length < 1
}
function isEmptyStr(str) {
return str && str === ""
}
const needVerifyArr = ['specialCase', 'screening', 'publicSecurity', 'peoplelive', 'professionalrdenlevel', 'remediation']
const needVerifyStr = ['dhcity', 'propertyattribute', 'housinguse', 'houserusefor', 'structureType']
const arrVerifyRes = needVerifyArr.reduce((r, v) => isEmptyArr(obj[v]) ? 'onShaixuan' : r, '')
return needVerifyStr.reduce((r, v) => isEmptyStr(obj[v]) ? 'onShaixuan' : r, arrVerifyRes)
}
哎呦,一目了然,并且我们后期写的空字符串、数组判断有问题时,也更好维护。
那我们到这步就结束了吗?
两个全空判断 + 一个三元返回
javascript
function getSxClass(obj) {
const needVerifyArr = ['specialCase', 'screening', 'publicSecurity', 'peoplelive', 'professionalrdenlevel', 'remediation']
const needVerifyStr = ['dhcity', 'propertyattribute', 'housinguse', 'houserusefor', 'structureType']
const isAllEmptyArr = needVerifyArr.reduce((r, v) => isEmptyArr(obj[v]) && r, true)
const isAllEmptyStr = needVerifyStr.reduce((r, v) => isEmptyStr(obj[v]) && r, true)
return isAllEmptyArr && isAllEmptyStr ? '' : 'onShaixuan'
}
最后
结语
通过上面一个问题,我们可以学习到多个知识点,包括但不限于 ES6 reduce、卫语句、非空方法验证。
我们应当习惯性的进行代码重构,当好的开发方式形成我们的开发习惯后,凭借本能,我们也能写出优雅的代码。
附录
卫语句
卫语句既是一种编程模式,也是我们优化 if else
的常见手段。一般用来处理特殊的异常情况,以便于主函数逻辑可以保持简洁、清晰。最常用卫语句的是后台处理异常。
例如,假设我们有一个函数,它需要一个非空的字符串作为参数。我们可以使用卫语句来检查这个条件:
javascript
function processString(str) {
// 卫语句
if (!str || typeof str !== 'string') {
throw new Error('Invalid argument: str must be a non-empty string');
}
// 主要的函数逻辑
// ...
}
该方案通常后端用的比较多,但我们前端做验证时,常常也是一样的逻辑。因此,我们学习、使用对我们的开发也是好处多多~
reduce
JS 中 reduce 方法是 ES6 新增的一个高阶函数,但很多人容易忽略这个方法,因为它往往没那么直观的感受到什么时候我们该使用它。
这里只简单讲讲它的用法:
我们需要计算一个子数组中所有数字的和:
js
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => (accumulator + currentValue), 0);
后续我会总结一篇 ES6 中的新数组方法的意义、使用方法、实际案例,欢迎大家关注后续内容~~