javascript 设计模式之单例模式javascript
javascript 适配、代理、装饰者模式的比较github
javascript 设计模式之观察者模式markdown
策略模式:定义一系列的算法(这些算法目标一致),把它们一个个封装起来,而且使它们能够相互替换。
每家公司年终奖的发放都会根据该年度员工表现给予必定的奖惩,固然 A 公司也不例外, A 公司的年终绩效制度以下:
代码实现以下:
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel == 'S') {
return salary * 4
}
if (performanceLevel == 'A') {
return salary * 3
}
if (performanceLevel == 'B') {
return salary * 2
}
if (performanceLevel == 'C') {
return salary * 0.3
}
}
console.info(calculateBonus('S', 20000)) // 80000
console.info(calculateBonus('C', 15000)) // 4500
复制代码
功能上能够正常使用,但存在以下几个缺点:
让咱们先来解决第三点算法复用性差问题。 把各类算法(即年终奖的计算规则)封装到一个个独立的小函数中。 代码改为以下:
var performanceS = function (salary) {
return salary * 4
}
var performanceA = function (salary) {
return salary * 3
}
var performanceB = function (salary) {
return salary * 2
}
var performanceC = function (salary) {
return salary * 0.3
}
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel == 'S') {
return performanceS(salary)
}
if (performanceLevel == 'A') {
return performanceA(salary)
}
if (performanceLevel == 'B') {
return performanceB(salary)
}
if (performanceLevel == 'C') {
return performanceC(salary)
}
}
console.info(calculateBonus('S', 20000)) // 80000
console.info(calculateBonus('C', 15000)) // 4500
复制代码
采用组合函数,将每一个绩效算法抽离成单独的函数,是解决了复用问题,若是有别的地方要计算 S 等级的薪资,直接调用 performanceS 函数便可。 但上面一、2两个问题仍然存在,咱们继续优化,引出主角策略模式
设计模式中很重要的一点就是将不变的部分和变化的部分分离出来,而策略模式的目的就是将算法的使用和算法的实现分离开来。
在这个例子中,算法的使用方式是不变的,都是根据等级和薪资计算年终奖;算法的实现是变化的,好比 S 有 S 等级的计算方式, A 有 A 等级的计算方式。
策略模式的组成:
- 一组策略类,策略类封装了具体的算法,并负责具体的计算过程。
- 环境类Context,Context接收客户的请求,随后把请求委托给某一个策略类。
定义策略类:
//策略类(S)
class performanceS {
calculate(salary) {
return salary * 4;
}
}
//策略类(A)
class performanceA {
calculate(salary) {
return salary * 3;
}
}
//策略类(B)
class performanceB {
calculate(salary) {
return salary * 2;
}
}
//策略类(C)
class performanceC {
calculate(salary) {
return salary * 0.3;
}
}
复制代码
定义环境类:
// 环境类
class Bonus {
constructor() {
this.salary = null; //原始工资
this.strategy = null; //绩效公司对应的策略对象
}
setSalary(salary) {
this.salary = salary; //设置原始工资
}
setStrategy(strategy) {
this.strategy = strategy; //设置员工绩效等级对应的策略对象
}
getBonus() {//取得奖金数额
//维持对策略对象的引用
//委托给对应的策略对象
return this.strategy.calculate( this.salary );
}
}
复制代码
验证:
const bonus = new Bonus()
bonus.setSalary( 20000 );
bonus.setStrategy( new performanceS() ); //设置策略对象
console.info(bonus.getBonus()) // 80000
复制代码
上述展现了策略模式的应用,代码比较清晰,解决了上述的几大问题,要再增长个绩效 D 的逻辑,不会动到 Bonus 类,只要再定义个 performanceD 策略类便可,在 Bonus 类里作的事情也很单一,负责接收策略类实例,并调用。
上述展现的策略模式是基于传统的面向对象语言,能够进一步对这段代码进行优化,变成JavaScript版本的策略模式。
在 JS 中,函数也是对象,因此能够将策略类直接定义为函数,并以对象映射形式展现。
//策略对象
var strategies = {
//一系列算法
"S": function ( salary ) {
return salary * 4;
},
"A": function ( salary ) {
return salary * 3;
},
"B": function ( salary ) {
return salary * 2;
},
"C": function (salary) {
return salary * 0.3;
},
};
复制代码
同时,也能够将环境类定义为函数,改为以下:
var calculateBonus = function ( level, salary) {
return strategies[ level ]( salary );
};
复制代码
验证下:
console.log( calculateBonus('S', 20000)); // 80000
复制代码
再来看下策略模式的定义
定义一系列的算法(这些算法目标一致),把它们一个个封装起来,而且使它们能够相互替换。
策略模式指的是定义一系列的算法,而且把它们封装起来,可是策略模式不只仅只封装算法,咱们还能够用来封装一系列的业务规则,只要这些业务规则目标一致,咱们就可使用策略模式来封装它们;好比表单验证
需求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表单验证</title>
</head>
<body>
<form action='xxx.com' id='registerForm' method='post'>
请输入用户名:<input type='text' name='userName'/ >
请输入密码:<input type='text' name='password'/ >
请输入手机号码:<input type='text' name='phoneNumber'/ >
<button>提交</button>
</form>
<script>
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function () {
if ( registerForm.userName.value === '') {
alert('用户名不能为空');
return false;
}
if (registerForm.password.value.length < 6) {
alert('密码长度不能小于6位');
return false;
}
if (!(/^1[3|5|8][0-9]{9}$/.test(registerForm.phoneNumber.value))) {
alert('手机号码格式不正确');
return false;
}
}
</script>
</body>
</html>
复制代码
跟上述绩效奖金,存在同样的问题,函数过于庞大、缺少弹性以及复用性差,下面采用策略模式优化
采用策略模式,首先要定义策略类,那策略类要先找到算法具体是指什么:表单验证逻辑的业务规则
因此定义策略类以下:
// 定义策略类
const strategies = {
isNonEmpty: function ( value, errorMsg) {
if ( value === '') {
return errorMsg;
}
},
minLength: function ( value, length, errorMsg ) {
if ( value.length < length ) {
return errorMsg
}
},
isMobile: function ( value, errorMsg) {
if ( !/(^1[3|5|8][0-9]{9}$)/.test( value )) {
return errorMsg;
}
}
}
复制代码
接着要定义环境类,在定义环境类以前,先看下客户通常是怎么使用的?
var validataFunc = function () {
//建立一个validator对象
var validator = new Validator();
//添加校验规则
validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空');
validator.add( registerForm.password, 'minLength:6', '密码长度不能少于6位');
validator.add( registerForm.phoneNumber, 'isMobile', '手机号码格式不正确');
var errorMsg = validator.start();
//返回校验结果
return errorMsg;
}
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function () {
var errorMsg = validataFunc(); //若是存在,则说明未经过校验
if ( errorMsg ) {
alert( errorMsg );
return false; //阻止表单提交
}
}
复制代码
从上述代码中,能够明确在环境类 Validator 中要有 add 方法,经过 add 方法来添加校验规则。
同时有 start 方法,经过 start 方法开始校验,若是有错误,那么就返回错误信息( errorMsg )
有了策略对象以及策略对象与环境类( Validator )的桥梁,咱们即可以写出 Validator 类代码
class Validator {
constructor() {
this.cache = []; //保存校验规则
}
//添加检验规则函数
add(dom,rule,errorMsg){
//把strategy和参数分开'minLength:6' 如'minLength:6' -> ["minLength", "6"]
let ary = rule.split(':');
this.cache.push ( function () {
//用户挑选的strategy ["minLength", "6"] -> 'minLength'
let strategy = ary.shift();
//把input的value添加进参数列表
ary.unshift( dom.value );
//把errorMsg添加进参数列表
ary.push( errorMsg );
//委托策略对象调用
return strategies[ strategy ].apply( dom, ary );
})
}
start(){
for ( var i = 0,validatorFunc; validatorFunc = this.cache[i++];) {
var msg = validatorFunc(); //开始校验,并取得校验后的返回信息
if ( msg ) { //若是msg存在,则说明校验不经过
return msg;
}
}
}
}
复制代码
在上述中,经过对业务规则这种算法的抽象,经过策略模式来完成表单检验,在修改某个校验规则的时候,咱们只有修改少许代码便可。好比想把用户名的输入改为不能少于4个字符,只须要把minLength:6改成minLength:4便可
目前实现的表单校验有一点小问题:一个文本输入框只能对应一种校验规则。 若是想要添加多种检验规则,能够经过如下方式添加:
validator.add( registerForm.userName, [{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空'
},{
strategy: 'minLength:10',
errorMsg: '用户名长度不能小于10位'
}])
复制代码
要修改 Validator 中的 add 方法,经过遍历的方式,把多个检验规则添加到cache中。
add (dom, rules) {
let self = this;
for (let i = 0,rule; rule = rules[i++];) {
(function ( rule ) {
let strategyAry = rule.strategy.split( ':' );
let errorMsg = rule.errorMsg;
self.cache.push( function () {
let strategy = strategyAry.shift();
strategyAry.unshift( dom.value );
strategyAry.push( errorMsg );
return strategies[ strategy ].apply( dom, strategyAry )
})
})(rule)
}
}
复制代码
优势:
缺点:
使用场景:
你的点赞是对我最大的确定,若是以为有帮助,请留下你的赞扬,谢谢!!!