JavaScript设计模式行为型设计模式--策略模式
Posted Wendy-lxq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript设计模式行为型设计模式--策略模式相关的知识,希望对你有一定的参考价值。
俗话说“条条大路通罗马”。在现实中,很多时候也有多种途径可以到达同一个目的地,比如,我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路。在程序设计中,我们也常常遇到类似的情况,要实现某一个功能有多种方案可以选择。比如要对一个数组进行排序,我们可以选择快速排序算法,也可以选择冒泡排序算法。
策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。当然这些算法灵活多样,而且可以随意相互替换。这种方案呢,就是本文将要介绍的策略模式。
贴上策略模式的官方定义:
策略模式指的是定义一些列的算法,把他们一个个封装起来,并且使他们可以相互替换。目的就是将算法的使用与算法的实现分离开来。
从定义上看,策略模式就是用来封装算法的。但如果把策略模式仅仅用来封装算法,未免有点大材小用。在实际开发中,我们通常会把算法的含义扩散开来,使策略模式也可以用来封装一系列的“业务规则”。只要这些业务规则指向的目标一致,并且可以被替换使用,我们就可以使用策略模式来封装他们。
接下来想用一个表单校验的算法进行说明这个模式。
在一个Web项目中,注册、登录、修改用户信息等功能的实现都离不开提交表单。
假设我们正在编写一个注册的页面,在点击注册之前,有如下几条校验逻辑。
- 用户名不能为空;
- 密码长度不能少于6位;
- 手机号码必须符合格式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>register</title>
</head>
<body>
<form id="registerForm" method="post" action="http://###.com/register">
<label for="userName">请输入用户名:</label>
<input type="text" name="userName" id="userName">
<label for="pwd">请输入密码:</label>
<input type="text" name="password" id="pwd">
<label for="phoneNumber">请输入密码:</label>
<input type="text" name="phoneNumber" id="phoneNumber">
<input type="button" value="提交">
</form>
<script>
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function()
if( registerForm.userName.value === '')
alert("用户名不能为空!");
return false;
if( registerForm.pwd.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>
解析:
1) registerForm.onsubmit 函数比较庞大,包含了很多if语句,这些语句需要覆盖所有的校验规则;
2) registerForm.onsubmit 函数缺乏弹性,如果增加了一个新的校验规则,或者想把密码的长度校验从6变成8,我们都必须深入这个函数的内部实现,这违反了开放-封闭原则;
3)算法的复用性差,如果程序中增加另外一个表单,比如登录,这个表单也需要进行一些类似的校验,那么我们很可能只是复制一下这段代码,然后相对应的进行修改,造成的后果就是相似的代码到处都有,不利于代码的维护与美观。
那么,为了解决以上出现的这些问题,我们可以采用策略模式重构这个表单校验。 2、用策略模式重构表单校验 在重构代码的第一步就是将这些校验进行封装成策略对象。
//利用策略模式
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类,负责接收用户的请求并委托给strategy对象。在实现Validator类的代码之前,我们先了解下用户是如何向Validator类发送请求的。
var validataFunc = function ()
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();
if(errorMsg)
alert(errorMsg);
return false; //阻止表单提交
;
解析:
- 我们首先创建了一个 Validator对象,然后通过validator.add()方法往对象中添加一些校验规则,这个方法接受3个参数。
- validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空'); 其中registerForm.userName为参与校验的input输入框; 'isNonEmpty'为名字的校验参数;'用户名不能为空'是当校验未通过时返回的错误信息。
var Validator = function ()
this.cache = [];
;
Validator.prototype.add = function (dom, rule, errorMsg)
var ary = rule.split(':');
this.cache.push(function ()
var strategy = ary.shift();
ary.unshift(dom.value);
ary.push(errorMsg);
return strategies[strategy].apply(dom,ary);
);
;
Validator.prototype.start = function ()
for( var i = 0,validataFunc;validataFunc = this.cache[ i++]; )
var msg = validataFunc();
if(msg)
return msg;
;
使用策略模式重构代码之后,我们仅仅通过“配置”的方式就可以完成一个表达的校验,而且这些校验规则也可以复用在程序中的其他需要校验的地方。在修改某个校验规则的时候,只需要编写或者该写少量的代码。 3、总结 策略模式定义了一系列算法,从概念上来说,所有的这些算法都是做相同的事情,只是实现不同,他可以以相同的方式调用所有的方法,减少了各种算法类与使用算法类之间的耦合。
从另外一个层面上来说,单独定义算法类,也方便了单元测试,因为可以通过自己的算法进行单独测试。
实践中,不仅可以封装算法,也可以用来封装几乎任何类型的规则,是要在分析过程中需要在不同时间应用不同的业务规则,就可以考虑是要策略模式来处理各种变化。
优点:
- 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句;
- 策略模式提供了对开放-封闭原则的完美支持,将算法进行封装,使得它们易于切换,易于理解和易于扩展;
- 策略模式的代码可以复用在代码的其他地方,从而避免许多重复的复制黏贴工作,也使得代码更简洁。
以上是关于JavaScript设计模式行为型设计模式--策略模式的主要内容,如果未能解决你的问题,请参考以下文章