程序员的精神,不该不止于实现,更要注重优化。不该止于表面,更要研究内部机制,方能青出于蓝而胜于蓝。javascript
在上家公司开发后台管理系统的时候,频繁要处理各类数据显示的问题,一开始是实现就好。后来写多了,本身看得也难受了。就想着怎么优化代码和复用了。下面就经过一个简单的例子,怎么让 API 更加的实用,更好的复用。前端
1.代码的实用性,只能尽可能,尽可能再尽可能。不会出现完美的API,或者是一次编写,永不修改的 API 。java
2.关于实用性,API 命名和扩展性也很重要。但以前写过文章,在这里就不重复了。[前端开发]--分享我的习惯的命名方式,重构 - 设计API的扩展机制程序员
好比有一个需求,有这样的数据数组
{
cashAmount: 236700,//回款金额(分)
cashDate: "2018-05-26 10:25:28",//回款时间
cashId: "SM2018022800020692",//回款ID
cashStatus: 0,//回款状态
createTime: "2018-05-23 10:26:25",//建立时间
custoName: "广州测试有限公司",//回款公司名称
id: "SM2018022800020692",//回款ID
merchandisers: "守候",//回款公司联系人
ordId: "SO2018022800020692",//订单ID
payChannel: null,//支付方式
remark: "",//备注
userMobile: "18819222363",//回款公司联系人电话
}
复制代码
须要对数据进行如下处理,再渲染到页面微信
1.cashAmount 转换成元,并保留两位小数函数
2.cashStatus 进行解析(0-未回款 1-已回款)post
3.payChannel 进行解析 ('zfb'-支付宝,'wx'-微信支付,'cash'-现金支付,'bankTransfer'-银行转帐)测试
4.全部值为 '' , null , undefined 的字段,所有设置为:'--'微信支付
面对这样的须要,很简单,顺手就来
let obj = {
cashAmount: 236700,//回款金额(分)
cashDate: "2018-05-26 10:25:28",//回款时间
cashId: "SM2018022800020692",//回款ID
cashStatus: 0,//回款状态
createTime: "2018-05-23 10:26:25",//建立时间
custoName: "广州测试有限公司",//回款公司名称
id: "SM2018022800020692",//回款ID
merchandisers: "守候",//回款公司联系人
ordId: "SO2018022800020692",//订单ID
payChannel: null,//支付方式
remark: "",//备注
userMobile: "13226452474",//回款公司联系人电话
}
function setValue(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
//设置金额
_obj.cashAmount = (_obj.cashAmount / 100).toFixed(2);
//解析回款状态
_obj.cashStatus = _obj.cashStatus === 0 ? '未回款' : '已回款';
//解析支付方式
let payChannelLabel = {
'zfb': '支付宝',
'wx': '微信支付',
'cash': '现金支付',
'bankTransfer': '银行转帐'
}
_obj.payChannel=payChannelLabel[_obj.payChannel];
//设置默认值
for (let key in _obj){
if(_obj[key]===''||_obj[key]===null||_obj[key]===undefined){
_obj[key]='--'
}
}
return _obj;
}
obj=setValue(obj);
console.log(obj)
复制代码
结果也正确,以下图
可是若是之后需求变了,好比 userMobile 要改为 xxx xxx xxxx 这种展现方式呢?
也很简单,修改下
function setValue(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
//设置金额
//解析回款状态
//解析支付方式
/*和上面代码同样,不重复粘贴*/
//设置电话号码格式
let _formatType="xxx xxx xxxx",i = 0;
_obj.userMobile= _formatType.replace(/x/g, function(){
return _obj.userMobile[i++]
});
//设置默认值
/*和上面代码同样,不重复粘贴*/
}
复制代码
代码写好了,想必你们也开始难受了。由于每改一次需求,就要改一次 setValue 。改的多了,出现问题的几率就大了。并且,这样没复用性。试想,若是别的页面有一个需求,一样的数据。可是 cashDate 字段只须要精确到时分秒。这样的需求,大同小异。但上面的代码不适用,须要拷贝一个 setValue 方法(就叫 setValue2 吧),而后添加 cashDate 只显示 时分秒的逻辑。代码很好写
function setValue2(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
//设置金额
//解析回款状态
//解析支付方式
//设置电话号码格式
/*和上面代码同样,不重复粘贴*/
//设置 cashDate 只显示时分秒
_obj.cashDate= _obj.cashDate.split(' ')[0];
//设置默认值
/*和上面代码同样,不重复粘贴*/
}
复制代码
想必你们更难受了,由于没发复用,致使出现了几乎彻底同样的函数。这个问题解决方式不少,先说下第一个,也是一个 API 设计原则--单一职责原则。
顾名思义,单一职责原则就是让每个函数只作一件事。下面把代码改造下
/** * @description 设置默认值 * @param obj 待处理对象 * @return obj 已处理对象 */
function setDefault(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
for (let key in _obj){
if(_obj[key]===''||_obj[key]===null||_obj[key]===undefined){
_obj[key]='--'
}
}
return _obj;
}
/** * @description 格式化电话号码 * @param obj 待处理对象 * @return obj 已处理对象 */
function setFormatMobile(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
let _formatType="xxx xxx xxxx",i = 0;
_obj.userMobile= _formatType.replace(/x/g, function(){
return _obj.userMobile[i++]
});
return _obj;
}
/** * @description 解析支付方式 * @param obj 待处理对象 * @return obj 已处理对象 */
function setPayChannelLabel(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
let payChannelLabel = {
'zfb': '支付宝',
'wx': '微信支付',
'cash': '现金支付',
'bankTransfer': '银行转帐'
}
_obj.payChannel = payChannelLabel[_obj.payChannel];
return _obj;
}
/** * @description 设置回款金额 * @param obj 待处理对象 * @return obj 已处理对象 */
function setCashAmount(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
_obj.cashAmount = (_obj.cashAmount / 100).toFixed(2);
return _obj;
}
/** * @description 解析回款状态 * @param obj 待处理对象 * @return obj 已处理对象 */
function setCashStatus(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
_obj.cashStatus = _obj.cashStatus === 0 ? '未回款' : '已回款';
return _obj;
}
obj=setFormatMobile(obj);
obj=setCashStatus(obj);
obj=setCashAmount(obj);
obj=setPayChannelLabel(obj);
obj=setDefault(obj);
复制代码
结果同样,若是须要加上 cashDate 只显示 时分秒。加上逻辑就好了
/** * @description 设置汇款时间 * @param obj 待处理对象 * @return obj 已处理对象 */
function setCashDate(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
_obj.cashDate = _obj.cashDate.split(' ')[0];
return _obj;
}
obj=setFormatMobile(obj);
obj=setCashStatus(obj);
obj=setCashAmount(obj);
obj=setCashDate(obj);
obj=setPayChannelLabel(obj);
obj=setDefault(obj);
console.log(obj)
复制代码
让 API 保持单一原则的好处是,复用性比复杂的 API 更好,并且编写的难度更低。
上面的写法虽然实现了复用,看着比以前好了一点,可是看着也是难受,毕竟赋值了几回,并且还有那么多的全局函数。
首先,全局函数这个容易解决,用一个对象包裹起来,全局函数少了,也方便管理。
重复的代码和注释,这里忽略,不重复粘贴
let handle={
setDefault(obj) {
//省略的代码
},
setFormatMobile(obj) {
//省略的代码
},
setPayChannelLabel(obj) {
//省略的代码
},
setCashAmount(obj) {
//省略的代码
},
setCashStatus(obj) {
//省略的代码
}
}
obj=handle.setFormatMobile(obj);
obj=handle.setCashStatus(obj);
obj=handle.setCashAmount(obj);
obj=handle.setPayChannelLabel(obj);
obj=handle.setDefault(obj);
console.log(obj)
复制代码
第二个让人难受的地方就是一个步骤,通过了几回的赋值,这个不免有点难受,写起来也麻烦,记忆成本高。解决起来也很简单,就是另写一个函数,把那些操做步骤封装在一块儿就好了。封装的目的就是为了让使用的人,只须要记住一个函数的使用方式就能够了,不须要记住多个函数的使用方式。
let handle={
/*省略代码*/
setCash(obj){
let _obj=JSON.parse(JSON.stringify(obj));
_obj=this.setFormatMobile(_obj);
_obj=this.setCashStatus(_obj);
_obj=this.setCashAmount(_obj);
_obj=this.setPayChannelLabel(_obj);
_obj=this.setDefault(_obj);
return _obj;
}
}
obj=handle.setCash(obj);
console.log(obj)
复制代码
上面的代码,看着算是比较舒服了,可是问题仍是有,就是 setCash 函数写得太死了。固定了五个方法 :setFormatMobile,setCashStatus,setCashAmount,setPayChannelLabel,setDefault 。若是之后不须要处理电话号码,又要改 setCash ,把 _obj=this.setFormatMobile(_obj);
这行代码去掉。虽然改动也很小,可是问题就出来了。若是其中一个地方须要执行 setFormatMobile
,就不能删除。若是另外一个地方, 不须要执行 setFormatMobile
,就要删除。这样子就顾此失彼了。
解决的方案想必你们也知道了,就是须要执行什么函数,就在函数上动态传入。
let handle={
/*省略代码*/
setCash(obj,fns='setFormatMobile,setCashStatus,setCashAmount,setPayChannelLabel,setDefault'){
let _obj=JSON.parse(JSON.stringify(obj));
let _fns=fns.split(',');
_fns.forEach(item => {
_obj=this[item](_obj);
});
return _obj;
}
}
obj=handle.setCash(obj);
console.log(obj)
//好比另外一个地方不须要执行 setFormatMobile
obj = {
cashAmount: 236700,//回款金额(分)
cashDate: "2018-05-26 10:25:28",//回款时间
cashId: "SM2018022800020692",//回款ID
cashStatus: 0,//回款状态
createTime: "2018-05-23 10:26:25",//建立时间
custoName: "广州测试有限公司",//回款公司名称
id: "SM2018022800020692",//回款ID
merchandisers: "守候",//回款公司联系人
ordId: "SO2018022800020692",//订单ID
payChannel: null,//支付方式
remark: "",//备注
userMobile: "13226452474",//回款公司联系人电话
}
obj=handle.setCash(obj,'setCashStatus,setCashAmount,setPayChannelLabel,setDefault');
console.log('好比另外一个地方不须要执行 setFormatMobile',obj)
复制代码
看到这里,好像差很少了。可是写下去,你们才会知道,通常的后台管理系统的用户列表,数据通常不会只有一条。通常而言是一个数组对象。以下
let arr=[
{
cashAmount: 236700,//回款金额(分)
cashDate: "2018-05-26 10:25:28",//回款时间
cashId: "SM2018022800020692",//回款ID
cashStatus: 0,//回款状态
createTime: "2018-05-23 10:26:25",//建立时间
custoName: "广州测试有限公司",//回款公司名称
id: "SM2018022800020692",//回款ID
merchandisers: "守候",//回款公司联系人
ordId: "SO2018022800020692",//订单ID
payChannel: null,//支付方式
remark: "",//备注
userMobile: "13226452474",//回款公司联系人电话
},
{/*省略的代码*/},
{/*省略的代码*/},
{/*省略的代码*/},
//省略的代码
]
复制代码
写起来的时候呢,要这样写
arr.forEach((item,index)=>{
arr[index]=handle.setCash(item);
})
console.log(arr)
复制代码
虽然代码很少,可是有更好的方案,就用更好的方案。好比使用批量处理的方式。就多写一个函数就好了。
let handle={
/*省略代码*/
batch(arr,fns,...orther){
let _arr=JSON.parse(JSON.stringify(arr));
let _fns=fns.split(',');
_arr.forEach((item,index)=>{
_fns.forEach(fn => {
_arr[index]=this[fn](_arr[index],...orther);
});
})
return _arr
}
}
复制代码
调用的时候就比以前简单了一点,结果也正确
arr=handle.batch(arr,'setCash')
console.log(arr)
复制代码
要传其余参数也能够
arr=handle.batch(arr,'setCash','setCashStatus,setCashAmount,setPayChannelLabel,setDefault')
console.log(arr)
复制代码
若是要传入多个操做函数
arr=handle.batch(arr,'setCashStatus,setCashAmount')
console.log(arr)
复制代码
关于开发上,API 的实用性,暂时就先提这几个方面,若是之后发现有其余例子,还能从其余方面提升 API 的实用性,就再发文章分享。关于这篇文章,也是我目前尝试的一种方式,若是你们有更好的一个实现方式,欢迎在评论区留言。
-------------------------华丽的分割线--------------------
想了解更多,和我交流,内推职位,请添加我微信。或者关注个人微信公众号:守候书阁