资讯专栏INFORMATION COLUMN

[探索] 怎样让 JS - API 具有更好的实用性

lolomaco / 2852人阅读

摘要:下面就通过一个简单的例子,怎么让更加的实用,更好的复用。代码的实用性,只能尽量,尽量再尽量。关于实用性,命名和扩展性也很重要。而且,这样没复用性。关于这篇文章,也是我目前尝试的一种方式,如果大家有更好的一个实现方式,欢迎在评论区留言。

</>复制代码

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

1.前言

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

</>复制代码

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

  2. 2.关于实用性,API 命名和扩展性也很重要。但之前写过文章,在这里就不重复了。[前端开发]--分享个人习惯的命名方式,重构 - 设计API的扩展机制

2.举个例子

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

</>复制代码

  1. {
  2. cashAmount: 236700,//回款金额(分)
  3. cashDate: "2018-05-26 10:25:28",//回款时间
  4. cashId: "SM2018022800020692",//回款ID
  5. cashStatus: 0,//回款状态
  6. createTime: "2018-05-23 10:26:25",//创建时间
  7. custoName: "广州测试有限公司",//回款公司名称
  8. id: "SM2018022800020692",//回款ID
  9. merchandisers: "守候",//回款公司联系人
  10. ordId: "SO2018022800020692",//订单ID
  11. payChannel: null,//支付方式
  12. remark: "",//备注
  13. userMobile: "18819222363",//回款公司联系人电话
  14. }

需要对数据进行以下处理,再渲染到页面

1.cashAmount 转换成元,并保留两位小数

2.cashStatus 进行解析(0-未回款 1-已回款)

3.payChannel 进行解析 ("zfb"-支付宝,"wx"-微信支付,"cash"-现金支付,"bankTransfer"-银行转账)

4.所有值为 "" , null , undefined 的字段,全部设置为:"--"

面对这样的需要,很简单,顺手就来

</>复制代码

  1. let obj = {
  2. cashAmount: 236700,//回款金额(分)
  3. cashDate: "2018-05-26 10:25:28",//回款时间
  4. cashId: "SM2018022800020692",//回款ID
  5. cashStatus: 0,//回款状态
  6. createTime: "2018-05-23 10:26:25",//创建时间
  7. custoName: "广州测试有限公司",//回款公司名称
  8. id: "SM2018022800020692",//回款ID
  9. merchandisers: "守候",//回款公司联系人
  10. ordId: "SO2018022800020692",//订单ID
  11. payChannel: null,//支付方式
  12. remark: "",//备注
  13. userMobile: "13226452474",//回款公司联系人电话
  14. }
  15. function setValue(obj) {
  16. let _obj=JSON.parse(JSON.stringify(obj));
  17. //设置金额
  18. _obj.cashAmount = (_obj.cashAmount / 100).toFixed(2);
  19. //解析回款状态
  20. _obj.cashStatus = _obj.cashStatus === 0 ");"未回款" : "已回款";
  21. //解析支付方式
  22. let payChannelLabel = {
  23. "zfb": "支付宝",
  24. "wx": "微信支付",
  25. "cash": "现金支付",
  26. "bankTransfer": "银行转账"
  27. }
  28. _obj.payChannel=payChannelLabel[_obj.payChannel];
  29. //设置默认值
  30. for (let key in _obj){
  31. if(_obj[key]===""||_obj[key]===null||_obj[key]===undefined){
  32. _obj[key]="--"
  33. }
  34. }
  35. return _obj;
  36. }
  37. obj=setValue(obj);
  38. console.log(obj)

结果也正确,如下图

但是如果以后需求变了,比如 userMobile 要改成 xxx xxx xxxx 这种展示方式呢?

也很简单,修改下

</>复制代码

  1. function setValue(obj) {
  2. let _obj=JSON.parse(JSON.stringify(obj));
  3. //设置金额
  4. //解析回款状态
  5. //解析支付方式
  6. /*和上面代码一样,不重复粘贴*/
  7. //设置电话号码格式
  8. let _formatType="xxx xxx xxxx",i = 0;
  9. _obj.userMobile= _formatType.replace(/x/g, function(){
  10. return _obj.userMobile[i++]
  11. });
  12. //设置默认值
  13. /*和上面代码一样,不重复粘贴*/
  14. }

代码写好了,想必大家也开始难受了。因为每改一次需求,就要改一次 setValue 。改的多了,出现问题的概率就大了。而且,这样没复用性。试想,如果别的页面有一个需求,同样的数据。但是 cashDate 字段只需要精确到时分秒。这样的需求,大同小异。但上面的代码不适用,需要拷贝一个 setValue 方法(就叫 setValue2 吧),然后添加 cashDate 只显示 时分秒的逻辑。代码很好写

</>复制代码

  1. function setValue2(obj) {
  2. let _obj=JSON.parse(JSON.stringify(obj));
  3. //设置金额
  4. //解析回款状态
  5. //解析支付方式
  6. //设置电话号码格式
  7. /*和上面代码一样,不重复粘贴*/
  8. //设置 cashDate 只显示时分秒
  9. _obj.cashDate= _obj.cashDate.split(" ")[0];
  10. //设置默认值
  11. /*和上面代码一样,不重复粘贴*/
  12. }

3.单一职责原则

想必大家更难受了,因为没发复用,导致出现了几乎完全一样的函数。这个问题解决方式很多,先说下第一个,也是一个 API 设计原则--单一职责原则。

顾名思义,单一职责原则就是让每一个函数只做一件事。下面把代码改造下

</>复制代码

  1. /**
  2. * @description 设置默认值
  3. * @param obj 待处理对象
  4. * @return obj 已处理对象
  5. */
  6. function setDefault(obj) {
  7. let _obj=JSON.parse(JSON.stringify(obj));
  8. for (let key in _obj){
  9. if(_obj[key]===""||_obj[key]===null||_obj[key]===undefined){
  10. _obj[key]="--"
  11. }
  12. }
  13. return _obj;
  14. }
  15. /**
  16. * @description 格式化电话号码
  17. * @param obj 待处理对象
  18. * @return obj 已处理对象
  19. */
  20. function setFormatMobile(obj) {
  21. let _obj=JSON.parse(JSON.stringify(obj));
  22. let _formatType="xxx xxx xxxx",i = 0;
  23. _obj.userMobile= _formatType.replace(/x/g, function(){
  24. return _obj.userMobile[i++]
  25. });
  26. return _obj;
  27. }
  28. /**
  29. * @description 解析支付方式
  30. * @param obj 待处理对象
  31. * @return obj 已处理对象
  32. */
  33. function setPayChannelLabel(obj) {
  34. let _obj=JSON.parse(JSON.stringify(obj));
  35. let payChannelLabel = {
  36. "zfb": "支付宝",
  37. "wx": "微信支付",
  38. "cash": "现金支付",
  39. "bankTransfer": "银行转账"
  40. }
  41. _obj.payChannel = payChannelLabel[_obj.payChannel];
  42. return _obj;
  43. }
  44. /**
  45. * @description 设置回款金额
  46. * @param obj 待处理对象
  47. * @return obj 已处理对象
  48. */
  49. function setCashAmount(obj) {
  50. let _obj=JSON.parse(JSON.stringify(obj));
  51. _obj.cashAmount = (_obj.cashAmount / 100).toFixed(2);
  52. return _obj;
  53. }
  54. /**
  55. * @description 解析回款状态
  56. * @param obj 待处理对象
  57. * @return obj 已处理对象
  58. */
  59. function setCashStatus(obj) {
  60. let _obj=JSON.parse(JSON.stringify(obj));
  61. _obj.cashStatus = _obj.cashStatus === 0 ");"未回款" : "已回款";
  62. return _obj;
  63. }
  64. obj=setFormatMobile(obj);
  65. obj=setCashStatus(obj);
  66. obj=setCashAmount(obj);
  67. obj=setPayChannelLabel(obj);
  68. obj=setDefault(obj);

结果一样,如果需要加上 cashDate 只显示 时分秒。加上逻辑就行了

</>复制代码

  1. /**
  2. * @description 设置汇款时间
  3. * @param obj 待处理对象
  4. * @return obj 已处理对象
  5. */
  6. function setCashDate(obj) {
  7. let _obj=JSON.parse(JSON.stringify(obj));
  8. _obj.cashDate = _obj.cashDate.split(" ")[0];
  9. return _obj;
  10. }
  11. obj=setFormatMobile(obj);
  12. obj=setCashStatus(obj);
  13. obj=setCashAmount(obj);
  14. obj=setCashDate(obj);
  15. obj=setPayChannelLabel(obj);
  16. obj=setDefault(obj);
  17. console.log(obj)

让 API 保持单一原则的好处是,复用性比复杂的 API 更好,而且编写的难度更低。

4.最少知识原则

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

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

</>复制代码

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

</>复制代码

  1. let handle={
  2. setDefault(obj) {
  3. //省略的代码
  4. },
  5. setFormatMobile(obj) {
  6. //省略的代码
  7. },
  8. setPayChannelLabel(obj) {
  9. //省略的代码
  10. },
  11. setCashAmount(obj) {
  12. //省略的代码
  13. },
  14. setCashStatus(obj) {
  15. //省略的代码
  16. }
  17. }
  18. obj=handle.setFormatMobile(obj);
  19. obj=handle.setCashStatus(obj);
  20. obj=handle.setCashAmount(obj);
  21. obj=handle.setPayChannelLabel(obj);
  22. obj=handle.setDefault(obj);
  23. console.log(obj)

第二个让人难受的地方就是一个步骤,经过了几次的赋值,这个难免有点难受,写起来也麻烦,记忆成本高。解决起来也很简单,就是另写一个函数,把那些操作步骤封装在一起就行了。封装的目的就是为了让使用的人,只需要记住一个函数的使用方式就可以了,不需要记住多个函数的使用方式。

</>复制代码

  1. let handle={
  2. /*省略代码*/
  3. setCash(obj){
  4. let _obj=JSON.parse(JSON.stringify(obj));
  5. _obj=this.setFormatMobile(_obj);
  6. _obj=this.setCashStatus(_obj);
  7. _obj=this.setCashAmount(_obj);
  8. _obj=this.setPayChannelLabel(_obj);
  9. _obj=this.setDefault(_obj);
  10. return _obj;
  11. }
  12. }
  13. obj=handle.setCash(obj);
  14. console.log(obj)
5.配置数据和业务逻辑分离

上面的代码,看着算是比较舒服了,但是问题还是有,就是 setCash 函数写得太死了。固定了五个方法 :setFormatMobile,setCashStatus,setCashAmount,setPayChannelLabel,setDefault 。如果以后不需要处理电话号码,又要改 setCash ,把 _obj=this.setFormatMobile(_obj); 这行代码去掉。虽然改动也很小,但是问题就出来了。如果其中一个地方需要执行 setFormatMobile ,就不能删除。如果另一个地方, 不需要执行 setFormatMobile ,就要删除。这样子就顾此失彼了。

解决的方案想必大家也知道了,就是需要执行什么函数,就在函数上动态传入。

</>复制代码

  1. let handle={
  2. /*省略代码*/
  3. setCash(obj,fns="setFormatMobile,setCashStatus,setCashAmount,setPayChannelLabel,setDefault"){
  4. let _obj=JSON.parse(JSON.stringify(obj));
  5. let _fns=fns.split(",");
  6. _fns.forEach(item => {
  7. _obj=this[item](_obj);
  8. });
  9. return _obj;
  10. }
  11. }
  12. obj=handle.setCash(obj);
  13. console.log(obj)
  14. //比如另一个地方不需要执行 setFormatMobile
  15. obj = {
  16. cashAmount: 236700,//回款金额(分)
  17. cashDate: "2018-05-26 10:25:28",//回款时间
  18. cashId: "SM2018022800020692",//回款ID
  19. cashStatus: 0,//回款状态
  20. createTime: "2018-05-23 10:26:25",//创建时间
  21. custoName: "广州测试有限公司",//回款公司名称
  22. id: "SM2018022800020692",//回款ID
  23. merchandisers: "守候",//回款公司联系人
  24. ordId: "SO2018022800020692",//订单ID
  25. payChannel: null,//支付方式
  26. remark: "",//备注
  27. userMobile: "13226452474",//回款公司联系人电话
  28. }
  29. obj=handle.setCash(obj,"setCashStatus,setCashAmount,setPayChannelLabel,setDefault");
  30. console.log("比如另一个地方不需要执行 setFormatMobile",obj)

6.批量处理

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

</>复制代码

  1. let arr=[
  2. {
  3. cashAmount: 236700,//回款金额(分)
  4. cashDate: "2018-05-26 10:25:28",//回款时间
  5. cashId: "SM2018022800020692",//回款ID
  6. cashStatus: 0,//回款状态
  7. createTime: "2018-05-23 10:26:25",//创建时间
  8. custoName: "广州测试有限公司",//回款公司名称
  9. id: "SM2018022800020692",//回款ID
  10. merchandisers: "守候",//回款公司联系人
  11. ordId: "SO2018022800020692",//订单ID
  12. payChannel: null,//支付方式
  13. remark: "",//备注
  14. userMobile: "13226452474",//回款公司联系人电话
  15. },
  16. {/*省略的代码*/},
  17. {/*省略的代码*/},
  18. {/*省略的代码*/},
  19. //省略的代码
  20. ]

写起来的时候呢,要这样写

</>复制代码

  1. arr.forEach((item,index)=>{
  2. arr[index]=handle.setCash(item);
  3. })
  4. console.log(arr)

虽然代码不多,但是有更好的方案,就用更好的方案。比如使用批量处理的方式。就多写一个函数就行了。

</>复制代码

  1. let handle={
  2. /*省略代码*/
  3. batch(arr,fns,...orther){
  4. let _arr=JSON.parse(JSON.stringify(arr));
  5. let _fns=fns.split(",");
  6. _arr.forEach((item,index)=>{
  7. _fns.forEach(fn => {
  8. _arr[index]=this[fn](_arr[index],...orther);
  9. });
  10. })
  11. return _arr
  12. }
  13. }

调用的时候就比之前简单了一点,结果也正确

</>复制代码

  1. arr=handle.batch(arr,"setCash")
  2. console.log(arr)

要传其他参数也可以

</>复制代码

  1. arr=handle.batch(arr,"setCash","setCashStatus,setCashAmount,setPayChannelLabel,setDefault")
  2. console.log(arr)

如果要传入多个操作函数

</>复制代码

  1. arr=handle.batch(arr,"setCashStatus,setCashAmount")
  2. console.log(arr)

7.小结

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

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7227.html

相关文章

  • [探索]怎样 JS - API 具有更好用性

    摘要:下面就通过一个简单的例子,怎么让更加的实用,更好的复用。代码的实用性,只能尽量,尽量再尽量。关于实用性,命名和扩展性也很重要。而且,这样没复用性。关于这篇文章,也是我目前尝试的一种方式,如果大家有更好的一个实现方式,欢迎在评论区留言。 程序员的精神,不应不止于实现,更要注重优化。不应止于表面,更要研究内部机制,方能青出于蓝而胜于蓝。 1.前言 在上家公司开发后台管理系统的时候,频繁要处...

    LMou 评论0 收藏0
  • 基于DevOps、微服务以及k8s高可用架构探索与实现

    摘要:前言本文给大家分享的题目是基于微服务以及的高可用架构探索与实现。比如说年大地震的时候我正好在东京,当时在做一个金融系统的相关工作。那次大地震导致很多很多的问题,虽然大地震不是在东京发生,但是还是给我们的系统造成了影响。 前言 本文给大家分享的题目是《基于DevOps、微服务以及K8S的高可用架构探索与实现》。整个企业的高可用架构面临很多的挑战,面向微服务、容器化以及敏态交付,是我们现在...

    cnio 评论0 收藏0
  • 3月份前端资源分享

    摘要:面试如何防骗一份优秀的前端开发工程师简历是怎么样的作为,有哪些一般人我都告诉他,但是他都不听的忠告如何面试前端工程师 更多资源请Star:https://github.com/maidishike... 文章转自:https://github.com/jsfront/mo... 3月份前端资源分享 1. Javascript 使用judge.js做信息判断 javascript...

    nanchen2251 评论0 收藏0
  • 有赞业务对账平台探索与实践

    摘要:业务对账平台的核心目的,就是及时发现类似问题,并及时修复。这对对账平台的吞吐量造成了挑战。五健康度对账中心可以拿到业务系统及其所在整个链路的数据一致性信息。在分布式环境下,没有人能回避数据一致性问题,我们对此充满着敬畏。 一、引子 根据CAP原理,分布式系统无法在保证了可用性(Availability)和分区容忍性(Partition)之后,继续保证一致性(Consistency)。我...

    wangjuntytl 评论0 收藏0

发表评论

0条评论

lolomaco

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<