FED

©FrontEndDev.org
2015 - 2024
web@2.22.0 api@2.20.0

javascript 设计模式1——策略模式

在《javascript设计模式》中,作者并没有向我们介绍策略模式,然而它却是一种在开发中十分常见的设计模式。最常见的就是当我们遇到一个复杂的表单验证的时候,常常需要编写一大段的if和else逻辑代码,这些代码维护起来非常麻烦,但是麻烦的事情远远不止于此。通常一个项目中不止涉及单个的表单或者数据的认证,他们往往成群结队地出现。所以一开始为了他们而编写的if和else逻辑代码不仅会显得非常臃肿,而且随着项目的扩展,使你的代码或变得越来越黏糊,就像。。。。。。。是的,就像意大利面条一样!为了改动小小的一个逻辑——姓名从以前的三个字的限制变成现在四个字限制——,为了这个,你必须把整个项目的所有关于验证的界面都查找替换个遍。但是如果你一开始采用合理的设计模式来设计你的代码的话,或许结果就并不是在漆黑的夜苦苦地加班了。在这里,我们以一个简简单单的数据类型为例,来了解一下我们的策略模式是怎么样帮助我们工作的。

var data = {
    firstName : '晓薇儿',
    lastName : '被瑞尔',
    age : 100,
    sex : 0,
    adress : 'stree 12th',
    phone : '15988563324',
    boss : true
}

这是我们要验证的数据源,我们限制条件如下

  1. fistName和lastName字段不能为空并且长度小于三;
  2. age字段不能大于100,
  3. 电话号码必须是正确的
  4. 地址长度必须限制在200个字符以内
  5. 是不是老板这个嘛,只能告诉我们是true和false,也就是只能是boolean类型

来看看传统的验证做法:

function g() {
  if(data.firstName!="" && data.length < 4) {
	  return false;
  }

  if(data.lastName !="" && data.length <4) {
	  return false;
  }
  ......................//此处省略N多行
}

这还只是一个数据源的验证代码,随着数据源的增加,代码会不会庞大起来?

下面让我们来看看策略模式是怎么样实现的

var voidValue = {//策略者
    C : {},//默认配置规则
    M : {//未通过验证时输出的信息
        isEmpty : 'EMPTY',
        isPhone : 'NOTPHONE',
        isBoolean : 'NOTBOOLEAN',
        isLength : 'BIGGER THAN MAX',
        isUndefined : 'UNDEFINED',
        isNumber : 'NOTNUMBER'
    },
    R : {//自定义规则,所有的规则在里面逐步添加
            isEmpty : function(v) {
                return v != '';
            },
            isUndefined :  function(v) {
                return typeof v === 'undefined';
            },
            isPhone : function(v) {
                return /^1[3|4|5|8]\d{9}$/.test(v);
            },
            isBoolean : function(v) {
                return Object.prototype.toString.call(v) === '[object Boolean]';
            },
            isNumber : function(v) {
                return Object.prototype.toString.call(v) === '[object Number]';
            },
            isName :  function(v) {
                return /^[\u4E00-\u9FFF]{1,6}$/.test(v);
            },<br>                    isAdress : function(v) {<br>                        return this.isEmpty(v) && v.length<200;<br>                    }<br>
    },
    vaild : function(d) {//入口函数,传入数据源
        for(var i in d) {//循环传入的对象
            if(!this.C[i]) continue;
            if(this.C[i].fn) {//判断是否有用户自定义输出的字符串,这里其实是经常用到的,比如某个字段没有通过验证需要怎么样提示,以及提示的文字,在验证表单是尤其重要!
                var fn = this.R[this.C[i]['fn']] || this.C[i]['fn'], message = this.C[i]['tip'];
            }else {
                var fn = this.R[this.C[i]] || this.C[i], message = this.M[this.C[i]];
            }
            var t = Object.prototype.toString.call(fn);//这里我们判断是需要执行验证函数还是比对数值大小
            if(t === '[object Function]') {
                if(!fn(d[i])) {
                    console.warn(message);
                    return false;
                }
            }else if(t === '[object Number]') {
                if(!/\d+/.test(d[i]) || parseInt(d[i]) >= fn) {
                    console.warn(message);
                    return false;
                }
            }  
        }
        return d;//如果都匹配到了,可以输出完整的数据源对象。
    }
}

我们来看下他如何调用的

voidValue.C = {//C是公开的对象,外面可以任意改变其值,是为了针对多种多样的验证逻辑。
    firstName : {fn : 'isName', tip : '请填写正确的姓名'},
	//最外层的字段必须是和数据源字段一一对应的,里面的对象第一个参数fn是需要验证的方法,第二个是未通过该方法时提示的文字。
    lastName : {fn : 'isName', tip : '请填写正确的姓名'},
    age : {fn : 122, tip : '您是彭祖吗?这么高龄!'},
	adress : {fn : 'isAdress', tip : '请输入正确的地址'},
    phone : {fn : 'isPhone', tip : '请填写正确的手机号码'},
    boss : {fn : 'isBoolean', tip : '你确定你是老板妈妈吗?'},
}

首先你要做的是改变这个策略者的验证逻辑,如果之后的某个字段验证逻辑一样的话,就不必再写一次了。下面就是简简单单有一句调用入口函数

var m = voidValue.vaild(data);

如果一切都顺利,就会放回data给m了。如果不顺利,也就是说某个字段未通过验证,那么m === false;