怎样让 JS - API 具有更好的实用性

更新日期: 2019-05-06 阅读: 2.3k 标签: api
程序员的精神,不应不止于实现,更要注重优化。不应止于表面,更要研究内部机制,方能青出于蓝而胜于蓝。


1.前言

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

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

2.关于实用性,API 命名和扩展性也很重要。但之前写过文章,在这里就不重复了。[[前端开发]--分享个人习惯的命名方式](https://juejin.im/post/5b6ad6...重构 - 设计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)

//比如另一个地方不需要执行 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)


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://segmentfault.com/a/1190000019079820


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/3206

相关推荐

适合写api接口文档的管理工具有哪些?

现在越来越流行前后端分离开发,使用ajax交互。所以api接口文档就变的十分有意义了,目前市场有哪些比较优秀的接口文档管理工具呢?比如:MinDoc,eoLinker,apizza,RAML,Swagger等等

前后端分离,如何防止api接口被恶意调用或攻击

无论网站,还是App目前基本都是基于api接口模式的开发,那么api的安全就尤为重要了。目前攻击最常见的就是“短信轰炸机”,由于短信接口验证是App,网站检验用户手机号最真实的途径,使用短信验证码在提供便利的同时,也成了呗恶意攻击的对象,那么如何才能防止被恶意调用呢?

超赞的腾讯短网址(微信url.cn短链接)生成api接口

腾讯短网址的应用场景很广,譬如短信营销、邮件推广、微信营销、QQ营销、自媒体推广、渠道推广等,都会用到短网址。究其原因是在于短网址可以降低推广成本、用户记忆成本,提高用户点击率;在特定的场景下推广还能规避关键词,防止域名被拦截

JSON API免费接口_ 免费的天气预报、地图、IP、手机信息查询、翻译、新闻等api接口

整理提供最新的各种免费JSON接口,其中有部分需要用JSONP调用。方面前端同学的学习或在网站中的使用,包括:免费的天气预报、地图、IP、手机信息查询、翻译、新闻等api接口

ACE Editor在线代码编辑器的API使用文档

ACE 是一个开源的、独立的、基于浏览器的代码编辑器,可以嵌入到任何web页面或JavaScript应用程序中。ACE支持超过60种语言语法高亮,并能够处理代码多达400万行的大型文档

浏览器中的图像识别 API

在 Native 开发中,Android 和 IOS 平台都在系统层面直接提供给了应用开发识别图像的一些能力,比如对于二维码/条形码的识别,Android 可以使用 barcode API 、 iOS 可以使用 CIQRCodeFeature API 。

Vue 3.0 体验 Vue Function API

近 Vue 官方公布了 Vue 3.0 最重要的RFC:Function-based component API,并发布了兼容 Vue 2.0 版本的 plugin:vue-function-api,可用于提前体验 Vue 3.0 版本的 Function-based component API

vue-next 函数式 api

在分享 vue-next 各个子模块的实现之前,我觉的有必要比较全面的整理下 vue-next 中提出的函数式 api,了解这些的话,无论是对于源码的阅读,还是当正式版发布时开始学习,应该都会有起到一定的辅助作用

在原生 React Native 应用中使用 Expo API

你可以在任何 React Native 应用程序中使用尽可能少或尽可能多的 Expo SDK。 我们已经花了很多时间构建和维护这些包含原生应用特性的跨平台 API,我们很高兴最终实现了向整个 React Native 生态共享这些 API

构建API的最佳编程语言是什么?

你是否正在设计第一个Web应用程序?也许你过去已经建立了一些,但是目前也正在寻找语言的变化以提高你的技能,或尝试新的东西。有了所有信息,就很难决定为下一个产品或项目选择哪种编程语言。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!