策略模式:和大量的if-else说拜拜~

在群里看到群友提问:

先来模拟下他的代码:

jsx 复制代码
// 模拟后端返回数据
let item = {
    targetName: '低',
    viewMod: '场景', // 或者 '区域'
    thresholdValue: '2,4,6,8',
    tatgetAlgo: ''
};

// 模拟逻辑
if (item.targetName == '无') {
    item.tatgetAlgo = '根满分';
} else if (item.targetName == '弱') {
    if (item.viewMod.indexOf('场景') > -1) {
        let arr = item.thresholdValue.split(',', 7);
        item.tatgetAlgo = `以下 ${arr[0]} % ;`;
    } else if (item.viewMod.indexOf('区域') > -1) {
        let arr = item.thresholdValue.split(',', 3);
        item.tatgetAlgo = `1、问题 ${arr[0]} ;`;
    }
} else if (item.targetName == '低') {
    if (item.viewMod.indexOf('场景') > -1) {
        let arr = item.thresholdValue.split(',', 4);
        item.tatgetAlgo = `以下 ${arr[3]}G;`;
    } else if (item.viewMod.indexOf('区域') > -1) {
        let arr = item.thresholdValue.split(',', 2);
        item.tatgetAlgo = `1、问题得分`;
    }
}

可以看到有大量的if判断操作,逻辑比较复杂,如果线上有bug,处理起来也很麻烦。那么有什么好的方式来优化分支代码呢? ps:文章末尾有方案哦

先来看一个简单的例子:

定义一个名为 speak 的函数,该函数根据传入的参数 name 的不同值输出不同的字符串。具体来说:

  • 如果 name 的值是 '剑圣',则输出 '真正的大师永远都怀着一颗学徒的心。'
  • 如果 name 的值是 '盖伦',则输出 '生命不息,战斗不止。'
  • 如果 name 的值是 '阿木木',则输出 '我还以为你从来都不会选我呢。'
  • 如果 name 的值是 '诺手',则输出 '只有我才能带领我们走向胜利。'
  • 如果 name 的值不是以上任何一个值,那么输出 '我是谁?'

青铜

jsx 复制代码
function speak(name){
    if(name==='剑圣'){
        console.log('真正的大师永远都怀着一颗学徒的心。')
    } else if(name==='盖伦'){
        console.log('生命不息,战斗不止。')
    } else if(name==='阿木木'){
        console.log('我还以为你从来都不会选我呢。')
    } else if(name==='诺手'){
        console.log('只有我才能带领我们走向胜利。')
    } else {
       console.log('我是谁?')
    }
}

speak('诺手') //'只有我才能带领我们走向胜利。'

问题:代码中含有大量的 if-else 判断逻辑,维护困难。当代码逻辑数量达到几百个时,代码的可读性大大降低。

白银

白银参考了策略模式的思路,将逻辑封装到一个对象中。这种方式使得这个对象能够独立出来,只需专注于维护这个对象本身即可。

jsx 复制代码
function speak(name){
    let map = {
        '剑圣': '真正的大师永远都怀着一颗学徒的心。',
        '盖伦': '生命不息,战斗不止。',
        '阿木木': '我还以为你从来都不会选我呢。',
        '诺手': '只有我才能带领我们走向胜利。',
    }

    if(map[name]){
       console.log(map[name])
    } else {
       console.log('我是谁?')
    }
}

speak('诺手') //'只有我才能带领我们走向胜利。'

问题:这里的方法都一致只是参数不同,,盖伦说我需要蹲草丛里,我是草丛伦,阿木木说猥琐发育等我六级,诺手说无情铁手致命打击大杀四方。这样写,似乎是没问题的。但如果每个方法不同,阁下该如何应对呢?比如在说台词之后,需要有自己单独的逻辑,比如盖伦说生命不息,战斗不止后再说我需要蹲草丛里,我是草丛伦

黄金

将对象中的处理逻辑单独封装成一个函数,内部处理自己的逻辑。

jsx 复制代码
function speak(name){
    let map = {
        '剑圣': ()=> {
            console.log('真正的大师永远都怀着一颗学徒的心。','你们先上我开团')
         },
        '盖伦': ()=> { 
            console.log('生命不息,战斗不止');
            console.log('我需要蹲草丛里,我是草丛伦')
         },
        '阿木木': ()=> {
            console.log('我还以为你从来都不会选我呢');
            console.log('猥琐发育等我六级');
            console.log('猥琐发育等我六级!!');
         },
        '诺手': ()=> {
            console.log('无情铁手致命打击大杀四方')
        },
    }

    if(map[name]){
      map[name]()
    } else {
       console.log('我是谁?')
    }
}

speak('剑圣') //'真正的大师永远都怀着一颗学徒的心。' '你们先上我开团'

问题: 现在回答逻辑抽离出来了,但是判断逻辑还是有问题,。如果每个条件中的判断逻辑不一样怎么办呢?

铂金

我要除了判断姓名外,还要判断剑圣和盖伦的武器是什么不是剑,要判断阿木木等级是不是大于等于6级。诺手的等级是不大于等于16级。铂金可能会选择返璞归真,直接用if else实现,这样的逻辑很常见,也比较清晰一些。

jsx 复制代码
function speak(name,weapon,level){
    if(name==='剑圣' && weapon.includes('剑')){
        console.log('真正的大师永远都怀着一颗学徒的心。','你们先上我开团')
    }else if(name==='盖伦'&&weapon.includes('剑')){
         console.log('生命不息,战斗不止');
         console.log('我需要蹲草丛里,我是草丛伦');
    }else if(name==='阿木木'&&level>=6){
        console.log('我还以为你从来都不会选我呢');
        console.log('猥琐发育等我六级');
        console.log('猥琐发育等我六级!!');
    }else if(name==='诺手'&&level>=16){
        console.log('无情铁手致命打击大杀四方')
    }else {
       console.log('我是谁?')
    }
}

speak('盖伦','大宝剑',4) //'生命不息,战斗不止'  //'我需要蹲草丛里,我是草丛伦'
speak('盖伦','大保健',6) //'我是谁?'

钻石

怎么将上面的逻辑封装成策略模式呢?来看看钻石的做法。首先将map改造成一个二维数组,每一子项数组中第一项是判断逻辑函数,第二项是回答逻辑函数。

jsx 复制代码
function speak(name,weapon,level){
    let map = [
        [ 
            ()=> { name==='剑圣' && weapon.includes('剑') }, 
            ()=> { console.log('真正的大师永远都怀着一颗学徒的心。','你们先上我开团')}
        ],
        [ 
            ()=> { name==='盖伦' && weapon.includes('剑') }, 
            ()=> { console.log('生命不息,战斗不止'); console.log('我需要蹲草丛里,我是草丛伦')}
        ],
        [ 
            ()=> { name==='阿木木' && level>=6}, 
            ()=> {  console.log('我还以为你从来都不会选我呢');console.log('猥琐发育等我六级'); console.log('猥琐发育等我六级!!');}
        ],
        [ 
            ()=> { name==='诺手' && level>=16 }, 
            ()=> { console.log('无情铁手致命打击大杀四方') }
        ],
    ]
    const target = map.find(item=>item[0]())
    if(target){
      target[1]()
    } else {
       console.log('我是谁?')
    }
}

speak('盖伦','大宝剑',4) //'生命不息,战斗不止'  //'我需要蹲草丛里,我是草丛伦'
speak('盖伦','大保健',6) //'我是谁?'

同理,按照这个思路可以把文章开头的例子优化了:

jsx 复制代码
// 调用示例
const item = {
    targetName: '弱',
    viewMod: ['场景'],
    thresholdValue: '1,2,3,4,5,6,7'
};

let map = [
    [
        () => item.targetName === '无',
        () => {
            item.tatgetAlgo = '根满分';
        }
    ],
    [
        () => item.targetName === '弱' && item.viewMod.indexOf('场景') > -1,
        () => {
            let arr = item.thresholdValue.split(',', 7);
            item.tatgetAlgo = `以下 ${arr[0]} % ;`;
        }
    ],
    [
        () => item.targetName === '弱' && item.viewMod.indexOf('区域') > -1,
        () => {
            let arr = item.thresholdValue.split(',', 3);
            item.tatgetAlgo = `1、问题 ${arr[0]} ;`;
        }
    ],
    [
        () => item.targetName === '低' && item.viewMod.indexOf('场景') > -1,
        () => {
            let arr = item.thresholdValue.split(',', 4);
            item.tatgetAlgo = `以下 ${arr[3]}G;`;
        }
    ],
    [
        () => item.targetName === '低' && item.viewMod.indexOf('区域') > -1,
        () => {
            let arr = item.thresholdValue.split(',', 2);
            item.tatgetAlgo = `1、问题得分`;
        }
    ]
];

const target = map.find(item => item[0]());
if (target) {
    target[1]();
}

最强王者

最强王者段位的代码,就靠评论区的各位了~

相关推荐
undefined&&懒洋洋1 分钟前
Web和UE5像素流送、通信教程
前端·ue5
大前端爱好者2 小时前
React 19 新特性详解
前端
随云6322 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6322 小时前
WebGL编程指南之进入三维世界
前端·webgl
J老熊2 小时前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
我爱学Python!2 小时前
面试问我LLM中的RAG,秒过!!!
人工智能·面试·llm·prompt·ai大模型·rag·大模型应用
OLDERHARD3 小时前
Java - LeetCode面试经典150题 - 矩阵 (四)
java·leetcode·面试
寻找09之夏3 小时前
【Vue3实战】:用导航守卫拦截未保存的编辑,提升用户体验
前端·vue.js
非著名架构师3 小时前
js混淆的方式方法
开发语言·javascript·ecmascript
银氨溶液4 小时前
MySql数据引擎InnoDB引起的锁问题
数据库·mysql·面试·求职