定义
定义一系列的算法,把它们一个个封装起来,并可以随意的相互替换。 策略模式的目的就是将算法的使用与算法的实现分离开来(将各种算法的代码、 内部数据和依赖关系与其他代码隔离开来)。
优缺点
优点:
- 优化一些多重条件的逻辑判断代码
开放封闭原则,易于切换算法,易于扩展- 将不同行为抽取到一个独立类层次结构中,并将原始类组合成同一个,从而减少重复代码,易于复用
缺点:
- 程序中会有较多策略类
适用范围
- 需要了解算法的不同点,才可以编写合适的策略类(即算法规则)
- 算法的使用与算法的实现分离开,也就是说,算法的上下文逻辑不是特别重要,这样才有分离的意义
策略模式的组成
一个策略模式至少由两部分组成。
- 第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。
- 第二个部分是环境类 Context,Context 接受客户的请求,随后把请求委托给某一个策略类。
示例一:优化表单校验代码
需求背景
假设一个表单提交时,需要验证一下信息:
- 用户名不能为空
- 密码长度不能少于6位
- 手机号码必须符合格式
常规写法
const form = document.getElementById('registerForm');
form.onsubmit = function () {
if (form.userName.value === '') {
alert('用户名不能为空');
return false;
}
if (form.password.value.length < 6) {
alert('密码长度不能少于6位');
return false;
}
if (!/^1[3|5|8][0-9]{9}$/.test(form.phoneNumber.value)) {
alert('手机号码格式不正确');
return false;
}
}
缺点:
if-else语句太多,onsubmit函数庞大- 违反
开放-封闭原则:需要新增一个校验规则时,需要修改onsubmit函数内部
使用策略模式
// step1:编写策略类
const strategies = {
isNonEmpty: function (value, errMsg) {
if (value === '') {
return errMsg;
}
},
minLenth: function (value, length, errMsg) {
if (value.length < length) {
return errMsg;
}
},
isMobile: function (value, errMsg) {
if (!/^1[3|5|8][0-9]{9}$/.test(value)) {
return errMsg;
}
}
}
// step2:定义环境类 Context
class Validator {
constructor() {
this.cache = [];
}
add(dom, rule, errMsg) {
const arr = rule.split(':');
this.cache.push(() => {
const strategy = arr.shift();
arr.unshift(dom.value);
arr.push(errMsg);
return strategies[strategy].apply(dom, arr);
})
}
start() {
for (let i = 0; i < this.cache.length; i++) {
const msg = this.cache[i]();
if (msg) return msg;
}
}
}
// step3:调用策略
form.onsubmit = function () {
const validator = new Validator();
validator.add(form.userName, 'isNonEmpty', '用户名不能为空');
validator.add(form.password, 'minLength:6', '密码长度不能少于6位');
validator.add(form.phoneNumber, 'isMobile', '手机号码格式不正确');
const errMsg = validator.start();
if (errMsg) {
alert(errMsg);
return false;
}
}