【javascript】javasrcipt设计模式之策略模式

策略模式支持在运行时由使用者选择合适的算法,对于使用者而言不用关心背后的具体事项,而使用者自动根据当前程序执行的上下文和配置,从已有的算法列表中选择出合适的算法来处理当前任务。

1.要解决的问题

2.如何实现

3.在开源框架和类库中的使用

4.总结

要解决的问题

策略模式同样解决的是解耦的问题,目的是使调用的客户端与需要调用的算法解耦开来,保证算法的内部实现的更改不会影响到客户端的调用。当然这些算法往往需要封装为较为通用的。这样一来可以自由的从算法几种选取需要调用的合适的算法,就像搭积木一样,而算法也可以独立出来单测。

如何实现(应用场景之一表单验证

对于一个表单,各个字段的输入值格式并不唯一,有的是数字,有的是电话号码。因而他们可能有各自都有的验证的要求。

策略模式是可复用的,比如多个字段可能都要求验证非空,这时,我们可以把场景的验证策略抽象为一个策略集合

使用者需要对表单数据进行校验时,只需要传入数据以及制定各个字段的验证策略,就可以给出相应的验证结果了,从而将表达你的处理和验证逻辑分离开来。

方法

validator.types = {

    isNotEmpty: {
        validate: function(value){
            return value !== "";
        },
        message: "不得为空"
    },

    isNotEqualTo: {
        validate: function(data, curField, compareField){
            return data[curField] === data[compareField];
        },
        message: function(fieldText){
            return "不得与" + fieldText + "相同,请重新输入";
        }
    },

    isValidName: {
        validate: function(value){
            return (/^[\u4e00-\u9fa5]{2,4}$/).test(value);
        },
        message: "只能为2-4个字的汉字"
    },

    isValidIdentity: {
        validate: function(value){
            return (/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i).test(value);
        },
        message: "不合法,请输入身份证上的18位身份证号"
    },

    isBirthEqualTo: {
        validate: function(data, curField, compareField){
            return data[curField].replace(/-/g, '') === data[compareField].substr(6, 8);
        },
        message: function(fieldText){
            return "与" + fieldText + "中生日不一致,请修改";
        }
    },

    isValidDate: {
        validate: function(value){
            var t = new Date(value),
                paddingZero = function(value){
                    return value < 10 ? ('0' + value) : value;
                },
                transStr = [t.getFullYear(), paddingZero(t.getMonth() + 1), paddingZero(t.getDate())].join('-');
            return transStr === value;
        },
        message: '格式不合法,请按"2008-01-01"格式输入日期'
    },

    isValidMobile: {
        validate: function(value){
            return (/^0?(13[0-9]|15[012356789]|18[02356789]|14[57])[0-9]{8}$/).test(value);
        },
        message: "格式不合法,请输入正确的手机号码"
    },

    isValidEmail: {
        validate: function(value){
            return (/^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/).test(value);
        },
        message: "格式不合法,请输入正确的Email地址"
    }

};
View Code

 处理数据并放入方法

var validator = {
    // 所有可用的校验
    types: {},
    // 错误信息
    messages: [],
    // 当前校验配置
    config: {},
    // 接口方法
    validate: function(data){
        var i, msg, item, validators, validatorItem, checker, result;
        // 重置错误消息
        this.messages = [] ;
        // 遍历要校验的键值对数据
        for(i in data){
            console.log(i)
            if(data.hasOwnProperty(i)){
                // 获取校验项
                item = this.config[i];
                // 不存在校验项,不需要校验
                if(!item){
                    continue;
                }
                // 遍历校验项
                validators = item.validators;
                for(var v = 0, len = validators.length; v < len; v++){
                    // 获取对应的校验模块
                    validatorItem = validators[v];
                    // 校验项为字符串,直接校验
                    if(typeof validatorItem == 'string'){
                        checker = this.types[validatorItem];
                        console.log(validatorItem,'字符串');
                        if(!checker){
                            throw {
                                name: "ValidationError",
                                message: "No handler to validate type " + validators[v]
                            };
                        }
                        result = checker.validate(data[i]);

                        console.log(checker,'字符串结果')
                    }else if(Array.isArray(validatorItem)){
                        console.log(validatorItem,'数组');
                        checker = this.types[validatorItem[0]];
                        if(!checker){
                            throw {
                                name: "ValidationError",
                                message: "No handler to validate type " + validators[v]
                            };
                        }
                        // 替换validatorItem第一个元素(validatorName)为data[i](当前校验项值)
                        console.log(validatorItem.slice(1),'截取');
                        console.log([data, i],'数组加上后面')
                        result = checker.validate.apply(this, [data, i].concat(validatorItem.slice(1)));
                    }else{
                        throw {
                            name: "ValidationError",
                            message: "No such validator *" + validatorItem + "* found."
                        };
                    }
                    // 校验不通过
                    if(!result){
                        // 校验器message类型可为string/function
                        var msgType = typeof checker.message;
                        if(msgType == 'string'){
                            msg = item.text + checker.message;
                        }else if(msgType == 'function'){
                            msg = item.text + checker.message.call(null, this.config[validatorItem[1]].text);
                        }
                        this.messages.push(msg);
                    }
                }
            }
        }
        return this.hasErrors();
    },
    hasErrors: function(){
        return this.messages.length !== 0;
    }
};
View Code

数据以及配置

var data = {
    name: '王x',
    gender: 1,
    identity: '011110198806061234',
    birthday: '1988-13-01',
    mobile: '15800000000',
    spareMobile: '13911111111',
    email: 'abcdef.cn'
};

//校验配置
validator.config = {
    name: {
        text: '姓名',
        validators: ['isNotEmpty', 'isValidName']
    },
    identity: {
        text: '身份证号',
        validators: ['isNotEmpty','isValidIdentity']
    },
    birthday: {
        text: '生日',
        validators: [['isBirthEqualTo','identity'],'isValidDate']
    },
    mobile: {
        text: '手机号码',
        validators: ['isValidMobile']
    },
    spareMobile: {
        text: '备用手机号码',
        validators: ['isValidMobile', ['isNotEqualTo', 'mobile']]
    },
    email: {
        text: 'Email',
        validators: ['isValidEmail']
    }
};
View Code

调用方法

validator.validate(data);
if(validator.hasErrors()){
    console.log(validator.messages.join('\n'));
}
View Code

 总结:

策略模式对于有多种可提炼出较为通用的算法,并在不同的使用场景中可能会按需选择某一种或某几种策略完成对应的业务逻辑时比较有用。

所以如果当你的程序中设计到类似的一些场景,入业务逻辑涉及到分类和按需应用时,就可以考虑策略模式来实现算法和调用的解耦。

posted @ 2018-03-21 17:43  teemor  阅读(171)  评论(0编辑  收藏  举报