Javascript设计模式-----策略模式
一、定义
策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算饭的客户.
二、正文
相信大家在web开发的时候都接触过jquery验证插件jquery.validate.js, 接下来我们通过此插件的源码和用法来展开讨论策略模式的用法。jquery.validate.js在线源码网址:
http://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.js
1、纵观整个源码我们可以发现验证插件主要通过jquery的exten和fn来给jquery扩展对象从而达到验证表单的目的
(function($) { $.extend($.fn, { validateDelegate: function( delegate, type, handler ) { return this.bind(type, function( event ) { var target = $(event.target); if ( target.is(delegate) ) { return handler.apply(target, arguments); } }); } }); }(jQuery));
2、jquery.validate.js的用法
第一步首先在文档加载完毕后初始化验证规则
1 $(document).ready(function() { 2 3 // 中文验证 4 jQuery.validator.addMethod("chinese", function(value, element) { 5 var ip = /[\u4e00-\u9fa5]/; 6 return this.optional(element) ||(!ip.test(value)); 7 }, "不允许输入中文"); 8 9 // jquery验证 10 $("#interface-form").validate({ 11 rules : { 12 serviceName : { 13 required : true, 14 maxlength : 60 15 }, 16 serverHost : { 17 required : true, 18 maxlength : 60, 19 chinese:true 20 }, 21 target : { 22 required : true, 23 maxlength : 200, 24 chinese:true 25 }, 26 interval : { 27 required : true, 28 maxlength : 10 29 }, 30 warningVal : { 31 required : true, 32 maxlength : 10 33 }, 34 timeoutVal : { 35 required : true, 36 maxlength : 10 37 }, 38 method : { 39 required : true, 40 maxlength : 200, 41 chinese:true 42 }, 43 "userName" : { 44 required : true, 45 maxlength : 60 46 }, 47 password : { 48 required : true, 49 maxlength : 200 50 }, 51 passwordAgain : { 52 required : true, 53 maxlength : 200, 54 equalTo:"#password" 55 }, 56 testsql : { 57 required : true, 58 maxlength : 200 59 } 60 61 }, 62 messages : { 63 serviceName : { 64 required : "请输入接口名称", 65 // minlength: "用户名长度至少为 3字符", 66 maxlength : "接口名称名称长度最大为 60 字符" 67 68 }, 69 serverHost : { 70 required : "请输入IP", 71 // minlength: "用户名长度至少为 3字符", 72 maxlength : "ip最大为 60 字符" 73 74 }, 75 target : { 76 required : "请输入访问地址", 77 // minlength: "负责人长度至少为 3字符", 78 maxlength : "ip最大为 200 字符" 79 80 }, 81 interval : { 82 required : "请输入监控频率", 83 // minlength: "用户名长度至少为 3字符", 84 maxlength : "电话号码长度最大为 10 字符" 85 86 }, 87 warningVal : { 88 required : "请输入查询耗时警戒值", 89 // minlength: "用户名长度至少为 3字符", 90 maxlength : "电话号码长度最大为 10 字符" 91 92 }, 93 timeoutVal : { 94 required : "请输入超时", 95 // minlength: "用户名长度至少为 3字符", 96 maxlength : "电话号码长度最大为 10 字符", 97 98 }, 99 method : { 100 required : "请输入方法名", 101 // minlength: "用户名长度至少为 3字符", 102 maxlength : "电话号码长度最大为 200 字符" 103 104 }, 105 userName : { 106 required : "请输入用户名 ", 107 // minlength: "用户名长度至少为 3字符", 108 maxlength : "电话号码长度最大为 200 字符" 109 110 }, 111 password : { 112 required : "请输入密码", 113 // minlength: "用户名长度至少为 3字符", 114 maxlength : "电话号码长度最大为 200 字符" 115 116 }, 117 passwordAgain : { 118 required : "请输入确认密码", 119 // minlength: "用户名长度至少为 3字符", 120 maxlength : "电话号码长度最大为 200 字符", 121 equalTo:"两次输入密码不一致!" 122 123 }, 124 testsql : { 125 required : "请输入测试sql", 126 // minlength: "用户名长度至少为 3字符", 127 maxlength : "电话号码长度最大为 200 字符" 128 129 } 130 } 131 }); 132 133 }); 134
接着在表单提交的时候调用如下方法:
1 var submitForm = $("#interface-form"); 2 if (submitForm.valid() == false) { 3 // alert("表单验证就那么神奇地发生了"); 4 return false; 5 } 6
从上面的代码我们可以发现,表单验证的入口是插件中的一个valid方法,源码如下
1 // http://docs.jquery.com/Plugins/Validation/valid 2 valid: function() { 3 if ( $(this[0]).is("form")) { 4 return this.validate().form(); 5 } else { 6 var valid = true; 7 var validator = $(this[0].form).validate(); 8 this.each(function() { 9 valid = valid && validator.element(this); 10 }); 11 return valid; 12 } 13 },
通过代码进一步发现valid方法进一步循环调用了element方法,源码如下:
1 // http://docs.jquery.com/Plugins/Validation/Validator/element 2 element: function( element ) { 3 element = this.validationTargetFor( this.clean( element ) ); 4 this.lastElement = element; 5 this.prepareElement( element ); 6 this.currentElements = $(element); 7 var result = this.check( element ) !== false; 8 if ( result ) { 9 delete this.invalid[element.name]; 10 } else { 11 this.invalid[element.name] = true; 12 } 13 if ( !this.numberOfInvalids() ) { 14 // Hide error containers on last error 15 this.toHide = this.toHide.add( this.containers ); 16 } 17 this.showErrors(); 18 return result; 19 }, 20 21 22 23 24 25 26 再由 27 28 var result = this.check( element ) !== false; 29 30 31 可知element进一步调用了check方法(希望是最后一层嵌套),源码如下: 32 33 34 35 check: function( element ) { 36 element = this.validationTargetFor( this.clean( element ) ); 37 38 var rules = $(element).rules(); 39 var dependencyMismatch = false; 40 var val = this.elementValue(element); 41 var result; 42 43 for (var method in rules ) { 44 var rule = { method: method, parameters: rules[method] }; 45 try { 46 47 result = $.validator.methods[method].call( this, val, element, rule.parameters ); 48 49 // if a method indicates that the field is optional and therefore valid, 50 // don't mark it as valid when there are no other rules 51 if ( result === "dependency-mismatch" ) { 52 dependencyMismatch = true; 53 continue; 54 } 55 dependencyMismatch = false; 56 57 if ( result === "pending" ) { 58 this.toHide = this.toHide.not( this.errorsFor(element) ); 59 return; 60 } 61 62 if ( !result ) { 63 this.formatAndAdd( element, rule ); 64 return false; 65 } 66 } catch(e) { 67 if ( this.settings.debug && window.console ) { 68 console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); 69 } 70 throw e; 71 } 72 } 73 if ( dependencyMismatch ) { 74 return; 75 } 76 if ( this.objectLength(rules) ) { 77 this.successList.push(element); 78 } 79 return true; 80 }, 81 82 83
由
result = $.validator.methods[method].call( this, val, element, rule.parameters );
可知check通过call调用插件中定义的验证方法,自此验证完毕。
3、插件中策略模式的应用
我们再来回顾下策略模式的标准定义:策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算饭的客户.
由源码我们可以知道validate 插件只是定义了一下 比较常用的验证算法,如果用户需要添加自定义的验证方法,只需要通过addMethod方法即可,无需在插件中添加新的方法,虽然也可以实现。更可贵的是表单的验证其实并不是通过if,else来判断验证类型,而是通过动态添加验证类型,在源码中验证类型(存放在classRuleSettings对象中)和判断是否存在相关验证类型.
4、由jquery.validate.js插件我们可以模拟出我们自己的验证框架,主要思想就是侧路模式,一下代码来自http://www.cnblogs.com/TomXu/archive/2012/03/05/2358552.html
var validator = { // 所有可以的验证规则处理类存放的地方,后面会单独定义 types: {}, // 验证类型所对应的错误消息 messages: [], // 当然需要使用的验证类型 config: {}, // 暴露的公开验证方法 // 传入的参数是 key => value对 validate: function (data) { var i, msg, type, checker, result_ok; // 清空所有的错误信息 this.messages = []; for (i in data) { if (data.hasOwnProperty(i)) { type = this.config[i]; // 根据key查询是否有存在的验证规则 checker = this.types[type]; // 获取验证规则的验证类 if (!type) { continue; // 如果验证规则不存在,则不处理 } if (!checker) { // 如果验证规则类不存在,抛出异常 throw { name: "ValidationError", message: "No handler to validate type " + type }; } result_ok = checker.validate(data[i]); // 使用查到到的单个验证类进行验证 if (!result_ok) { msg = "Invalid value for *" + i + "*, " + checker.instructions; this.messages.push(msg); } } } return this.hasErrors(); }, // helper hasErrors: function () { return this.messages.length !== 0; } }; // 验证给定的值是否不为空 validator.types.isNonEmpty = { validate: function (value) { return value !== ""; }, instructions: "传入的值不能为空" }; // 验证给定的值是否是数字 validator.types.isNumber = { validate: function (value) { return !isNaN(value); }, instructions: "传入的值只能是合法的数字,例如:1, 3.14 or 2010" }; // 验证给定的值是否只是字母或数字 validator.types.isAlphaNum = { validate: function (value) { return !/[^a-z0-9]/i.test(value); }, instructions: "传入的值只能保护字母和数字,不能包含特殊字符" }; var data = { first_name: "Tom", last_name: "Xu", age: "unknown", username: "TomXu" }; //该对象的作用是检查验证类型是否存在 validator.config = { first_name: 'isNonEmpty', age: 'isNumber', username: 'isAlphaNum' }; validator.validate(data); if (validator.hasErrors()) { console.log(validator.messages.join("\n")); }
三、总结
再一次回顾下策略模式的定义,因为这是对策略模式的最好的总结
策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户.