02-策略模式

1. 策略模式是什么

在多种不同的方式中,权衡各影响因素,选择出优的那个。

2. 如何去理解

从广州出发去厦门,可以有的出行方式是飞机、高铁、汽车等,存在的影响因素有舒适度、价格、旅程耗时、当天可供出现的方式等。

具体采用何种方式,综合影响因素得出,不同的人有不同的优先考虑因素。

常规的做法是根据不同的影响因素,划分不同出现方式的优先级,比如以旅程耗时划分,为飞机 > 高铁 > 汽车。

存在的缺点是有许多的判断,并且当影响因素有变化时(随着时代发展,出现个人飞行器),需要去维护每一处的判断。

3. 如何去使用

将“分封制”改为“*集权制”

归拢各影响因素的逻辑到一个专门的处理对象中,输入因素,得出结果。

4. 举个栗子

计算员工当月奖金

奖金基数1000,绩效为S的员工奖金4倍,绩效A的员工奖金3倍,绩效B的员工奖金2倍。

我们可以很快就实现出这样的程序:

const bonusS = (salary) => {
    return salary * 4
}
const bonusA = (salary) => {
    return salary * 3
}
const bonusB = (salary) => {
    return salary * 2
}

/**
 * 依据绩效级别,计算员工当月奖金
 * @params {String} level - 员工绩效级别
 * @params {Number} salary - 奖金基数
 */
const calcBonus = (level, salary) => {
    if (level === 'S') {
        return bonusS(salary)
    }
    if (level === 'A') {
        return bonusA(salary)
    }
    if (level === 'B') {
        return bonusB(salary)
    }
}

calcBonus('S', 1000)

简单直白的写法,功能上完全没有问题,但是我们从代码层面上来说,会存在些什么问题?

存在问题:
(1)if判断语句过多,不易维护;
(2)calcBonus计算函数随着需求迭代,可能会越来越庞大,且当需求变更时(如新增一个绩效级别)缺乏弹性。


采用策略模式进行改造

const bonusCategories = {
	S: (salary) => {
        return salary * 4
    },
    A: (salary) => {
        return salary * 3
    },
    B: (salary) => {
        return salary * 2
    }
}

const calcBonus = (level, salary) => {
    return bonusCategories[level](salary)
}

calcBonus('S', 1000)

5. 开发实践

封装业务请求

往常做法:

const type = isAddMethod ? 'post' : 'put',
      url = '/table' + isAddMethod ? '' : '/ 123'

this.$api[type](url, {
    a: 1,
    b: 2
}, {
    headers: {
        'Content-type': 'application/x-www-form-urlencoded'
    }
}).then(d => {
    // do something...
})

存在问题:
不易维护,页面可能存在多个地方调用同一个接口,当接口有变化时,需要改动多次

策略模式改造

策略对象:

写于 vuex

getData ({ rootState, dispatch }, payload) {
    const urlInfo = URL.table[payload.key],
          urlPlaceholder = payload.urlPlaceholder || {}
    // 替换 url占位符
    urlInfo.hasPlaceholder && Object.keys(urlPlaceholder).forEach(key => {
        urlInfo.url = urlInfo.url.replace(new RegExp('\\' + key), urlPlaceholder[key])
    })
    
    return dispatch('sendAjaxByType', { info: Object.assign(urlInfo, payload) })
},
sendAjaxByType (content, { info }) {
	return Vue.api(info.type)(info.url, info.data, info.config)
}

调用

this.$store.dispatch('getData', {
    key: isAddMethod ? 'add' : 'edit', // 包含请求 URL和请求类型
    urlPlaceholder: {
        '$id': '123'
    },
    data: {
        a: 1,
        b: 2
    },
    config: {
        headers: {
            'Content-type': 'application/x-www-form-urlencoded'
        }
    }
}).then(d => {
    // do something...
})

6. 评价

采用策略模式成功实现了高内聚、低耦合,能够有效地避免多重条件判断语句,还支持开放-封闭原则,但是由于增加了策略对象,使用时必须了解各策略的不同之处,才能选择出合适的策略,所以提高了复杂度,不如简单的判断来的一眼明了。

上一篇:MYSQL分组函数


下一篇:[C#]快速将字符串转换为结构