设计模式 - 策略模式

Posted by luoxiao on 2021-07-13

什么是策略模式?

策略模式定义了一系列算法,从概念上来说,所有的这些算法都是做相同的事情,只是实现不同,他可以以相同的方式调用所有的方法,减少了各种算法类与使用算法类之间的耦合。实践中,不仅可以封装算法,也可以用来封装几乎任何类型的规则,是要在分析过程中需要在不同时间应用不同的业务规则,就可以考虑是要策略模式来处理各种变化。

优势

  • 策略模式利用组合/委托和多态等技术和思想,可以有效的避免多重条件选择语句;
  • 策略模式提供了对开放-封闭原则的完美支持,将算法封装中独立的策略类中,使得它们易于切换/理解/扩展;
  • 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻便的代替方案。

实现方式

从代码中分离不变的算法策略,将其进行封装,以适用于各类大致相同的场景中使用,具备独立性,甚至可以封装为插件能够被移植,最终算法的使用和实现分离。

适用场景

消除if-else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var calculateBonus = function(performanceLevel, salary) {
if (performanceLevel === 'S') {
return salary * 4;
}
if (performanceLevel === 'A') {
return salary * 3;
}
if (performanceLevel === 'B') {
return salary * 2;
}
};

calculateBonus('B', 20000); // 输出:40000
calculateBonus( 'S', 6000 ); // 输出:24000

// 改良后:

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

const calculateBonus = (level, salary) => {
return strtegies[level](salary "level")
}

console.log(calculateBonus('s', 20000))
console.log(calculateBonus('a', 10000))
  • 策略类 strategies 封装了具体的算法和计算过程
  • 环境类 calculateBonus 接受请求,把请求委托给策略类 strategies
  • 将算法的使用和算法的实现分离,代码清晰,职责分明;
  • 消除大量的 if-else 语句。

表单校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// 表单dom
const registerForm = document.getElementById('registerForm')

// 表单规则
const rules = {
userName: [
{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空'
},
{
strategy: 'minLength:10',
errorMsg: '用户名长度不能小于10位'
}
],
password: [
{
strategy: 'minLength:6',
errorMsg: '密码长度不能小于6位'
}
],
phoneNumber: [
{
strategy: 'isMobile',
errorMsg: '手机号码格式不正确'
}
]
}

// 策略类
var strategies = {
isNonEmpty: function(value, errorMsg) {
if (value === '') {
return errorMsg;
}
},
minLength: function(value, errorMsg, length) {
console.log(length)
if (value.length < length) {
return errorMsg;
}
},
isMobile: function(value, errorMsg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
};

// 验证类
const Validator = function () {
this.cache = []
}

// 添加验证方法
Validator.prototype.add = function ({ dom, rules}) {
rules.forEach(rule => {
const { strategy, errorMsg } = rule
console.log(rule)
const [ strategyName, strategyCondition ] = strategy.split(':')
console.log(strategyName)
const { value } = dom
this.cache.push(strategies[strategyName].bind(dom, value, errorMsg, strategyCondition))
})
}

// 开始验证
Validator.prototype.start = function () {
let errorMsg
this.cache.some(cacheItem => {
const _errorMsg = cacheItem()
if (_errorMsg) {
errorMsg = _errorMsg
return true
} else {
return false
}
})

return errorMsg
}

// 验证函数
const validatorFn = () => {
const validator = new Validator()
console.log(validator.add)

Object.keys(rules).forEach(key => {
console.log(2222222, rules[key])
validator.add({
dom: registerForm[key],
rules: rules[key]
})
})

const errorMsg = validator.start()
return errorMsg
}


// 表单提交
registerForm.onsubmit = () => {
const errorMsg = validatorFn()
if (errorMsg) {
alert(errorMsg)
return false
}
return false
}

添加策略

已有的策略即使再多,有时候也是不能满足其他的需求的,为了增强扩展性,我们可以增加一个为策略对象添加策略算法的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var InputStrategy = function() {
var strategy = {
notNull: function(value) {},
phone: function(value) {},
}
return {
check: function(type, value) {},
addStrategy: function(type, fn) {
strategy[type] = fn
}
}
}()

InputStrategy.addStrategy('email', (value) => {
})