The spirit of the programmer should not only be about implementation, but also about optimization. Should not stop at the surface, but also to study the internal mechanism, in order to outshine blue.

1. Introduction

In the last company to develop background management system, frequently to deal with various data display problems, the beginning is to achieve good. Later, I wrote a lot, and I felt uncomfortable. Just figuring out how to optimize code and reuse it. Here is a simple example of how to make an API more practical and reusable.

1. The usefulness of the code can only be achieved as much as possible. There won’t be perfect apis, or apis written once and never changed.

2. In terms of usability, API naming and extensibility are also important. But I’ve written before, so I won’t repeat it here. [Front-end development]– Share personal preferences for naming, refactoring – Design extension mechanisms for apis

2. Here’s an example

Let’s say there’s a requirement, there’s data like this

{
    cashAmount: 236700.// Amount of payment received (cent)
    cashDate: "The 2018-05-26 10:25:28".// Time of payment collection
    cashId: "SM2018022800020692"./ / collection ID
    cashStatus: 0.// Payment collection status
    createTime: "The 2018-05-23 10:26:25".// Create time
    custoName: Guangzhou Testing Co., LTD.// The name of the payee company
    id: "SM2018022800020692"./ / collection ID
    merchandisers: "Waiting".// Contact person of payment collection company
    ordId: "SO2018022800020692"./ / order ID
    payChannel: null.// Payment method
    remark: ""./ / note
    userMobile: "18819222363".// Phone number of the contact person of the payment collection company
}

Copy the code

The data needs to be rendered to the page with the following processing

1. CashAmount converts to yuan and keeps two decimal places

2. Analysis of cashStatus (0- unpaid 1- paid back)

3. PayChannel analysis (‘ ZFB ‘- alipay, ‘wx’- wechat pay, ‘cash’- cash payment, ‘bankTransfer’- bankTransfer)

4. Set all null, undefined values to: ‘–‘

In the face of such a need, it is easy to come

let obj = {
    cashAmount: 236700.// Amount of payment received (cent)
    cashDate: "The 2018-05-26 10:25:28".// Time of payment collection
    cashId: "SM2018022800020692"./ / collection ID
    cashStatus: 0.// Payment collection status
    createTime: "The 2018-05-23 10:26:25".// Create time
    custoName: Guangzhou Testing Co., LTD.// The name of the payee company
    id: "SM2018022800020692"./ / collection ID
    merchandisers: "Waiting".// Contact person of payment collection company
    ordId: "SO2018022800020692"./ / order ID
    payChannel: null.// Payment method
    remark: ""./ / note
    userMobile: "13226452474".// Phone number of the contact person of the payment collection company
}
function setValue(obj) {
    let _obj=JSON.parse(JSON.stringify(obj));
    // Set the amount
    _obj.cashAmount = (_obj.cashAmount / 100).toFixed(2);
    // Parse the payback status
    _obj.cashStatus = _obj.cashStatus === 0 ? 'Unpaid' : 'Paid back';
    // Parse the payment method
    let payChannelLabel = {
        'zfb': Alipay.'wx': 'wechat Pay'.'cash': 'Cash payment'.'bankTransfer': 'Bank transfer'
    }
    _obj.payChannel=payChannelLabel[_obj.payChannel];
    // Set the default value
    for (let key in _obj){
        if(_obj[key]===' '||_obj[key]===null||_obj[key]===undefined){
            _obj[key]=The '-'}}return _obj;
}
obj=setValue(obj);
console.log(obj)
Copy the code

The result is also correct, as shown below

But what if the requirements change later, such as userMobile to XXX, XXX, XXXX display mode?

It’s also very simple. Let me modify it

function setValue(obj) {
    let _obj=JSON.parse(JSON.stringify(obj));
    // Set the amount
    // Parse the payback status
    // Parse the payment method
    /* As above, do not duplicate paste */
    // Set the phone number format
    let _formatType="xxx xxx xxxx",i = 0;
    _obj.userMobile= _formatType.replace(/x/g.function(){
        return _obj.userMobile[i++]
    });
    // Set the default value
    /* As above, do not duplicate paste */
}
Copy the code

The code has been written, we must also begin to feel uncomfortable. Because every time you change the requirement, you change the setValue. Change a lot, the probability of problems is greater. Plus, there’s no reusability. Imagine if another page had a request for the same data. But the cashDate field only needs to be precise. Such requirements, much the same. The above code does not apply, so you need to copy a setValue method (call it setValue2) and add the logic for cashDate to display only minutes and seconds. The code is easy to write

function setValue2(obj) {
    let _obj=JSON.parse(JSON.stringify(obj));
    // Set the amount
    // Parse the payback status
    // Parse the payment method
    // Set the phone number format
    /* As above, do not duplicate paste */
    // Set cashDate to display only minutes and seconds
    _obj.cashDate= _obj.cashDate.split(' ') [0];
    // Set the default value
    /* As above, do not duplicate paste */
}
Copy the code

3. Principle of single responsibility

I think it’s even worse for you, because you don’t reuse it, so you get almost exactly the same function. There are many ways to solve this problem, but the first one is also an API design principle — the single responsibility principle.

As the name implies, the single responsibility principle is to make each function do only one thing. So let’s change the code

/ * * *@description Set the default value *@param Obj Object to be processed *@return Obj Processed object */
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]=The '-'}}return _obj;
}
/ * * *@description Format the phone number *@param Obj Object to be processed *@return Obj Processed object */
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 Parse the payment method *@param Obj Object to be processed *@return Obj Processed object */
function setPayChannelLabel(obj) {
    let _obj=JSON.parse(JSON.stringify(obj));
    let payChannelLabel = {
        'zfb': Alipay.'wx': 'wechat Pay'.'cash': 'Cash payment'.'bankTransfer': 'Bank transfer'
    }
    _obj.payChannel = payChannelLabel[_obj.payChannel];
    return _obj;
}
/ * * *@description Set the amount of collection *@param Obj Object to be processed *@return Obj Processed object */
function setCashAmount(obj) {
    let _obj=JSON.parse(JSON.stringify(obj));
    _obj.cashAmount = (_obj.cashAmount / 100).toFixed(2);
    return _obj;
}
/ * * *@description Parse the collection status *@param Obj Object to be processed *@return Obj Processed object */
function setCashStatus(obj) {
    let _obj=JSON.parse(JSON.stringify(obj));
    _obj.cashStatus = _obj.cashStatus === 0 ? 'Unpaid' : 'Paid back';
    return _obj;
}

obj=setFormatMobile(obj);
obj=setCashStatus(obj);
obj=setCashAmount(obj);
obj=setPayChannelLabel(obj);
obj=setDefault(obj);
Copy the code

The result is the same if you need to add cashDate to display only the minutes and seconds. Just add logic

/ * * *@description Set remittance time *@param Obj Object to be processed *@return Obj Processed object */
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)
Copy the code

The advantage of keeping apis single is that they are more reusable than complex apis and are less difficult to write.

The least knowledge rule

It looks a little better than before, but it’s also uncomfortable to look at, given the number of assignments, and the number of global functions.

First, global functions are easy to solve, wrapped in an object, with fewer global functions, and easy to manage.

Duplicate code and comments, ignored here, do not duplicate and paste

let handle={
   setDefault(obj) {
        // The omitted code
    },
    setFormatMobile(obj) {
        // The omitted code
    },
    setPayChannelLabel(obj) {
        // The omitted code
    },
    setCashAmount(obj) {
        // The omitted code
    },
    setCashStatus(obj) {
        // The omitted code
    }
}


obj=handle.setFormatMobile(obj);
obj=handle.setCashStatus(obj);
obj=handle.setCashAmount(obj);
obj=handle.setPayChannelLabel(obj);
obj=handle.setDefault(obj);
console.log(obj)
Copy the code

The second uncomfortable part is one step, after several assignments, this is inevitably a little uncomfortable, difficult to write, high memory cost. The solution is as simple as writing another function that wraps those steps together. The purpose of encapsulation is to allow the user to remember only how to use one function, rather than how to use multiple functions.

let handle={
   /* omit code */
   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)
Copy the code

5. Separate data from service logic

The above code looks more comfortable, but the problem is that the setCash function is too dead. Fixed the five methods: setFormatMobile setCashStatus, setCashAmount, setPayChannelLabel, setDefault. _obj=this.setFormatMobile(_obj); _obj=this.setFormatMobile(_obj); Let’s get rid of this line of code. It was a minor change, but there was a problem. If one of these places requires setFormatMobile, it cannot be removed. If there’s another place where you don’t need to do setFormatMobile, delete it. In this way, you lose the other.

The solution, as you probably know, is to dynamically pass in whatever function you need to execute.

let handle={
   /* omit code */
   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)

// For example, there is another place where setFormatMobile is not required
obj = {
    cashAmount: 236700.// Amount of payment received (cent)
    cashDate: "The 2018-05-26 10:25:28".// Time of payment collection
    cashId: "SM2018022800020692"./ / collection ID
    cashStatus: 0.// Payment collection status
    createTime: "The 2018-05-23 10:26:25".// Create time
    custoName: Guangzhou Testing Co., LTD.// The name of the payee company
    id: "SM2018022800020692"./ / collection ID
    merchandisers: "Waiting".// Contact person of payment collection company
    ordId: "SO2018022800020692"./ / order ID
    payChannel: null.// Payment method
    remark: ""./ / note
    userMobile: "13226452474".// Phone number of the contact person of the payment collection company
}
obj=handle.setCash(obj,'setCashStatus,setCashAmount,setPayChannelLabel,setDefault');
console.log('For example, another place where you don't need to do setFormatMobile',obj)
Copy the code

6. Batch processing

If you look at this, it looks like it’s almost there. But write down, you will know that the general background management system user list, data generally not only one. In general, it’s an array object. The following

let arr=[
    {
        cashAmount: 236700.// Amount of payment received (cent)
        cashDate: "The 2018-05-26 10:25:28".// Time of payment collection
        cashId: "SM2018022800020692"./ / collection ID
        cashStatus: 0.// Payment collection status
        createTime: "The 2018-05-23 10:26:25".// Create time
        custoName: Guangzhou Testing Co., LTD.// The name of the payee company
        id: "SM2018022800020692"./ / collection ID
        merchandisers: "Waiting".// Contact person of payment collection company
        ordId: "SO2018022800020692"./ / order ID
        payChannel: null.// Payment method
        remark: ""./ / note
        userMobile: "13226452474".// Phone number of the contact person of the payment collection company
    },
    {/* Omitted code */},
    {/* Omitted code */},
    {/* Omitted code */},
    // The omitted code
]
Copy the code

When you write it, you write it like this

arr.forEach((item,index) = >{
    arr[index]=handle.setCash(item);
})
console.log(arr)
Copy the code

There’s not much code, but if there’s a better solution, use the better solution. For example, batch processing. I’ll just write one more function.

let handle={
   /* omit code */
   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
    }
}
Copy the code

It’s a little bit easier to call than it was before, and the result is correct

arr=handle.batch(arr,'setCash')
console.log(arr)
Copy the code

Other parameters can be passed as well

arr=handle.batch(arr,'setCash'.'setCashStatus,setCashAmount,setPayChannelLabel,setDefault')
console.log(arr)
Copy the code

If you want to pass in more than one operation function

arr=handle.batch(arr,'setCashStatus,setCashAmount')
console.log(arr)
Copy the code

7. Summary

As for the usability of the API, I will mention these aspects for now. If I find other examples that can improve the usability of the API in other ways, I will post again. About this article, is also a way I am trying, if you have a better way to achieve, welcome to leave a message in the comment section.

— — — — — — — — — — — — — — — — — — — — — — — — — gorgeous line — — — — — — — — — — — — — — — — — — — —

If you want to know more, communicate with me and promote your position, please add me on wechat. Or follow my wechat public number: Waiting book Pavilion