JavaScript设计模式:策略模式

策略模式的定义

策略模式的定义是,定义一个策略对象,封装一系列算法,算法可以灵活切换使用。
它主要解决在有多种算法相似的情况下,使用 if和else 所带来的复杂和难以维护的问题。

策略模式的例子

有一天,铁匠a打造出了一把宝剑sa,并宣扬整个铁匠圈子里就他这把剑打造得最好,成天得意洋洋地在同行面前炫耀。
事情慢慢地传开,隔壁城的铁匠b一听不乐意了,于是带上自己的得意之作——宝剑sb要去跟铁匠a进行较量。
两把宝剑的数据是这样的:

	const sword1 = {
    
    
		name: 'sa',//宝剑的名字sa
		sharpness: 999,//宝剑的锋利度,这数据确实是值得吹嘘
		vulnerability: 100,//宝剑的脆弱度,可以说是比较脆弱了
		magic: '101',//宝剑的魔法ID
	}
	const sword2 = {
    
    
		name: 'sb',//宝剑的名字sb
		sharpness: 500,//宝剑的锋利度,也是一把锋利的绝世好剑
		vulnerability: 10,//宝剑的脆弱度,这把剑算得上比较坚硬
		magic: '102',//宝剑的魔法ID
	}

铁匠a抚须一笑,拿出了锋利度比较器,两人把剑放上去,比较器是这样的:

	function compareSharpness (s1, s2) {
    
    
		if (s1.sharpness > s2.sharpness) {
    
    
			console.log(s1.name + '胜利')
		} else {
    
    
			console.log(s2.name + '胜利')
		}
	}

比较器启动了

	compareSharpness(sword1, sword2)

结果是sa胜利。铁匠b一看就说你这比较器不对劲啊,宝剑怎么能只比较锋利度呢?不行,得加个脆弱度。于是铁匠b掏出一个比较器,一把拍在桌子上,这个比较器长这样:

	function compareSharpnessAndDurability (s1, s2) {
    
    
		const point = 0
		for (let key in s1) {
    
    
			if (key === 'sharpness') {
    
    
				point += (s1[key] - s2[key])
			} else if (key === 'vulnerability') {
    
    
				point += ((s2[key] - s1[key]) * 10)
				//脆弱度是越低越好的,而且脆弱评分的比例要比锋利度高
			}
		}
		if (point > 0) {
    
    
			console.log(s1.name + '胜利')
		} else {
    
    
			console.log(s2.name + '胜利')
		}
	}

这个比较器启动了:

	compareSharpnessAndDurability(sword1, sword2)

很显然是宝剑sb获胜。但轮到铁匠a不服了,表示这是一把魔剑,能释放大魔法——毁天灭地。铁匠b又回击,别得意,我这也能释放大魔法——神圣湮灭。于是二人又合资买了个新的比较器:

	//这个比较器比较先进,记录了一些魔法的评分
	const magicList = {
    
    
		'101': {
    
    
			name: '毁天灭地',
			point: 7000
		},
		'102': {
    
    
			name: '神圣湮灭',
			point: 6000
		}
	}
	function compareSword (s1, s2) {
    
    
		const point = 0
		for (let key in s1) {
    
    
			if (key === 'sharpness') {
    
    
				point += (s1[key] - s2[key])
			} else if (key === 'vulnerability') {
    
    
				point += ((s2[key] - s1[key]) * 10)
				//脆弱度是越低越好的,而且脆弱评分的比例要比锋利度高
			} else if (key === 'magic') {
    
    
				point += (magicList[s1[key]].point - magicList[s2[key]].point)
				//魔法评分要读取魔法表
			}
		}
		if (point > 0) {
    
    
			console.log(s1.name + '胜利')
		} else {
    
    
			console.log(s2.name + '胜利')
		}
	}

这回又到sa胜利了,铁匠b嘴里说着服气认输,却心想不对啊,这魔法就差一点点,怎么评分差那么多,于是悄悄找上比较器的公司。
比较器的公司稍作了研究,说魔法和脆弱度的评分比重都不应该这么算,而且宝剑的数据还有更多,需要改成更复杂的逻辑。于是一个包含数十个if-else几千行的比较器横空出世……后来的开发人员表示无从下手。
那么,如果从用上策略模式呢?
把比较策略都封装在一个对象里,根据对象的属性名来动态选择比较策略。
代码会变成这样:

	const strats = {
    
    
		sharpness: function (v1, v2) {
    
    
			return v1 - v2
		},
		vulnerability: function (v1, v2) {
    
    
			return (v2 - v1) * 10
		},
		magic: function (v1, v2) {
    
    
			return magicList[v1].point - magicList[v2].point
		},
	}
	const magicList = {
    
    
		'101': {
    
    
			name: '毁天灭地',
			point: 7000
		},
		'102': {
    
    
			name: '神圣湮灭',
			point: 6000
		}
	}
	function compareSword (s1, s2) {
    
    
		const point = 0
		for (let key in s1) {
    
    
			point += strats[key](s1[key], s2[key])
		}
		if (point > 0) {
    
    
			console.log(s1.name + '胜利')
		} else {
    
    
			console.log(s2.name + '胜利')
		}
	}

以后无论是添加要比较的属性还是修改属性的比较方法,都只需要在策略里面更改就可以了,甚至不同的策略可以拆开在不同的文件里写。

策略模式的优缺点

优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

缺点: 1、策略函数会增多。

策略模式的使用场景

  • 一个系统需要动态地在几种算法中选择一种。
  • 一个对象有很多的行为,使用多重的条件选择语句来实现。

基于策略模式的表单验证器

根据对策略模式的理解,我写了一个基于策略模式的表单验证器。
首先定义一个构造函数Validator。

const Validator = function(errFunc){
    
    
    this.cache=[]
    this.errFunc=errFunc
}

其中cache是用来记录现在需要验证的所有字符串的验证函数。
errFunc是用于对错误信息处理的函数。
再定义一个策略对象。

Validator.strategies = {
    
    
  /**
   * @param {string} errorMsg - 错误信息
   */
  isNonEmpty: function (errorMsg) {
    
    
    if(!errorMsg){
    
    
      errorMsg="输入不能为空"
    }
    return function (value) {
    
    
      if (value === "") {
    
    
        return errorMsg;
      }
    };
  },
  /**
   * @param {number} length - 最小长度
   * @param {string} errorMsg - 错误信息
   */
  minLength: function (length, errorMsg) {
    
    
    if(!errorMsg){
    
    
      errorMsg="输入内容过少"
    }
    return function (value) {
    
    
      if (value.length < length) {
    
    
        return errorMsg;
      }
    };
  },
  /**
   * @param {number} length - 最大长度
   * @param {string} errorMsg - 错误信息
   */
  maxLength: function (length, errorMsg) {
    
    
    if(!errorMsg){
    
    
      errorMsg="输入内容不能超出长度限制"
    }
    return function (value) {
    
    
      if (value.length > length) {
    
    
        return errorMsg;
      }
    };
  },
  /**
   * @param {string} errorMsg - 错误信息
   */
  isMobile: function (errorMsg) {
    
    
    if(!errorMsg){
    
    
      errorMsg="手机号输入有误"
    }
    return function (value) {
    
    
      if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
    
    
        return errorMsg;
      }
    };
  },
};

在策略中定义了开发常用的四种表单验证策略,包括判空、最大最小长度的限制,以及手机号验证。每个策略中最后一个参数都是错误信息。接下来是对验证策略的添加,一个值可以有多个策略进行验证。

/**
 * 
 * @param {string} value - 待验证字符串
 * @param {function} strategies - 验证策略
 */
Validator.prototype.add=function(value,strategies){
    
    
    if(strategies instanceof Array){
    
    
        for(var i=0;i<strategies;i++){
    
    
            this.cache.push(strategies[i].bind(null,value))
        }
    }else{
    
    
        this.cache.push(strategies.bind(null,value))
    }
}

参数strategies需要传入刚才定义的一系列策略中的一个或多个,所有的函数都会被压入到cache中,直到触发check方法

/**
 * 验证所有的值的合法性
 */
Validator.prototype.check=function(retain){
    
    
    for(var i=0;i<this.cache.length;i++){
    
    
        var msg=this.cache[i]()
        if(msg){
    
    
            this.errFunc(msg)
            return false
        }
    }
    if(!retain){
    
    
        this.cache=[]
    }
    return true
}

check方法会将cache中所有的函数都执行一遍,如果发现不匹配就返回false

表单验证器的使用

只看代码可能对这个表单验证器的使用有些模糊,下面给出一些表单验证器的使用示例。

var text = "123456"
var validator = new Validator(console.log)//传入这个函数是用于提示错误信息的,可以传入$alert来提示错误信息
validator.add(text,Validator.strategies.minLength(4,'长度未达到要求!'))//加入最小长度为4的验证策略
validator.add(text,Validator.strategies.maxLength(6,'太长了!!!'))//加入最大长度为6
validator.check()

也可以在add方法的第二个参数填入数组形式的策略

//同等效果
validator.add(text,[Validator.strategies.minLength(4),Validator.strategies.maxLength(6)])
//验证策略的错误信息有默认值,可不填
validator.check()

如果验证的字符串没有问题,validator.check()会返回true,如果验证的字符串不符合验证策略的要求,validator.check()会返回false并将错误信息作为第一个参数调用错误提示函数,提示错误信息。

猜你喜欢

转载自blog.csdn.net/rdxtfcec/article/details/126153806
今日推荐