消除if else, 让你的代码看起来更优雅

 4182

今天给大家介绍如何消除if else, 让你的代码看起来更优雅,一起来看看吧。

前言

应该有不少同学有遇到过充斥着if else的代码,面对这样的一团乱麻,简单粗暴地继续增量修改常常只会让复杂度越来越高,可读性越来越差。那么是时候重构了,花几分钟看看这篇文章, 说不定对你有一丢丢帮助。

场景一: 根据status显示对应名称

优化方案1:object对象

  1. const statusStr = {
  2.   '1': '待付款',
  3.   '2': '待发货',
  4.   '3': '已发货',
  5.   '4': '交易完成',
  6.   '5': '交易关闭',
  7.   'default': '',
  8. }
  9. const getStatus = (status) =>{
  10.   return statusStr[status] || statusStr['default']
  11. }

将判断条件作为对象的属性名,将处理逻辑作为对象的属性值,在按钮点击的时候,通过对象属性查找的方式来进行逻辑判断.

优化方案2: Map对象

  1. const statusStr = new map([
  2.   '1': ['待付款'],
  3.   '2': ['待发货'],
  4.   '3': ['已发货'],
  5.   '4': ['交易完成'],
  6.   '5': ['交易关闭'],
  7.   'default': [''],
  8. ])
  9. const getStatus = (status) =>{
  10.   let actions = statusStr.get(status) || statusStr.get('default')
  11.   return  actions[0];
  12. }

这样写用到了es6里的Map对象,那么Map对象和Object对象有什么区别呢?

一个对象通常都有自己的原型,所以一个对象总有一个"prototype"键。 一个对象的键只能是字符串或者Symbols,但一个Map的键可以是任意值。 你可以通过size属性很容易地得到一个Map的键值对个数,而对象的键值对个数只能手动确认。

场景二:多个condition对应名称

现在把问题升级一下, 以前按钮点击时候只需要判断status,现在还需要判断用户的身份:
「举个栗子:」

  1. const onButtonClick = (status,identity)=>{
  2.   if(identity == 'guest'){
  3.     if(status == 1){
  4.       //do sth
  5.     }else if(status == 2){
  6.       //do sth
  7.     }else if(status == 3){
  8.       //do sth
  9.     }else if(status == 4){
  10.       //do sth
  11.     }else if(status == 5){
  12.       //do sth
  13.     }else {
  14.       //do sth
  15.     }
  16.   }else if(identity == 'master') {
  17.     if(status == 1){
  18.       //do sth
  19.     }else if(status == 2){
  20.       //do sth
  21.     }else if(status == 3){
  22.       //do sth
  23.     }else if(status == 4){
  24.       //do sth
  25.     }else if(status == 5){
  26.       //do sth
  27.     }else {
  28.       //do sth
  29.     }
  30.   }
  31. }

上面的例子我们可以看到,当你的逻辑升级为二元判断时,你的判断量会加倍,你的代码量也会加倍,这时怎么写更清爽呢?

优化方案1: 将condition用字符拼接形式存在Map对象里

  1. const actions = new Map([
  2.   ['guest_1', ()=>{/*do sth*/}],
  3.   ['guest_2', ()=>{/*do sth*/}],
  4.   ['guest_3', ()=>{/*do sth*/}],
  5.   ['guest_4', ()=>{/*do sth*/}],
  6.   ['guest_5', ()=>{/*do sth*/}],
  7.   ['master_1', ()=>{/*do sth*/}],
  8.   ['master_2', ()=>{/*do sth*/}],
  9.   ['master_3', ()=>{/*do sth*/}],
  10.   ['master_4', ()=>{/*do sth*/}],
  11.   ['master_5', ()=>{/*do sth*/}],
  12.   ['default', ()=>{/*do sth*/}],
  13. ])
  14. const onButtonClick = (identity,status)=>{
  15.   let action = actions.get(`${identity}_${status}`) || actions.get('default')
  16.   action.call(this)
  17. }

上述代码核心逻辑是:把两个条件拼接成字符串,并通过以条件拼接字符串作为键,以处理函数作为值的Map对象进行查找并执行,这种写法在多元条件判断时候尤其好用。

优化方案2: 将condition用字符拼接形式存在Object对象里

  1. const actions = {
  2.   'guest_1':()=>{/*do sth*/},
  3.   'guest_2':()=>{/*do sth*/},
  4.   //....
  5. }
  6. const onButtonClick = (identity,status)=>{
  7.   let action = actions[`${identity}_${status}`] || actions['default']
  8.   action.call(this)
  9. }

优化方案3: 将condition用Object对象形式存在Map对象里

可能用查询条件拼成字符串有点别扭,那还有一种方案,就是用Map对象,以Object对象作为key:

  1. const actions = new Map([
  2.   [{identity:'guest',status:1},()=>{/*do sth*/}],
  3.   [{identity:'guest',status:2},()=>{/*do sth*/}],
  4.   //...
  5. ])
  6. const onButtonClick = (identity,status)=>{
  7.   let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status))
  8.   action.forEach(([key,value])=>value.call(this))
  9. }

场景三:根据status做出相应操作

「举个栗子:」

  1. function init () {
  2.     if (isAnswer === 1) {
  3.         if (isOldUser === 1) {
  4.             // ...
  5.         } else if (isOldUser === 2) {
  6.             // ...
  7.         }
  8.     } else if (isAnswer === 2) {
  9.         if (isOldUser === 1) {
  10.             // ...
  11.         } else if (isOldUser === 2) {
  12.             // ...
  13.         }
  14.     } else if (isAnswer === 3) {
  15.         if (isOldUser === 1) {
  16.             // ...
  17.         } else if (isOldUser === 2) {
  18.             // ...
  19.         }
  20.     }
  21. }

优化方案1: 查找表,职责链查找表

  1. const rules = [
  2.     {
  3.         match (an, old) {if (an === 1) {return true}},
  4.         action (an, old) {
  5.         if (old === 1) {// ...} 
  6.         else if (old === 2) {// ...}
  7.         }
  8.     },
  9.     {
  10.         match (an, old) { if (an === 2) {return true } },
  11.         action (an, old) {
  12.             if (old === 1) {// ...} 
  13.             else if (old === 2) {// ...}
  14.         }
  15.     },
  16.     {
  17.         match (an, old) {if (an === 3) {return true}},
  18.         action (an, old) {
  19.             if (old === 1) {// ...} 
  20.             else if (old === 2) {// ...}
  21.         }
  22.     }
  23. ]
  24. function init (an, old) {
  25.     for (let i = 0; i < rules.length; i++) {
  26.         // 如果返回true
  27.         if (rules[i].match(an, old)) {
  28.             rules[i].action(an, old)
  29.         }
  30.     }
  31. }
  32. init(isAnswer, isOldUser)

虽然可能看着是治标不治本,其实不然,init函数的复杂度大大的降低了。我们已经把控制流程的复杂逻辑,拆分到determineAction函数中

优化方案2: 函数式编程

  1. import R from 'ramda'
  2. var fn = R.cond([
  3.   [R.equals(0),   R.always('water freezes at 0°C')],
  4.   [R.equals(100), R.always('water boils at 100°C')],
  5.   [R.T,           temp => 'nothing special happens at ' + temp + '°C']
  6. ]);
  7. fn(0); //=> 'water freezes at 0°C'
  8. fn(50); //=> 'nothing special happens at 50°C'
  9. fn(100); //=> 'water boils at 100°C'

场景四: 根据范围去进行不同处理

「举个栗子:」比如大家可能会遇到类似下面的需求:比如某平台的信用分数评级,超过700-950,就是信用极好,650-700信用优秀,600-650信用良好,550-600信用中等,350-550信用较差。

  1. function showGrace(grace) {
  2.     let _level='';
  3.     if(grace>=700){
  4.         _level='信用极好'
  5.     }
  6.     else if(grace>=650){
  7.         _level='信用优秀'
  8.     }
  9.     else if(grace>=600){
  10.         _level='信用良好'
  11.     }
  12.     else if(grace>=550){
  13.         _level='信用中等'
  14.     }
  15.     else{
  16.         _level='信用较差'
  17.     }
  18.     return _level;
  19. }

优化方案1: 用look-up表,把配置数据和业务逻辑分离

  1. function showGrace(grace,level,levelForGrace) {
  2.     for(let i=0;i<level.length;i++){
  3.         if(grace>=level[i]){
  4.             return levelForGrace[i];
  5.         }
  6.     }
  7.     //如果不存在,那么就是分数很低,返回最后一个
  8.     return levelForGrace[levelForGrace.length-1];
  9. }
  10. let graceForLevel=[700,650,600,550];
  11. let levelText=['信用极好','信用优秀','信用良好','信用中等','信用较差'];

小结

很多情况下我们都可以使用更灵活的方式去替代if else以及switch, 但也不是所有的if else都需要替代, 视情况而定。




TAG标签:
本文网址:https://www.zztuku.com/index.php/detail-7965.html
站长图库 - 消除if else, 让你的代码看起来更优雅
申明:如有侵犯,请 联系我们 删除。

评论(0)条

您还没有登录,请 登录 后发表评论!

提示:请勿发布广告垃圾评论,否则封号处理!!

    编辑推荐