JS实现策略模式

来源:岁月联盟 编辑:exp 时间:2012-10-25

基本概念介绍

策略模式支持你在运行时选择算法。代码的客户端可以使用同一个接口来工作。但是它却根据客户正在试图执行任务的上下文,从多个算法中选择用于处理特定任务的算法。

使用策略模式的其中一个例子是解决表单验证问题。可以创建一个具有validate()方法的验证器(validator)对象。无论表单的具体类型是什么,该方法都将会被调用,并且总是返回相同的结果,一个未经验证的数据列表以及任意的错误消息。

但是根据具体的表单形式及待验证的数据,验证器的客户端可以选择不同类型的检查方法,验证器将选择最佳的策略以处理任务,并将具体的数据验证委托给适当的算法。

数据验证示例

假设有以下数据块,它可能来自于网页上的一个表单,而你需要验证它是否有效:

var data = {
    first_name : "Super",
    last_name : "Man",
    age : "unknown",
    username : "o_0"
};
在这个具体的例子中,为使验证器知道什么 是最好的策略,首先需要配置该验证器,并且设置认为是有效的且可接受的规则。

validator.config = {
    first_name : 'isNonEmpty',
    age : 'isNumber',
    username : 'isAlphaNum'
};
 
validator.validate(data);
if(validator.hasErrors()) {
    console.log(validator.messages.join("/n"));
}
接下来实现validator。首先实现用于检查的可用算法。

validator.types.isNonEmpty = {
    validate : function(value) {
        return value !== '';
    },
    instructions : "the value cannot be empty."
};
 
validator.types.isNumber = {
    validate : function(value) {
        return !isNaN(value);
    },
    instructions : "the value can only be a valid number, e.g. 1, 3.14 or 2010."
};
 
validator.types.isAlphaNum = {
    validate : function(value) {
        return !/[^a-z0-9]/i.test(value);
    },
    instructions : "the value can only contain characters and numbers, no special symbols."
};
最后,核心的validator对象如下所示:

var validator = {
    // 所有可用的检查
    types : {},
 
    // 在当前验证会话中的错误信息
    messages : [],
 
    // 当前验证配置名称:验证类型
    config : {},
 
    // 接口方法 `data`为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];
                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();
    },
 
    // 帮助函数
    hasErrors : function() {
        return this.messages.length !== 0;
    }
};
完整代码:

var validator = {
    // 所有可用的检查
    types : {},
 
    // 在当前验证会话中的错误信息
    messages : [],
 
    // 当前验证配置名称:验证类型
    config : {},
 
    // 接口方法 `data`为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];
                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();
    },
 
    // 帮助函数
    hasErrors : function() {
        return this.messages.length !== 0;
    }
};
 
validator.types.isNonEmpty = {
    validate : function(value) {
        return value !== '';
    },
    instructions : "the value cannot be empty."
};
 
validator.types.isNumber = {
    validate : function(value) {
        return !isNaN(value);
    },
    instructions : "the value can only be a valid number, e.g. 1, 3.14 or 2010."
};
 
validator.types.isAlphaNum = {
    validate : function(value) {
        return !/[^a-z0-9]/i.test(value);
    },
    instructions : "the value can only contain characters and numbers, no special symbols."
};
 
var data = {
    first_name : "Super",
    last_name : "Man",
    age : "unknown",
    username : "o_0"
};
 
validator.config = {
    first_name : 'isNonEmpty',
    age : 'isNumber',
    username : 'isAlphaNum'
};
 
validator.validate(data);
if(validator.hasErrors()) {
    console.log(validator.messages.join("/n"));
}
以上validator对象是通用的,增强validator对象的方法是添加更多的类型检查。若在多个页面中使用它,很快就会有一个优良的特定检查集合,故以后针对每个新的用例,所需做的就是配置该验证器并运行validate()方法。