javascript设计模式二: 策略模式

策略模式指定义一系列算法,将它们一个个封装起来。将不变的部分与变化的部分隔开是每个设计模式的主题,策略模式同样如此,策略模式的目的就是将算法的使用与算法的实现分离开来。

策略模式的基础组成: 一个基于策略模式的程序至少要由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,Context接收。

客户的请求,随后把请求委托给某一个策略类(发送消息给某个策略对象),要做到这点,说明Context中要维持对某个策略对象的引用。

简易表单校验示例DEMO:

  1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<meta http-equiv="X-UA-Compatible" content="ie=edge">
7<title>Document</title>
8<style>
9    .error {
10        color#ff0000;
11        font-size12px;
12    }
13
14
</style>
15</head>
16<body>
17<form action="http://www.x111xx.com/register" method="post" id="registerForm">
18    <label>
19        <input type="text" name="username" >
20    </label>
21    <label>
22        <input type="password" name="password" >
23    </label>
24    <label>
25        <input type="text" name="phoneNumber">
26    </label>
27    <button>提交</button>
28</form>
29</body>
30<script>
31
32//为元素同时设置多个属性方法
33var setAttributes = function(el, attrs){
34    for(var key in attrs){
35        el.setAttribute(key, attrs[key])
36    }
37}
38
39//策略类
40var strategiesObj = {
41    isNonEmptyfunction(name, value, errorMsg){
42        if(value === ''){
43            var errObj = {errName: name, errValue: value, errMsg: errorMsg}
44            return errObj
45        }
46    },
47    minLengthfunction(name, value, length, errorMsg){
48        if(value.length < length){
49            var errObj = {errName: name, errValue: value, errLength: length, errMsg: errorMsg}
50            return errObj
51        }
52    },
53    isMobilefunction(name, value, errorMsg){
54        if(!/^1[3|4|5|7|8][0-9]{9}$/.test(value)){
55            var errObj = {errName: name, errValue: value, errMsg: errorMsg}
56            return errObj
57        }
58    }
59}
60
61//Context类
62var Validator = function(){
63    this.cache = [];     //保存校验规则
64    this.errArr = [];
65}
66
67Validator.prototype.add = function(name, dom, rules){
68    var self = this;
69    for(var i=0, rule; rule=rules[i++];){
70        (function(rule){
71            var strategyAry = rule.strategy.split(':');   //把strategy和rule分隔开
72            self.cache.push(function(){     //把校验的步骤用空函数包裹起来,并且放入cache中,
73                var strategy = strategyAry.shift(strategyAry);   //用户选择的strategy   shift()方法是删除数组第一个元素,并返回第一个元素的值
74                strategyAry.unshift(name)
75                strategyAry.splice(10, dom.value)   //把input的value添加进参数列表      unshift()方法是向数组的头部添加元素,并返回新数组的长度
76                strategyAry.push(rule.errorMsg)      //把errorMsg添加进参数列表
77                return strategiesObj[strategy].apply(null, strategyAry)
78            })
79        })(rule)
80    }
81}
82
83Validator.prototype.start = function(){
84    var msgArr = [];
85    var msgObj = {};
86    for(var i=0this.cache[i]; i++){    //此处的this.cache[i]函数是在this.cache数组中保存的校验规则函数
87        var resultArgs = this.cache[i]();   //开始校验,并取得校验后返回信息
88        if(resultArgs){    //如果有确切的返回值,说明校验没有通过
89            msgArr.push({    //将策略类具体方法的返回值组合成数组对象,包含错误表单的索引、name、value、错误信息及其他自定义配置信息如长度
90                errIndex:i, 
91                errName: resultArgs.errName, 
92                errValue: resultArgs.errValue, 
93                errMsg: resultArgs.errMsg, 
94                errLength: resultArgs.errLength?resultArgs.errLength:''
95            })
96        }
97    }
98    this.errArr = msgArr;
99    return msgArr
100}
101
102Validator.prototype.errShow = (function(){
103    var singleErr = null;
104
105    return function(){
106        console.log(singleErr);
107        if(!singleErr){
108            for(var i=0, l=this.errArr.length; i<l; i++){
109                var label = document.createElement('label')
110                label.innerHTML = this.errArr[i].errMsg
111                setAttributes(label, {
112                    'for'this.errArr[i].errName,
113                    'id'this.errArr[i].errName + '-error',
114                    'class''error'
115                })
116                //原生js插入兄弟节点是方法是获取父节点然后通过appendChild或者insertBefore方法插入
117                document.getElementsByName(this.errArr[i].errName)[0].parentNode.append(label)
118            }
119        }
120        return singleErr = label;
121    }
122})()
123
124//实例化Validator类
125var ValidatorFunc = function(){
126    var validator = new Validator();        //创建一个validator对象
127
128    //添加一些校验规则
129    validator.add('username', registerForm.username, [{strategy'isNonEmpty'errorMsg'用户名不能为空'}])
130    validator.add('password', registerForm.password, [{strategy'minLength:6'errorMsg'密码长度不能少于6位'}])
131    validator.add('phoneNumber', registerForm.phoneNumber, [{strategy'isMobile'errorMsg'手机号码格式不正确'}, {strategy'minLength:11'errorMsg'手机号长度不能少于11位'}])
132
133    var errorMsgArr = validator.start();   //获得校验结果
134    validator.errShow()
135    return errorMsgArr;    //返回校验结果
136}
137
138//实际表单校验调用
139var registerForm = document.getElementById('registerForm')
140registerForm.onsubmit = function(){
141    var errorMsgArr = ValidatorFunc(); //如果errorMsg有确切的返回值,说明未通过校验
142    if(errorMsgArr){
143        console.log(errorMsgArr);
144        return false    //阻止表单提交
145    }
146}
147
148
</script>
149</html>

这个DEMO还是存在bug的,比如重复点击提交按钮时会出现多次错误提示,这个应该可以通过将实例方法errShow改为类方法加闭包加单例模式来解决。另外在具体调用配置上还有很多可以优化的地方。

通过上面DEMO学习,可以体会到策略模式的优越性,比如可以用在多重条件选择语句上,将条件分支算法封装到策略类中,使得它们易于切换、易于理解,还容易扩展。

posted @ 2018-09-29 16:11  sxmn  阅读(727)  评论(0编辑  收藏  举报