由策略类和环境类组成。
-
策略类
封装了具体的算法,负责具体的计算过程
-
环境类Context
接受客户的请求,将请求委托给策略类
基于类的策略模式
Java like code
// 策略类
var PerformanceS = function () {};
PerformanceS.prototype.calculate = function (salary) {
return salary * 3;
};
PerformanceA = function () {};
PerformanceA.prototype.calculate = function (salary) {
return salary * 2;
};
PerformanceB = function () {};
PerformanceB.prototype.calculate = function (salary) {
return salary * 1;
};
// 环境类
var Bonus = function () {};
Bonus.prototype.setPerformance = function (performance) {
this.performance = performance;
};
Bonus.prototype.setSalary = function (salary) {
this.salary = salary;
};
Bonus.prototype.calculateBonus = function () {
return this.performance.calculate(this.salary);
};
var bonus = new Bonus();
bonus.setPerformance(new PerformanceS());
bonus.setSalary(1000);
console.log(bonus.calculateBonus()); // 3000
bonus.setPerformance(new PerformanceB());
console.log(bonus.calculateBonus()); // 1000
JavaScript的策略模式
js code
var performance = {
S: function (salary) {
return salary * 3;
},
A: function (salary) {
return salary * 2;
},
B: function (salary) {
return salary * 1;
}
};
var getBonus = function (level, salary) {
return performance[level](salary);
};
console.log(getBonus('S', 1000)); // 3000
console.log(getBonus('B', 1000)); // 1000
表单验证案例
<form id="regist" action="">
<label for="username">
用户名
<input type="text" id="username" placeholder="请输入用户名" />
</label>
<label for="password">
密码
<input type="password" id="password" placeholder="请输入密码" />
</label>
<label for="tel">
手机号
<input type="text" id="tel" placeholder="请输入手机号" />
</label>
<button type="submit">注册</button>
</form>
var telReg = /^1[3|5|7|8|9][0-9]{9}$/;
var strategies = {
isEmpty: function (value, errMsg) {
if (value === '') return errMsg;
},
minLength: function (value, length, errMsg) {
console.log('min');
if (value.length < length) return errMsg;
},
isMobile: function (value, errMsg) {
if (!telReg.test(value)) return errMsg;
}
};
var Validator = function () {
this.cache = [];
};
// add(form.input,minLength:6,不能少于6位数)
Validator.prototype.add = function (dom, rules) {
for (var i = 0, rule; (rule = rules[i++]); ) {
var self = this;
(function (rule) {
var args = rule.strategy.split(':');
var errMsg = rule.errMsg;
self.cache.push(function () {
var strategy = args.shift();
args.unshift(dom.value);
args.push(errMsg);
return strategies[strategy].apply(dom, args);
});
})(rule);
}
};
Validator.prototype.start = function () {
for (var i = 0, fn; (fn = this.cache[i++]); ) {
var errMsg = fn();
console.log(errMsg);
if (errMsg) {
return errMsg;
}
}
};
var form = document.getElementById('regist');
var validatFn = function () {
var validator = new Validator();
validator.add(form.username, [{ strategy: 'isEmpty', errMsg: '用户名不能为空' }]);
validator.add(form.password, [
{ strategy: 'minLength:6', errMsg: '密码不能少于6位数' },
{ strategy: 'isEmpty', errMsg: '密码不能为空' }
]);
validator.add(form.tel, [{ strategy: 'isMobile', errMsg: '手机号格式错误' }]);
return validator.start();
};
form.onsubmit = function () {
var ret = validatFn();
if (ret) {
alert(ret);
return;
}
};
总结
- ❏ 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
- ❏ 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的strategy中,使得它们易于切换,易于理解,易于扩展。
- ❏ 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
- ❏ 在策略模式中利用组合和委托来让Context拥有执行算法的能力,这也是继承的一种更轻便的替代方案。