策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

介绍

  • 意图:定义一系列的算法,一个个封装起来, 并且使它们可相互替换。 算法集: { 算法名:算法实现 }

  • 主要解决:多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。 算法 = 算法集[算法名]

  • 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

  • 如何解决:将这些算法封装成一个一个的类,任意地替换。

  • 关键代码:实现同一个接口。

  • 应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。3、表单验证,不同类型数据分策略验证

一步步实现表单验证的策略模式

  • 现在有一批数据
var formData = {
    name: "0_O",
    age: 18,
    phone: 15652168660,
    email: "pch1024@outlook.com"
};
  • 对每一项数据都配置对应的验证算法
var config = {
    name: [ "isNonEmpty", "isAlphaNum" ],
    age: [ "isNonEmpty", "intNumber" ],
    phone: [ "isPhone", "intNumber" ],
    email: [ "isEmail" ]
};
  • 实现具体的验证算法
var types = {
    isNonEmpty: {
        validate: function (val) {
            return val !== "";
        },
        msg: "值不能为空。"
    },
    intNumber: {
        validate: function (val) {
            return !isNaN(val) && Math.round(val) === val && val > 0;
        },
        msg: "值只能是整数。"
    },
    isPhone: {
        validate: function (val) {
            return !isNaN(val) && val >= 10000000000 && val < 20000000000 && Math.round(val) === val;
        },
        msg: "手机号应该是11位的数字。"
    },
    isAlphaNum: {
        validate: function (val) {
            return !/[^a-z0-9_]/i.test(val);
        },
        msg: "用户名仅支持数字、字母、下划线。"
    },
    isEmail: {
        validate: function (val) {
            return /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/i.test(val);
        },
        msg: "邮箱输入有误。"
    }
};
  • 核心策略模式代码
var validator = {
    // 所有可用的验证算法
    types: types,
    // 验证器配置类型约定
    config: config,
    // 验证结果存储
    messages: [],
    // 快速检测最终结果
    hasError: function () {
        return this.messages.length > 0;
    },
    // 验证方法实现
    validate: function (data) {
        // 重置验证结果存储
        this.messages = [];
        // 策略实现
        for (let key in data) {
            // 直属属性
            if (data.hasOwnProperty(key)) {
                // 获取数据的类型约定
                let types = this.config[ key ];
                // 没有约定,不校验
                if (!types || types.length === 0) continue;
                // 遍历约定
                for (let i = 0; i < types.length; i++) {
                    // 获取类型约定的校验规则
                    let type = types[ i ];
                    let tester = this.types[ type ];
                    // 校验规则不存在
                    if (!tester) {
                        throw {
                            name: "数据校验错误",
                            message: `没有找到数据校验规则:${ type }`
                        };
                    }
                    // 使用规则校验
                    let res = tester.validate(data[ key ]);
                    // 如果返回 false , 校验未通过
                    if (!res) {
                        // 记录错误信息在案
                        this.messages.push(`${ key }得到一个错误的值:${ data[ key ] },错误提示消息:${ tester.msg }`);
                    }
                }
            }
        }
        // 返回最终结果
        return this.hasError();
    }
};
  • 使用 validator 对象的 validate 方法验证并打印错误信息到控制台
validator.validate(formData);

if (validator.hasError()) {
    console.log(validator.messages.join("\n"));
}else{
    console.log('策略模式数据校验成功');
}

// 输出:策略模式数据校验成功

完整代码

// - 现在有一批数据
var formData = {
    name: "0_O",
    age: "",
    phone: 15652168661,
    email: "pch1024@outlook.com"
};

// - 对每一项数据都配置对应的验证算法
var config = {
    name: [ "isNonEmpty", "isAlphaNum" ],
    age: [ "isNonEmpty", "intNumber" ],
    phone: [ "isPhone", "intNumber" ],
    email: [ "isEmail" ]
};
// - 实现具体的验证算法

var types = {
    isNonEmpty: {
        validate: function (val) {
            return val !== "";
        },
        msg: "值不能为空。"
    },
    intNumber: {
        validate: function (val) {
            return !isNaN(val) && Math.round(val) === val && val > 0;
        },
        msg: "值只能是整数。"
    },
    isPhone: {
        validate: function (val) {
            return !isNaN(val) && val >= 10000000000 && val < 20000000000 && Math.round(val) === val;
        },
        msg: "手机号应该是11位的数字。"
    },
    isAlphaNum: {
        validate: function (val) {
            return !/[^a-z0-9_]/i.test(val);
        },
        msg: "用户名仅支持数字、字母、下划线。"
    },
    isEmail: {
        validate: function (val) {
            return /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/i.test(val);
        },
        msg: "邮箱输入有误。"
    }
};

// - 核心策略模式代码

var validator = {
    // 所有可用的验证算法
    types: types,
    // 验证器配置类型约定
    config: config,
    // 验证结果存储
    messages: [],
    // 快速检测最终结果
    hasError: function () {
        return this.messages.length > 0;
    },
    // 验证方法实现
    validate: function (data) {
        // 重置验证结果存储
        this.messages = [];
        // 策略实现
        for (let key in data) {
            // 直属属性
            if (data.hasOwnProperty(key)) {
                // 获取数据的类型约定
                let types = this.config[ key ];
                // 没有约定,不校验
                if (!types || types.length === 0) continue;
                // 遍历约定
                for (let i = 0; i < types.length; i++) {
                    // 获取类型约定的校验规则
                    let type = types[ i ];
                    let tester = this.types[ type ];
                    // 校验规则不存在
                    if (!tester) {
                        throw {
                            name: "数据校验错误",
                            message: `没有找到数据校验规则:${ type }`
                        };
                    }
                    // 使用规则校验
                    let res = tester.validate(data[ key ]);
                    // 如果返回 false , 校验未通过
                    if (!res) {
                        // 记录错误信息在案
                        this.messages.push(`${ key }得到一个错误的值:${ data[ key ] },错误提示消息:${ tester.msg }`);
                    }
                }
            }
        }
        // 返回最终结果
        return this.hasError();
    }
};

// - 使用 `validator` 对象的 `validate` 方法验证并打印错误信息到控制台

validator.validate(formData);

if (validator.hasError()) {
    console.log(validator.messages.join("\n"));
} else {
    console.log("策略模式数据校验成功");
}

// 输出:
// age得到一个错误的值:,错误提示消息:值不能为空。
// age得到一个错误的值:,错误提示消息:值只能是整数。