javascript設計模式之策略模式

1、定義

策略模式定義了算法家族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化獨立於使用算飯的客戶. html

2、正文

相信你們在web開發的時候都接觸過jquery驗證插件jquery.validate.js,  接下來咱們經過此插件的源碼和用法來展開討論策略模式的用法jquery.validate.js在線源碼網址: jquery

http://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.js
web

一、縱觀整個源碼咱們能夠發現驗證插件主要經過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));


二、jquery.validate.js的用法

第一步首先在文檔加載完畢後初始化驗證規則 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;
	}



從上面的代碼咱們能夠發現,表單驗證的入口是插件中的一個valid方法,源碼以下



// 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;
		}
	},



經過代碼進一步發現valid方法進一步循環調用了element方法,源碼以下:



// 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



四、由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"));
}





3、總結

再一次回顧下策略模式的定義,由於這是對策略模式的最好的總結 測試

策略模式定義了算法家族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化獨立於使用算法的客戶.

相關文章
相關標籤/搜索