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

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地址"
} };

 处理数据并放入方法

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

数据以及配置

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']
}
};

调用方法

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

 总结:

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

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

【javascript】javasrcipt设计模式之策略模式的更多相关文章

  1. JavaScript设计模式之策略模式(学习笔记)

    在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...

  2. JavaScript设计模式之策略模式

    所谓"条条道路通罗马",在现实中,为达到某种目的往往不是只有一种方法.比如挣钱养家:可以做点小生意,可以打分工,甚至还可以是偷.抢.赌等等各种手段.在程序语言设计中,也会遇到这种类 ...

  3. 设计模式:策略模式(Strategy)

    定   义:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化, 不会影响到使用算法的客户. 示例:商场收银系统,实现正常收费.满300返100.打8折.......等不同收费 ...

  4. PHP设计模式之策略模式

    前提: 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查 找.排序等,一种常用的方法是硬编码(Hard Cod ...

  5. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  6. 【设计模式】【应用】使用模板方法设计模式、策略模式 处理DAO中的增删改查

    原文:使用模板方法设计模式.策略模式 处理DAO中的增删改查 关于模板模式和策略模式参考前面的文章. 分析 在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大. ...

  7. [design-patterns]设计模式之一策略模式

    设计模式 从今天开始开启设计模式专栏,我会系统的分析和总结每一个设计模式以及应用场景.那么首先,什么是设计模式呢,作为一个软件开发人员,程序人人都会写,但是写出一款逻辑清晰,扩展性强,可维护的程序就不 ...

  8. 设计模式入门,策略模式,c++代码实现

    // test01.cpp : Defines the entry point for the console application.////第一章,设计模式入门,策略模式#include &quo ...

  9. 设计模式之策略模式和状态模式(strategy pattern & state pattern)

    本文来讲解一下两个结构比较相似的行为设计模式:策略模式和状态模式.两者单独的理解和学习都是比较直观简单的,但是实际使用的时候却并不好实践,算是易学难用的设计模式吧.这也是把两者放在一起介绍的原因,经过 ...

随机推荐

  1. P03-Python装饰器

    本文总结自oldboy python教学视频. 一.前言 1.装饰器本质上就是函数,功能是装饰其他函数,为其他函数添加附加功能. 装饰器在装饰函数时必须遵循3个重要的原则: (1)不能修改被装饰的函数 ...

  2. redux进阶 --- 中间件和异步操作

    你为什么需要异步操作? https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in ...

  3. Java基本数据类型-包装类

    为什么会有基本数据类型包装类? 将基本数据类型封装成为对象,这样可以在对象中定义更多的方法来操作该数据 包装类常用操作就是用于基本数据类型与字符串之间的转换 基本数据类型对应的包装类 byte(Byt ...

  4. Compile android source and kernel for emulator in Debian

    1.download the android source code Reference from http://source.android.com/source/downloading.html ...

  5. Idea与Eclipse操作代码的快捷方式

    1.Idea格式化代码的快捷键:ctrl+alt+L 2.在IDEA中创建了properties文件,发现默认中文不会自动进行unicode转码.如下 在project settings - File ...

  6. 解决emacs配置tern报错`tern-reparse-on-idle':

    使用Nodejs安装完tern后,在/user/local/bin建立软连接

  7. css定位问题的记录

    postion:relative是子块级元素面向父级元素的相对定位,定位关键字使用left/right/top/bottom.兄弟块元素之间相对进行定位,但是position移动后,原位置依然保留.而 ...

  8. 深入redis内部--字典实现

    redis的字典定义和实现在dict.h和dict.c文件中. 1.字典结构 typedef struct dict { dictType *type; //定义了字典需要的函数 void *priv ...

  9. mysql导入外部.sql文件时错误

    当前环境: 操作系统:windows 7 mysql版本:5.5.36 MySQL Community Server (GPL) 当我第一次导入.sql文件时报错: mysql> source ...

  10. 解决 swap file “*.swp”already exists!问题

     用vim编辑文件实际上是先copy一份临时文件,病映射到内存给你编辑,编辑的是临时文件,当执行:w后才保存临时文件到原文件,执行:q后才删除临时文件. 每次启动检索是否有临时文件,有就询问如何处理, ...