• if/else 的重构方案


    if/else 的重构方案

    背景

    日常开发中总会遇到条件判断,而写程序归根结底也是各种条件判断来控制程序执行的。最常用也是最信手拈来的就是 if/else, 或者优雅点会用到 switch 来实现多个条件的判断。当然 if/else 的使用在开发人员当中也时常会引起热烈讨论,到底条件判断怎么写会更优雅效率更高,也在每个程序编写者心中有着各自不同的认识和定义。

    接下来做了一些条件判断的使用场景和优化方案的介绍,目的不是批判某种用法,仅是把更多的可使用方案介绍给大家。

    介绍

    总结前置:

    1. if...
    2. if...else...
    3. if...else if...else
    4. switch / case
    5. JavaScript 三元运算
    6. 短路运算符
    7. Logic + Control (数据结构 + 算法)方式

    首先,明确两个最基本的条件判断,if/else 和 switch/case 的用法。


    1. if 应用场景:单个分支执行中插入额外执行 如下:

    var n = 1
    console.log('n 等于', n )
    if ( n < 10 ) {
    	console.log('n 小于10 ')
    }
    n++
    console.log('n 等于 ', n )
    
    # 输入出结果:
    n 等于 1
    n 小于10 
    n 等于  2
    
    # 当n=20时,输出结果:
    n 等于 20
    n 等于 21
    

    工作原理:

         if 只有当指定条件为 Boolean值 为 true 时,该语句才会执行代码。

    应用场景:

         a. 值匹配

         b. 区间判断


    2. if/else 应用场景:两个分支的判断 如下:

    var n = true
    if (n) {
      console.log('n 等于 true')
    }
    else {
      console.log('n 不等于 true')
    }
    

    3. if/else if/else 应用场景:至少三个分支的判断 如下:

    a.值匹配:

    var n = 'c'
    
    if (n==='a') {
        console.log('---> a')
    }
    else if (n==='b') {
        console.log('---> b')
    }
    else {
        console.log('---> c')
    }
    

    b.区间判断

    var n = 6
    if ( n < 5 ) {
        console.log('n 小于 5')
    }
    else if (n >= 5 && n < 10) {
        console.log('n 小于 10,并且不小于 5')
    }
    else {
        console.log('n 不小于 10')
    }
    

    应用场景:

         a. 值匹配

         b. 区间判断


    4. switch/case 应用场景:多分支判断 如下:

    var n = 'c'
    
    switch (n) {
        case 'a':
            console.log('---> a')
            break
        case 'b':
            console.log('----> b')
            break
        default:
            console.log('----> default')
    }
    

    工作原理:

         首先设置表达式 n(通常是一个变量)。随后表达式的值会与结构中的每个 case 的值做比较。如果存在匹配,则与该 case 关联的代码块会被执行。请使用 **break** 来阻止代码自动地向下一个 case 运行。

    应用场景:

         a. 值匹配

    其次, 除了上述常用的两种条件判断方式,还有一个不得不说的判断方式,JavaScript三目运算


    5. JavaScript三元运算的应用场景:两个分支判断的快捷写法 如下:

    var n = 5
    n > 3 ? alert("5大于3") : alert("5小于3");
    

    工作原理:

         (condition1) ? ture-doing : else-doing; 语句在条件为 true 时执行代码,在条件为 false 时执行其他代码。

    应用场景:

         a. 值匹配

         b. 区间判断

    可以看出,三目运算可以替代 if...else... 的使用,但与 if..else... 稍有区别,if/else没有返回值,而三元运算是有返回值的,如下:

    // if...else..
    var n=1;
    if(n>1){
        n=0;
    }else{
        ++n;
    }
    console.log(n);
    #输出结果:2
    
    // 三目运算
    var n=1;
    n = n>1?0 : ++n; 
    console.log(n); 
    #输出结果为:2
    

    6. 短路运算符 如下:

    var a = true
    var b = true
    var c = function() {
      console.log('c函数执行了!')
      return false
    }
    var d = function() {
      console.log('d函数执行了!')
      return true
    }
    
    var e = function() {
        console.log('e函数执行了!')
        return true
    }
    
    a && b && c() && d() || e()
    
    #输出结果为:
    c函数执行了!
    e函数执行了!
    

    应用场景:

         Boolean 值 的连续判断,“&&” 只要不遇到 false 就会一直往下执行,遇到 false 会去执行 “||”后边的执行语句

    值得注意的是:

         1. 如果执行的是个函数,没有返回值,那么默认是 return false
         2. 判断值是 0 和 1 时,0相当于false,1相当于true


    7. Logic + Control (数据结构+算法),如下:

    const sendLog = function(LogName) {
        console.log( 'log result ----> ', LogName )
    }
    
    const jumpTo = function (pageName) {
        console.log( '要去到的页面 ----> ', pageName )
    }
    
    const actions = {
        '1': ['processing', 'IndexPage'],
        '2': ['fail', 'FailPage'],
        '3': ['fail', 'FailPage'],
        '4': ['success', 'SuccessPage'],
        '5': ['cancel', 'CancelPage'],
        'default': ['other', 'Index']
    }
    
    const clickHandler = (status) => {
        let action = actions[status] || actions['default'], 
          LogName = action[0],
          pageName = action[1]
        sendLog(LogName)
        jumpTo(pageName)
    }
    

    应用场景:

         判断条件(即:状态值)有统一明确的匹配规则,如:可枚举型,可 switch/case 判断 等有明确的状态值的场景。


    先举个一个多层 if/else 嵌套的代码重构的例子,让大家理解其使用场景(代码取于网上 稍作修改):


    案例:

    const sendLog = function(LogName) {
        console.log( 'log result ----> ', LogName )
    }
    
    const jumpTo = function (pageName) {
        console.log( '要去到的页面 ----> ', pageName )
    }
    
    const clickHandler = (status) => {
      if(status === 1) {
        sendLog('processing')
        jumpTo('IndexPage')
      } else if(status === 2) {
        sendLog('fail')
        jumpTo('FailPage')
      } else if(status === 3) {
        sendLog('fail')
        jumpTo('FailPage')
      } else if(status === 4) {
        sendLog('success')
        jumpTo('SuccessPage')
      } else if(status === 5) {
        sendLog('cancel')
        jumpTo('CancelPage')
      } else {
        sendLog('other')
        jumpTo('Index')
      }
    }
    
    

    优化1:

    swich/case 方式重构:

    const clickHandler = (status) => {
      switch (status) {
        case 1:
          sendLog('processing')
          jumpTo('IndexPage')
          break
        case 2:
        case 3:
          sendLog('fail')
          jumpTo('FailPage')
          break
        case 4:
          sendLog('success')
          jumpTo('SuccessPage')
          break
        case 5:
          sendLog('cancel')
          jumpTo('CancelPage')
          break
        default:
          sendLog('other')
          jumpTo('Index')
      }
    }
    

    注意: case 2case 3 的逻辑一样,可以省略写法。

    代码优雅了些,并清洗了许多。


    优化2:

    logic + control 方式 重构:

    const actions = {
        '1': ['processing', 'IndexPage'],
        '2': ['fail', 'FailPage'],
        '3': ['fail', 'FailPage'],
        '4': ['success', 'SuccessPage'],
        '5': ['cancel', 'CancelPage'],
        'default': ['other', 'Index']
    }
    
    const clickHandler = (status) => {
        let action = actions[status] || actions['default'], 
          LogName = action[0],
          pageName = action[1]
        sendLog(LogName)
        jumpTo(pageName)
    }
    

    代码清爽了许多,并去除了重复代码的编写。


    优化3:

    logic + control 方式,加Map 对象的应用:

    const actions = new Map([
      ['1', ['processing', 'IndexPage']],
      ['2', ['fail', 'FailPage']],
      ['3', ['fail', 'FailPage']],
      ['4', ['success', 'SuccessPage']],
      ['5', ['cancel', 'CancelPage']],
      ['default', ['other', 'Index']]
    ])
    
    const clickHandler = (status) => {
      let action = actions.get(status) || actions.get('default')
      sendLog(action[0])
      jumpTo(action[1])
    }
    

    Map对象的优势,是 key 值可以是任意值(Boolean, String, Object, 正则 等)。


    理解与启发:一个多层 if/else 嵌套 重构的例子

    一个两层的if/else嵌套,根据用户身份 和 status 做不同的处理,理解整个重构的过程,你会获益匪浅

    例子:

    const clickHandler = (status, identity) => {
      if(identity == 'guest') {
        if(status === 1) {
          // to do something
        } else if (status === 2) {
          // to do something
        } else if (status === 3) {
          // to do something
        } else if (status === 4) {
          // to do something
        } else if (status === 5) {
          // to do something
        } else {
          // to do something
        }
      } else if(identity == 'master') {
        if(status === 1) {
          // to do something
        } else if (status === 2) {
          // to do something
        } else if (status === 3) {
          // to do something
        } else if (status === 4) {
          // to do something
        } else if (status === 5) {
          // to do something
        } else {
          // to do something
        }
      }
    }
    
    

    缺点:代码冗长,逻辑梳理起来较为复杂,后续维护困难:


    优化 1:

    const actions = new Map([
      ['guest_1', () => {/* to do something */}],
      ['guest_2', () => {/* to do something */}],
      ['guest_3', () => {/* to do something */}],
      ['guest_4', () => {/* to do something */}],
      ['guest_5', () => {/* to do something */}],
      ['master_1', () => {/* to do something */}],
      ['master_2', () => {/* to do something */}],
      ['master_3', () => {/* to do something */}],
      ['master_4', () => {/* to do something */}],
      ['master_5', () => {/* to do something */}],
      ['default', () => {/* to do something */}],
    ])
    

    上述代码的逻辑:

    • 把两个条件拼接成字符串
    • 以拼接的条件字符串作为key,以处理函数作为值的Map对象进行查找并执行

    优化 2:

    也可以用对象的方式来实现,这也是我们常用的方式:

    const actions = {
      'guest_1': () => {/* to do something */},
      'guest_2': () => {/* to do something */},
      'guest_3': () => {/* to do something */},
      'guest_4': () => {/* to do something */},
      'guest_5': () => {/* to do something */},
      'master_1': () => {/* to do something */},
      'master_2': () => {/* to do something */},
      'master_3': () => {/* to do something */},
      'master_4': () => {/* to do something */},
      'master_5': () => {/* to do something */},
      'default': () => {/* to do something */}
    }
    

    优化 3:

    当然觉得拼接字符串的方式不够优雅,也可以用对象作为key, 如下:

    const actions = new Map([
        [{indentity: 'guest', status: 1}, () => {/* to do something */}],
        [{indentity: 'guest', status: 2}, () => {/* to do something */}],
        [{indentity: 'guest', status: 3}, () => {/* to do something */}],
        [{indentity: 'guest', status: 4}, () => {/* to do something */}],
        [{indentity: 'guest', status: 5}, () => {/* to do something */}],
        [{indentity: 'guest', status: 'default'}, () => {/* to do something */}],
        [{indentity: 'master', status: 1}, () => {/* to do something */}],
        [{indentity: 'master', status: 2}, () => {/* to do something */}],
        [{indentity: 'master', status: 3}, () => {/* to do something */}],
        [{indentity: 'master', status: 4}, () => {/* to do something */}],
        [{indentity: 'master', status: 5}, () => {/* to do something */}],
        [{indentity: 'master', status: 'default'}, () => {/* to do something */}],
    ])
    
    const clickHandler = (identity, status) => {
      let action = [...actions].filter((key, value) => {
        key.identity === identity && key.status === status
      })
      action.forEach(([key, value]) => {
        value.call(this)
      })
    }
    

    MapObject 的区别在于,Mapkey值可以是任意数据类型, 这也正是 Map 对象的优势。


    优化 4:

    Map 加 正则表达式匹配的方式:

    function A () {
      // to do something
    }
    
    function B () {
      // to do something
    }
    
    function C () {
      // to do something
    }
    
    const actions = new Map([
      [/^guest_[1-4]$/, fucnctionA],
      [/^master_[1-5]$/, functionB],
      [/^guest_.$/, functionC]
    ])
    
    const clickHandler = (identity, status) {
      let action = [...actions].filter((key, value) => {key.test(`${identity}_${status}`)})
      action.forEach(([key, value]) => {value.call(this)})
    }
    

    个人总结:

    几种条件判断的最佳使用场景:

    1. 单分支插入执行块

      // a. if 方式
      if (【条件】) { doSomething...}
      
      // b.短路运算符方式
      【条件】 && (doSomething...)
      

    1. 两个分支的执行

      // a. if...else... 方式
      if (【条件】) {
        // do something ...
      }
      else {
        // do something ...
      }
      
      // b. 三元运算符
      【条件】 ? (doSomethingA...) : (doSomethingB...)
      
      // c. 短路运算符方式
      【条件】 && (doSomethingA...) || (doSomethingB)
      

    1. 三个分支的执行

      // a. if...else if...else... 方式
      if (【条件1】) {
      	// do something ...
      }
      else if (【条件2】) {
      	// do something ...
      }
      else {
      	// do something ...
      }
      
      // b. 三元运算符 (勉强接受)
      【条件1】 ? (doSomethingA...) : (【条件2 ? (doSomethingB...) : (doSomethingC...)】)
      
      // c.短路运算符 方式(不推荐)
      【条件1】&& (doSomethingA...) || (【条件2】&& (doSomethingB..., true)) || (doSomethingC...)
      

    1. 多个分支的执行

      // a. if...else if...else... (不推荐)
      
      // b. switch...case...(确定的枚举个数,并且数量不小于3个,推荐使用,并且 可枚举个数越多越不推荐)
      switch (status) {
      	case 【status值1】:
      		// do something ...
      		break
      	case 【status值2】:
      		// do something ...
      		break
      	case 【status值3】:
      		// do something ...
      		break
      	default:
      		// do something ...
      		break
      }
      
      // c. logic + control(推荐)
      const actions = new Map([
      	[【key1】, 【value1】],
      	[【key2】, 【value2】],
      	[【key3】, 【value3】],
      	[【key4】, 【value4】],
      	...
      ])
      
      const handler = function(【条件】) {
      	let action = [...actions].filter((key, value) => { key 匹配 【条件】 })
      	action.forEach(([key, value]) => {value.call(this)})
      }
      
      

    【注】:如果判断的条件是区间值,可先将区间进行分类划分后再使用 b 或 c 的判断方式


    参考:
        一个多层 if / else 嵌套的代码重构案例(JavaScript)

  • 相关阅读:
    UVa中国麻将(Chinese Mahjong,Uva 11210)
    Nginx-upstream模块
    Nginx-配置文件
    Nginx 负载均衡和反向代理实践
    Nginx-1
    linux下发送报警邮件(mailx)
    dns服务器搭建
    linux 时间相关
    Centos7调整swap分区
    rm 删除命令
  • 原文地址:https://www.cnblogs.com/cnblogs-jcy/p/14778095.html
Copyright © 2020-2023  润新知