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、总结

再一次回顾下策略模式的定义,由于这是对策略模式的最好的总结 测试

策略模式定义了算法家族,分别封装起来,让他们之间能够互相替换,此模式让算法的变化独立于使用算法的客户.

相关文章
相关标签/搜索