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

 

 

 

 

 

三、总结

再一次回顾下策略模式的定义,因为这是对策略模式的最好的总结

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

 

 

 

posted @ 2014-01-10 18:43  锋叔子  阅读(246)  评论(0编辑  收藏  举报