博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[探索] 怎样让 JS - API 具有更好的实用性
阅读量:6207 次
发布时间:2019-06-21

本文共 8722 字,大约阅读时间需要 29 分钟。

程序员的精神,不应不止于实现,更要注重优化。不应止于表面,更要研究内部机制,方能青出于蓝而胜于蓝。

1.前言

在上家公司开发后台管理系统的时候,频繁要处理各种数据显示的问题,一开始是实现就好。后来写多了,自己看得也难受了。就想着怎么优化代码和复用了。下面就通过一个简单的例子,怎么让 API 更加的实用,更好的复用。

1.代码的实用性,只能尽量,尽量再尽量。不会出现完美的API,或者是一次编写,永不修改的 API 。

2.关于实用性,API 命名和扩展性也很重要。但之前写过文章,在这里就不重复了。,

2.举个例子

比如有一个需求,有这样的数据

{    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-已回款)

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];    //设置默认值    /*和上面代码一样,不重复粘贴*/}复制代码

3.单一职责原则

想必大家更难受了,因为没发复用,导致出现了几乎完全一样的函数。这个问题解决方式很多,先说下第一个,也是一个 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 更好,而且编写的难度更低。

4.最少知识原则

上面的写法虽然实现了复用,看着比之前好了一点,但是看着也是难受,毕竟赋值了几次,而且还有那么多的全局函数。

首先,全局函数这个容易解决,用一个对象包裹起来,全局函数少了,也方便管理。

重复的代码和注释,这里忽略,不重复粘贴

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)复制代码

5.配置数据和业务逻辑分离

上面的代码,看着算是比较舒服了,但是问题还是有,就是 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)//比如另一个地方不需要执行 setFormatMobileobj = {    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)复制代码

6.批量处理

看到这里,好像差不多了。但是写下去,大家才会知道,一般的后台管理系统的用户列表,数据一般不会只有一条。一般而言是一个数组对象。如下

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)复制代码

7.小结

关于开发上,API 的实用性,暂时就先提这几个方面,如果以后发现有其他例子,还能从其他方面提高 API 的实用性,就再发文章分享。关于这篇文章,也是我目前尝试的一种方式,如果大家有更好的一个实现方式,欢迎在评论区留言。

-------------------------华丽的分割线--------------------

想了解更多,和我交流,内推职位,请添加我微信。或者关注我的微信公众号:守候书阁

转载于:https://juejin.im/post/5b390fb96fb9a00e9e59e2e2

你可能感兴趣的文章
Hadoop命令手册
查看>>
Salesforce Einstein承诺提供“开箱即用”的人工智能应用程序
查看>>
企业如何应对云计算的法律风险
查看>>
《程序化广告实战》一 第1章 程序化广告的前世今生
查看>>
摩尔定律堪称不死神话: IBM开发5纳米芯片
查看>>
友友云产品发布会在京举行
查看>>
Gartner:如何定位CDO首席数据官
查看>>
蓝代斯克助力企业决胜现代化IT
查看>>
5款应用优化Outlook体验感
查看>>
H3C许立宪:金融跨界成必然趋势
查看>>
Arkeia发布业界首个用于备份的虚拟设备
查看>>
机器学习的入门“秘籍”
查看>>
Struts2漏洞爆发 知道创宇云安全已拦截近万攻击样本
查看>>
关于5G的10个问答 让我们告别盲人摸象
查看>>
中兴通讯徐明:中国智慧城市应以人为核心让信息惠民
查看>>
用互联网铺开传统行业转型之路
查看>>
还坐在办公室?软件定义工作空间改变办公模式
查看>>
黑客很伤心,美国NSA泄露的黑客工具“无人问津”
查看>>
git笔记
查看>>
深度学习那么火,它究竟能做些什么?
查看>>