您的位置:首页 > Web前端 > JavaScript

js之策略模式

2016-12-01 21:05 330 查看

策略模式

计算奖金

level01:最初的代码实现

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
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',2000);
calculateBonus('S',2000);

</script>
</body>
</html>


缺点:

calculateBonus函数比较庞大

calculateBonus缺乏弹性,如:再增加一个绩效等级C,或者想把绩效 S 的奖金系数改为 5,就必须必须深入calculateBonus

函数的内部实现,这是违反开放-封闭原则。

算法的复用性差其他地方需要重用这些计算奖金的算法只能通过复制粘贴。

level02:使用组合函数重构代码

把各种算法封装到一个个的小函数里面,这些小函数有着良好的命名,可以一目了然地知道它对应着哪种算法,它们也可以被复用在程序的其他地方。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var performanceS = function(){
return  salary*4;
}
var performanceA = function(){
return  salary*3;
}
var performanceB = function(){
return  salary*2;
}
var calculateBonus = function () {
if ( performanceLevel === 'S' ){
return performanceS( salary );//变成了函数
}
if ( performanceLevel === 'A' ){
return performanceA( salary );//变成了函数
}
if ( performanceLevel === 'B' ){
return performanceB( salary );//变成了函数
}
};
calculateBonus( 'A' , 10000 ); // 输出:30000
</script>
</body>
</html>


优点:

1. 把算法封装在函数里便于以后维护和服用

2. 一目了然它对应着哪种算法

level03:使用策略模式重构代码(模仿传统面向对象语言中的实现)

策略模式指的是:定义一系列的算法,把他们一个个封装起来。

将不变的部分和变化的部分隔开是每个设计模式的主题策略模式

也不例外,策略模式的目的就是将算法的使用算法的实现分离开来。

一个基于策略模式的程序至少由两部分组成:第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。 第二个部分是环境类 Context,Context接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明 Context中要维持对某个策略对象的引用。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
//定义一系列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。
var performanceS = function(){};
performanceS.prototype.calculate = function( salary ){
return salary * 4;
};
var performanceA = function(){};
performanceA.prototype.calculate = function( salary ){
return salary * 3;
};
var performanceB = function(){};
performanceB.prototype.calculate = function( salary ){
return salary * 2;
};
//环境类 Context :
var Bonus = function(){
this.salary = null; // 原始工资
this.strategy = null; // 绩效等级对应的策略对象
};
Bonus.prototype.setSalary = function( salary ){
this.salary = salary; // 设置员工的原始工资
};
//在客户对 Context发起请求的时候,Context总是把请求委托给这些策略对象中间的某一个进行计算。
Bonus.prototype.setStrategy = function( strategy ){
this.strategy = strategy; // 设置员工绩效等级对应的策略对象
};
Bonus.prototype.getBonus = function(){ // 取得奖金数额
return this.strategy.calculate( this.salary ); // 把计算奖金的操作委托给对应的策略对象
};

var bonus = new Bonus();
bonus.setSalary( 10000 );
bonus.setStrategy( new performanceS() ); // 设置策略对象
console.log( bonus.getBonus() ); // 输出:40000
bonus.setStrategy( new performanceA() ); // 设置策略对象
console.log( bonus.getBonus() ); // 输出:30000
</script>
</body>
</html>


优点:

代码变得更加清晰

各个类的职责更加鲜明

level04:JavaScript 版本的策略模式

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var strategies = {
"S": function( salary ){
return salary * 4;
},
"A": function( salary ){
return salary * 3;
},
"B": function( salary ){
return salary * 2;
}
};
var calculateBonus = function( level, salary ){
return strategies[ level ]( salary );
};
console.log( calculateBonus( 'S', 20000 ) ); // 输出:80000
console.log( calculateBonus( 'A', 10000 ) ); // 输出:30000
</script>
</body>
</html>


表单校验

level01:表单校验的第一个版本

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http:// xxx.com/register" 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>


缺点:

registerForm.onsubmit 函数比较庞大

registerForm.onsubmit 函数缺乏弹性如果增加了一种新的校验规则,或者想把密码的长度校验从 6改成 8,我们都必须深入 registerForm.onsubmit 函数的内部实现,这是违反开放 — 封闭原则的。

算法的复用性差,如果在程序中增加了另外一个表单,这个表单也需要进行一些类似的校验,那我们很可能将这些校验逻辑复制得漫天遍野。

level02:用策略模式重构表单校验

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--用策略模式重构表单校验-->
<body>
<form action="http:// xxx.com/register" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/ >
请输入密码:<input type="text" name="password"/ >
请输入手机号码:<input type="text" name="phoneNumber"/ >
<button>提交</button>
</form>
<script>
/*  下面我们将用策略模式来重构表单校验的代码,很显然第一步我们要把这些校验逻辑都封装
成策略对象:*/
var 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;
}
}
};
// Validator 类在这里作为 Context:负责接收用户的请求并委托给 strategy 对象
var validataFunc = function(){//这个函数可以不要,把里面代码直接放到onsubmit函数里面
var validator = new Validator(); // 创建一个 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(); // 如果 errorMsg 有确切的返回值,说明未通过校验
if ( errorMsg ){
alert ( errorMsg );
return false; // 阻止表单提交
}
}

var Validator = function(){
this.cache = []; // 保存校验规则(把定义效验规则的函数存入数组,,之后循环执行)
};
Validator.prototype.add = function( dom, rule, errorMsg ){
var ary = rule.split( ':' ); // 把 strategy 和参数分开
this.cache.push(function(){ // 把校验的步骤用空函数包装起来,并且放入 cache
var strategy = ary.shift(); // 用于在strategies中选择对应的策略
ary.unshift( dom.value ); // 把 input 的 value 添加进参数列表 策略函数的参数
ary.push( errorMsg ); // 把 errorMsg 添加进参数列表 策略函数的参数
//ary很巧妙的成了策略函数的参数列表
return strategies[ strategy ].apply( dom, ary );
});
};
Validator.prototype.start = function(){
for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息
if ( msg ){ // 如果有确切的返回值,说明校验没有通过(就不用再执行下去了)
return msg;
}
}
};
</script>
</body>
</html>


level03:给某个文本输入框添加多种校验规则

和level02相比只是函数add和参数形式不同。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--用策略模式重构表单校验-->
<body>
<form action="http:// xxx.com/register" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/ >
请输入密码:<input type="text" name="password"/ >
请输入手机号码:<input type="text" name="phoneNumber"/ >
<button>提交</button>
</form>
<script>
/***********************策略对象**************************/
var 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;
}
}
};
/***********************Validator 类**************************/
var Validator = function(){
this.cache = [];
};
Validator.prototype.add = function( dom, rules ){
var self = this;//因为这里的this和内嵌函数的this不一样
for ( var i = 0, rule; rule = rules[ i++ ]; ){
(function( rule ){
var ary = rule.strategy.split( ':' );
var errorMsg = rule.errorMsg;
self.cache.push(function(){
var strategy = ary.shift();
ary.unshift( dom.value );
ary.push( errorMsg );
return strategies[ strategy ].apply( dom, ary );
});
})( rule )
}
};
Validator.prototype.start = function(){
for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
var errorMsg = validatorFunc();
if ( errorMsg ){
return errorMsg;
}
}
};
/***********************客户调用代码**************************/
var registerForm = document.getElementById( 'registerForm' );
var validataFunc = function(){
var validator = new Validator();
validator.add( registerForm.userName, [{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空'
}, {
strategy: 'minLength:6',
errorMsg: '用户名长度不能小于 10 位'
}]);
validator.add( registerForm.password, [{
strategy: 'minLength:6',
errorMsg: '密码长度不能小于 6 位'
}]);
validator.add( registerForm.phoneNumber, [{
strategy: 'isMobile',
errorMsg: '手机号码格式不正确'
}]);
var errorMsg = validator.start();
return errorMsg;
}
registerForm.onsubmit = function(){
var errorMsg = validataFunc();
if ( errorMsg ){
alert ( errorMsg );
return false;
}
};
</script>
</body>
</html>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: