策略模式定義了算法家族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化獨立於使用算飯的客戶. 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")); }
再一次回顧下策略模式的定義,由於這是對策略模式的最好的總結 測試
策略模式定義了算法家族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化獨立於使用算法的客戶.