js设计模式——策略模式

策略模式:
定义一系列算法,把它们一个个封装起来,并且使它们可以相互替代。

这段话读了之后可能还不是很理解策略模式的意思。可能需要一些实际例子来让自己对策略模式更加了解。

计算奖金的例子

这是计算奖金的例子,要求通过不同的绩效考核等级(A,B,C)和员工的工资来计算奖金:

var calculateBonus = function( performanceLevel, salary ){ 
 
  if ( performanceLevel === 'A' ){ 
  return salary * 3; 
  } 
  if ( performanceLevel === 'B' ){ 
  return salary * 2; 
  } 
  if ( performanceLevel === 'C' ){ 
  return salary * 1; 
  } 
}; 
calculateBonus( 'B', 20000 ); // 输出:40000 
calculateBonus( 'C', 6000 ); // 输出:6000

这段代码很简单,但是缺点也很明显:

  • 代码冗余。包含了大量if-else语句。
  • 缺乏弹性。如果要新增一种绩效,比如S等级,那无疑要深入到calculateBonus函数内部去修改代码,这违背了“开放-封闭原则”

开放-封闭原则:软件实体是可以扩展的,而不可修改的。即对扩展是开放的,对修改是封闭的

  • 代码复用性差。别的地方要使用这个功能,只能粘贴复制这段代码。

使用策略模式来重构

我们使用策略模式来重构这段代码。

实现策略模式的方法:将代码抽离出不变的部分和变化的部分,并相互组合。

策略模式的目的就是将算法的使用和算法的实现分离开来。

一个策略模式程序至少由两部分组成,第一个部分是策略类(strategy),另一个是环境类Context。环境类接收客户请求,然后将请求委托一个策略类。

  • 不变的部分,算法的使用,环境类Context,一个
  • 变化的部分,算法的实现,策略类Strategy,多个

核心代码

	var strategy={
		"A":function(salary){
			return salary*3;
		},
		"B":function(salary){
			return salary*2;
		},
		"C":function(salary){
			return salary*1;
		}
	}

	function calculateBonus(level,salary){
		return strategy[level](salary);
	}

策略模式:定义一些列算法(策略类strategy中有A,B,C这些策略),把它们一个个封装起来(A,B,C策略的实现都封装在了函数中),并且他们可以相互替换(在calculateBonus函数中,A,B,C三个策略的地位相等,可以替换)

现在看策略模式是不是解决了前面的简单实现代码的问题呢?

  • 去除了if-else语句,calculateBonus函数中只有很清爽的一句代码,且良好的命名使这段代码的意思一目了然,可读性也很好。
  • 可扩展。如果我们增加了一个S等级,那么无需深入calculateBonus函数,只需在strategy里面添加上S等级的算法实现即可。这符合开放-封闭原则。
  • 复用性。这个例子比较简单,这里可能体现不出来复用性。我们下个表单校验的例子来看。

运行代码

<!DOCTYPE html>
<html>
<head>
	<title>策略模式</title>
</head>
<body>
	原始薪资:
<input type="text" name="salary" id="myInput">
	绩效:
<select id="mySelect">
	<option value="A">A</option>
	<option value="B">B</option>
	<option value="C">C</option>
</select>
<button id="myBtn">计算奖金</button>
<div id="myDiv"></div>
</body>
<script type="text/javascript">
	
	var strategy={
		"A":function(salary){
			return salary*3;
		},
		"B":function(salary){
			return salary*2;
		},
		"C":function(salary){
			return salary*1;
		}
	}

	function calculateBonus(level,salary){
		return strategy[level](salary);
	}

	myBtn.onclick=function(){
		var myInputValue=parseInt(document.getElementById('myInput').value);
		var mySelect=document.getElementById('mySelect');
		var myBtn=document.getElementById('myBtn');
		var myDiv=document.getElementById('myDiv');

		var mySelectIndex=parseInt(mySelect.selectedIndex);
		var mySelectValue=mySelect.options[mySelectIndex].value;

		myDiv.innerHTML=calculateBonus(mySelectValue,myInputValue);
	}
</script>
</html>

表单校验

表单校验是很常用的,而且有时候也会比较繁琐。这里就直接给出代码。可粘贴复制后运行,测试一下。

<!DOCTYPE html>
<html>
<head>
	<title>策略模式--表单验证</title>
</head>
<body>
	<form id="myForm"  method="post">
		用户名:<input type="text" name="myName">
		密码:<input type="password" name="myPassword">
		电话号码:<input type="text" name="myPhoto">
		<button>提交</button>
	</form>

</body>
	<script type="text/javascript">
		var myForm=document.getElementById('myForm');
		//策略,将变化的元素集中起来
		var strategies={
			isNoEmpty:function(value,errorMsg){
				if(value==='')
				{
					return errorMsg;
				}
			},
			minLength:function(value,length,errorMsg){
				if(value.length<parseInt(length)){
					return errorMsg;
				}
			},
			reg:function(value,reg,errorMsg){
				var regExp = eval(reg)
				if(!regExp.test(value)){
					return errorMsg;
				}
			}
		}

		var Vaildator=function(){
			this.cache=[];//校验规则列表
		}

        //添加校验规则
		Vaildator.prototype.add=function(dom,rules,errorMsg){
			var ary=rules.split(":");//rules='minLength:6'=>ary=["minLength","6"],ary就是策略算法中的参数
			this.cache.push(function(){
				var strategy=ary.shift();//获取策略,比如minLength,此时ary=["6"]
				ary.unshift(dom.value);//加入value值,此时ary=["xxxx","6"]
				ary.push(errorMsg);//加入错误提示信息
				return strategies[strategy].apply(dom,ary);
			})

		}
        //启动校验
		Vaildator.prototype.start=function(){
			for(let i=0;i<this.cache.length;i++){
				let vaildatorFun=this.cache[i];
				var msg=vaildatorFun();
				if(msg){
					return msg;
				}
			}
		}

		function vaildatorFun(){
			var vaildator=new Vaildator();
			vaildator.add(myForm.myName,'isNoEmpty',"用户名不能为空");
			vaildator.add(myForm.myPassword,'minLength:10',"密码长度要大于10位");
			vaildator.add(myForm.myPhoto,'reg:/^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$/',"手机号格式不正确");
			var errorMsg=vaildator.start();
			return errorMsg;
		}

		myForm.onsubmit=function(){
			var errorMsg=vaildatorFun();
			if(errorMsg){
				alert(errorMsg);
			}else{
				alert("验证通过")
			}
			return false
		}
	</script>

</html> 

这段代码中策略放在strategies中,环境是Vaildator,我们只需进行vaildator.add()就可以添加验证规则了。

我们可以将这种表单校验代码封装起来,暴露必须的接口,就可以做成插件或者js文件。复用性也就体现出来了。

表单多种校验规则

这里再进一步添加多种校验规则。

<!DOCTYPE html>
<html>
<head>
	<title>策略模式--表单验证</title>
</head>
<body>
	<form id="myForm"  method="post">
		用户名:<input type="text" name="myName">
		密码:<input type="password" name="myPassword">
		电话号码:<input type="text" name="myPhoto">
		<button>提交</button>
	</form>

</body>
	<script type="text/javascript">
		var myForm=document.getElementById('myForm');
		//策略,将变化的元素集中起来
		var strategies={
			isNoEmpty:function(value,errorMsg){
				if(value==='')
				{
					return errorMsg;
				}
			},
			minLength:function(value,length,errorMsg){
				if(value.length<parseInt(length)){
					return errorMsg;
				}
			},
			reg:function(value,reg,errorMsg){
				var regExp = eval(reg)
				if(!regExp.test(value)){
					return errorMsg;
				}
			}
		}

		var Vaildator=function(){
			this.cache=[];//校验规则列表
		}

		Vaildator.prototype.add=function(dom,rules,errorMsg){
			var that=this;
			for (var i = 0,rule; i <rules.length,rule=rules[i]; i++) {
				//闭包,this指向全局
				(function(rule){
					var strategyAry=rule.strategy.split(':');
					var errorMsg=rule.errorMsg;
					that.cache.push(function(){
						var strategy=strategyAry.shift();
						strategyAry.unshift(dom.value);
						strategyAry.push(errorMsg);
						return strategies[strategy].apply(dom,strategyAry);
					})

				})(rule)
			}

		}

		Vaildator.prototype.start=function(){
			for(let i=0;i<this.cache.length;i++){
				let vaildatorFun=this.cache[i];
				var msg=vaildatorFun();
				if(msg){
					return msg;
				}
			}
		}

		function vaildatorFun(){
			var vaildator=new Vaildator();
			vaildator.add(myForm.myName,[{
				strategy:"isNoEmpty",
				errorMsg:"用户名不能为空"
			},{
				strategy:"minLength:3",
				errorMsg:"用户名长度不小于3"
			}]);
			vaildator.add(myForm.myPassword,[{
				strategy:'minLength:10',
				errorMsg:"密码长度要大于10位"
			}]);
			vaildator.add(myForm.myPhoto,[{
				strategy:'reg:/^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$/',
				errorMsg:"手机号格式不正确"
			}]);
			var errorMsg=vaildator.start();
			return errorMsg;
		}

		myForm.onsubmit=function(){
			var errorMsg=vaildatorFun();
			if(errorMsg){
				alert(errorMsg);
			}else{
				alert("验证通过")
			}
			return false
		}
	</script>

</html>

参考:javascript设计模式与开发

记录下来用于自己回顾

posted @ 2020-09-10 10:55  ellenxx  阅读(316)  评论(0编辑  收藏  举报