策略模式定义了算法家族,分别封装起来,让他们之间能够互相替换,此模式让算法的变化独立于使用算饭的客户. html
相信你们在web开发的时候都接触过jquery验证插件jquery.validate.js, 接下来咱们经过此插件的源码和用法来展开讨论策略模式的用法。jquery.validate.js在线源码网址: jquery
http://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.js
web
(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));
第一步首先在文档加载完毕后初始化验证规则 ajax
$(document).ready(function() { // 中文验证 jQuery.validator.addMethod("chinese", function(value, element) { var ip = /[\u4e00-\u9fa5]/; return this.optional(element) ||(!ip.test(value)); }, "不容许输入中文"); // jquery验证 $("#interface-form").validate({ rules : { serviceName : { required : true, maxlength : 60 }, serverHost : { required : true, maxlength : 60, chinese:true }, target : { required : true, maxlength : 200, chinese:true }, interval : { required : true, maxlength : 10 }, warningVal : { required : true, maxlength : 10 }, timeoutVal : { required : true, maxlength : 10 }, method : { required : true, maxlength : 200, chinese:true }, "userName" : { required : true, maxlength : 60 }, password : { required : true, maxlength : 200 }, passwordAgain : { required : true, maxlength : 200, equalTo:"#password" }, testsql : { required : true, maxlength : 200 } }, messages : { serviceName : { required : "请输入接口名称", // minlength: "用户名长度至少为 3字符", maxlength : "接口名称名称长度最大为 60 字符" }, serverHost : { required : "请输入IP", // minlength: "用户名长度至少为 3字符", maxlength : "ip最大为 60 字符" }, target : { required : "请输入访问地址", // minlength: "负责人长度至少为 3字符", maxlength : "ip最大为 200 字符" }, interval : { required : "请输入监控频率", // minlength: "用户名长度至少为 3字符", maxlength : "电话号码长度最大为 10 字符" }, warningVal : { required : "请输入查询耗时警惕值", // minlength: "用户名长度至少为 3字符", maxlength : "电话号码长度最大为 10 字符" }, timeoutVal : { required : "请输入超时", // minlength: "用户名长度至少为 3字符", maxlength : "电话号码长度最大为 10 字符", }, method : { required : "请输入方法名", // minlength: "用户名长度至少为 3字符", maxlength : "电话号码长度最大为 200 字符" }, userName : { required : "请输入用户名 ", // minlength: "用户名长度至少为 3字符", maxlength : "电话号码长度最大为 200 字符" }, password : { required : "请输入密码", // minlength: "用户名长度至少为 3字符", maxlength : "电话号码长度最大为 200 字符" }, passwordAgain : { required : "请输入确认密码", // minlength: "用户名长度至少为 3字符", maxlength : "电话号码长度最大为 200 字符", equalTo:"两次输入密码不一致!" }, testsql : { required : "请输入测试sql", // minlength: "用户名长度至少为 3字符", maxlength : "电话号码长度最大为 200 字符" } } }); });
var submitForm = $("#interface-form"); if (submitForm.valid() == false) { // alert("表单验证就那么神奇地发生了"); return false; }
// http://docs.jquery.com/Plugins/Validation/valid valid: function() { if ( $(this[0]).is("form")) { return this.validate().form(); } else { var valid = true; var validator = $(this[0].form).validate(); this.each(function() { valid = valid && validator.element(this); }); return valid; } },
// http://docs.jquery.com/Plugins/Validation/Validator/element element: function( element ) { element = this.validationTargetFor( this.clean( element ) ); this.lastElement = element; this.prepareElement( element ); this.currentElements = $(element); var result = this.check( element ) !== false; if ( result ) { delete this.invalid[element.name]; } else { this.invalid[element.name] = true; } if ( !this.numberOfInvalids() ) { // Hide error containers on last error this.toHide = this.toHide.add( this.containers ); } this.showErrors(); return result; },
再由 算法
var result = this.check( element ) !== false;
可知element进一步调用了check方法(但愿是最后一层嵌套),源码以下: sql
check: function( element ) { element = this.validationTargetFor( this.clean( element ) ); var rules = $(element).rules(); var dependencyMismatch = false; var val = this.elementValue(element); var result; for (var method in rules ) { var rule = { method: method, parameters: rules[method] }; try { result = $.validator.methods[method].call( this, val, element, rule.parameters ); // if a method indicates that the field is optional and therefore valid, // don't mark it as valid when there are no other rules if ( result === "dependency-mismatch" ) { dependencyMismatch = true; continue; } dependencyMismatch = false; if ( result === "pending" ) { this.toHide = this.toHide.not( this.errorsFor(element) ); return; } if ( !result ) { this.formatAndAdd( element, rule ); return false; } } catch(e) { if ( this.settings.debug && window.console ) { console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); } throw e; } } if ( dependencyMismatch ) { return; } if ( this.objectLength(rules) ) { this.successList.push(element); } return true; },
result = $.validator.methods[method].call( this, val, element, rule.parameters );
可知check经过call调用插件中定义的验证方法,自此验证完毕。 app
咱们再来回顾下策略模式的标准定义:策略模式定义了算法家族,分别封装起来,让他们之间能够互相替换,此模式让算法的变化独立于使用算饭的客户. 框架
由源码咱们能够知道validate 插件只是定义了一下 比较经常使用的验证算法,若是用户须要添加自定义的验证方法,只须要经过addMethod方法便可,无需在插件中添加新的方法,虽然也能够实现。更难得的是表单的验证其实并非经过if,else来判断验证类型,而是经过动态添加验证类型,在源码中验证类型(存放在classRuleSettings对象中)和判断是否存在相关验证类型. ide
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")); }
再一次回顾下策略模式的定义,由于这是对策略模式的最好的总结 测试
策略模式定义了算法家族,分别封装起来,让他们之间能够互相替换,此模式让算法的变化独立于使用算法的客户.