写在前面:入门介绍,认识策略模式的含义
策略模式属于行为设计模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
该模式主要解决在有多种算法相似的情况下,使用if...else所带来的复杂和难以维护的代码。它的优点是算法可以自由切换,同时可以避免多重if...else判断,且具有良好的拓展性。
设计原理
核心思想:
- 将算法 / 业务逻辑 与其调用逻辑分离,把不同的算法封装成独立的策略类
- 使得算法可以独立于使用它的客户端变化,客户端可以动态切换不同的策略
- 避免大量的
if-else/switch分支判断,让代码更清晰、易扩展
核心角色:
- 策略接口 (Strategy):定义所有策略必须实现的方法
- 具体策略类 (ConcreteStrategy):实现策略接口,封装具体的算法 / 逻辑
- 上下文 (Context):持有策略对象的引用,提供统一的调用入口,负责策略的切换和执行
以下代码存在问题:if-else太多,管理混乱
示例一:不同年终奖的计算
```js
function calBonus(level,salary){
if(level=='S'){
return salary*4
}else if(level=='A'){
return salary*3
}else if(level=='B'){
return salary*2
}else{
return salary
}
}
console.log(calBonus('S',10000))
console.log(calBonus('A',8000))
```
使用策略模式进行改进:
```js
var strategies={
'S':function(salary){
return salary*4
},
'A':function(salary){
return salary*3
},
'B':function(salary){
return salary*2
}
//还可以继续拓展,比如C级别
}
function calBonus(level,salary){
return strategies[level](salary)
}
console.log(calBonus('S',10000))
console.log(calBonus('A',8000))
```
示例二:不同审批进度的定制化展示
html
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
li{
display: flex;
justify-content: space-between;
}
.redItem{
background: red;
}
.yellowItem{
background: yellow;
}
.greenItem{
background: green;
}
</style>
</head>
<body>
<ul id="myList"></ul>
<script>
var list = [
{
title: "标题1",
type: 1
},
{
title: "标题2",
type: 2
},
{
title: "标题3",
type: 3
},
{
title: "标题4",
type: 2
}
]
myList.innerHTML = list.map(item => {
if (item.type === 1) {
return `<li>
<div>${item.title}</div>
<div class="yellowItem">审核中</div>
</li>`
} else if (item.type === 2) {
return `<li>
<div>${item.title}</div>
<div class="greenItem">已通过</div>
</li>`
} else if (item.type === 3) {
return `<li>
<div>${item.title}</div>
<div class="redItem">被驳回</div>
</li>`
}
}).join("")
</script>
</body>
使用策略模式进行改进:
javascript
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
li{
display: flex;
justify-content: space-between;
}
.redItem{
background: red;
}
.yellowItem{
background: yellow;
}
.greenItem{
background: green;
}
</style>
</head>
<body>
<ul id="myList"></ul>
<script>
var list = [
{
title: "标题1",
type: 1
},
{
title: "标题2",
type: 2
},
{
title: "标题3",
type: 3
},
{
title: "标题4",
type: 2
}
]
//策略对象
let obj = {
1:{
content: "审核中",
className: "yellowItem"
},
2:{
content: "已通过",
className: "greenItem"
},
3:{
content: "被驳回",
className: "redItem"
}
}
let myList = document.getElementById("myList")
myList.innerHTML = list.map(item => `
<li>
<div>${item.title}</div>
<div class="${obj[item.type].className}">${obj[item.type].content}</div>
</li>`
).join("")
</script>
</body>
相关应用:node中路由的匹配策略,支付方式选择,数据的不同类型格式化
小结:要有一个策略对象。同一目标的不同实现方式,通过切换策略对象完成,替代 if-else 分支。复杂模块可以按照策略接口,具体策略类,上下文进一步拆分。
以node的路由为例:
- 策略接口 :
(req, res) => {}(所有路由处理函数的统一规范);- 具体策略 :
(req, res) => res.end('获取用户信息')(/user 的具体处理逻辑);- 上下文 :
router对象(持有策略、提供 match 方法统一执行)。三者缺一不可:接口保证 "可替换",具体策略封装 "差异化逻辑",上下文屏蔽 "调用细节",这也是策略模式能解耦、易扩展的核心原因。