preface

The new company, the former left a project, which is filled with a lot of if… else… “, is the second, the main even notes are very few. Faced with code that was already online, I didn’t want to refactor it because it was too expensive, so I had to push myself not to write it

The problems faced?

Sometimes, we may face such a business logic (education company), if answered the question, if answered the question did not pass, if did not answer the question. If you don’t use a particular pattern, you might write code like this. Lump by lump if… Else is very uncomfortable to look at and difficult to maintain.

/** * initialize function * if... else if... * @return undefined */
function init () {
    // Have you answered the question 1? Yes, yes, yes, yes, no, no, no
    let isAnswer
    // Whether it is old user 1- Old user 2- New user
    let isOldUser

    if (isAnswer === 1) {
        // ...
    } else if (isAnswer === 2) {
        // ...
    } else if (isAnswer === 3) {
        // ...
    }

    if (isOldUser === 1) {
        // ...
    } else if (isOldUser === 2) {
        // ...}}Copy the code
/** * initialize function * if... else if... Nested case * @return undefined */
function init () {
    if (isAnswer === 1) {
        if (isOldUser === 1) {
            // ...
        } else if (isOldUser === 2) {
            // ...}}else if (isAnswer === 2) {
        if (isOldUser === 1) {
            // ...
        } else if (isOldUser === 2) {
            // ...}}else if (isAnswer === 3) {
        if (isOldUser === 1) {
            // ...
        } else if (isOldUser === 2) {
            // ...}}}Copy the code

Solution 1: lookup table, chain of responsibilities lookup table

It may seem like a palliative, but it’s not, and the complexity of the init function is dramatically reduced. We have split the complex logic of the control process into the determineAction function

// If... else if... Simple question
const rules = {
    isAnswer1 () {
        return code
    },
    isAnswer2 () {
        return code
    },
    isAnswer3 () {
        return code
    }
}

function determineAction (type) {
    if (isAnswer === 1) {
        return 'isAnswer1'
    } else if (isAnswer === 2) {
        return 'isAnswer2'
    } else if (isAnswer === 3) {
        return 'isAnswer3'}}function init () {
    let key = determineAction(isAnswer)
    return rules[key]
}
Copy the code
/ / in the face of the if... else if... Else nested complex case

const rules = [
    {
        match (an, old) {
            if (an === 1) {
                return true
            }
        },

        action (an, old) {
            if (old === 1) {
                // ...
            } else if (old === 2) {
                // ...
            }
        }
    },
    {
        match (an, old) {
            if (an === 2) {
                return true
            }
        },

        action (an, old) {
            if (old === 1) {
                // ...
            } else if (old === 2) {
                // ...
            }
        }
    },
    {
        match (an, old) {
            if (an === 3) {
                return true
            }
        },

        action (an, old) {
            if (old === 1) {
                // ...
            } else if (old === 2) {
                // ...}}}]function init (an, old) {
    for (let i = 0; i < rules.length; i++) {
        // If true is returned
        if (rules[i].match(an, old)) {
            rules[i].action(an, old)
        }
    }
}

init(isAnswer, isOldUser)
Copy the code

⬆️ in the complicated case above, you can also pull out the action but you might have to write out three separate functions, because there are three different cases of an


Solution 2: Aspect Oriented Programming (AOP)

For the prototype chain of Function, extend the after syntax and, if necessary, operate directly inside the Function and return the result. Return ‘next’ if the condition is not met to call the next node in the chain of responsibilities. Function.prototype.after is a Function that executes after before this Function executes

// If... else if... Simple question
Function.prototype.after = function (nextFn) {
    let self = this
    return function (. rest) {
        letcode = self(... rest)if (code === 'next') {
            returnnextFn(... rest) }return code
    }
}

// Refactor the original function

function isAnswer1 (type) {
    if (type === 1) {
        return code
    }
    return 'next'
}

function isAnswer2 () {
    if (type === 2) {
        return code
    }
    return 'next'
}

function isAnswer3 () {
    if (type === 3) {
        return code
    }
    return 'next'
}

let isAnswerFn = isAnswer1.after(isAnswer2).after(isAnswer3)

isAnswerFn(isAnswer)
Copy the code
/ / in the face of the if... else if... Else nested complex case

function isAnswer1 (an, old) {
    if (an === 1) {
        return isOldUserFn1(an, old)
    }
    return 'next'
}

function isAnswer2 (an, old) {
    if (an === 2) {
        return isOldUserFn2(an, old)
    }
    return 'next'
}

function isAnswer3 (an, old) {
    if (an === 3) {
        return isOldUserFn3(an, old)
    }
    return 'next'
}

/** * isAnswer == 1 isOldUser == 1 */
function isAnswer1IsOldUser1 (an, old) {
    if (old === 1) {
        return code
    }
    return 'next'
}

/** * isAnswer == 1 isOldUser == 2
function isAnswer1IsOldUser2 (an, old) {
    if (old === 2) {
        return code
    }
    return 'next'
}

/** * isAnswer == 2 isOldUser == 1
function isAnswer2IsOldUser1 (an, old) {
    if (old === 1) {
        return code
    }
    return 'next'
}

/** * isAnswer == 2 isOldUser == 2 */
function isAnswer2IsOldUser2 (an, old) {
    if (old === 2) {
        return code
    }
    return 'next'
}

/** * isAnswer == 3 isOldUser == 1
function isAnswer3IsOldUser1 (an, old) {
    if (old === 1) {
        return code
    }
    return 'next'
}

/** * isAnswer == 3 isOldUser == 2 */
function isAnswer3IsOldUser2 (an, old) {
    if (old === 2) {
        return code
    }
    return 'next'
}

let isAnswerFn = isAnswer1.after(isAnswer2).after(isAnswer3)

// Three chains of responsibility
let isOldUserFn1 = isAnswer1IsOldUser1.after(isAnswer1IsOldUser2)
let isOldUserFn2 = isAnswer2IsOldUser1.after(isAnswer2IsOldUser2)
let isOldUserFn3 = isAnswer3IsOldUser1.after(isAnswer3IsOldUser2)

isAnswerFn(isAnswer, isOldUser)
Copy the code

Solution 3: Functional programming

Use functional programming libraries such as Ramda to solve this problem, 🔗 link: ramda.cn/docs/#cond

import R from 'ramda'

var fn = R.cond([
  [R.equals(0),   R.always('water freezes at 0 ° C')],
  [R.equals(100), R.always('the water boils at 100 ° C')],
  [R.T,           temp => 'nothing special happens at ' + temp + '° C']]); fn(0); //=> 'water freezes at 0°C'
fn(50); //=> 'Nothing happens at 50°C'
fn(100); => 'water displayed at 100°C'
Copy the code