JavaScript design patterns: Strategy pattern

Definition of Strategy Pattern

The definition of strategy pattern is to define a strategy object that encapsulates a series of algorithms, and the algorithms can be flexibly switched for use.
It mainly solves the complex and difficult-to-maintain problems caused by using if and else when there are multiple similar algorithms.

Example of Strategy Pattern

One day, blacksmith a made a sword sa, and claimed that his sword was the best in the entire blacksmith circle. He proudly showed off in front of his peers all day long.
The story spread slowly, and Blacksmith B in the next city was unhappy when he heard it, so he took his favorite work, Sword SB, to compete with Blacksmith A.
The data of the two swords is as follows:

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

Blacksmith A stroked his beard and smiled, and took out the sharpness comparator. The two of them put the swords on it. The comparator looked like this:

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

Comparator started

	compareSharpness(sword1, sword2)

The result is sa victory. Blacksmith B looked at it and said there was something wrong with your comparator. How could swords only compare their sharpness? No, you have to add vulnerability. So blacksmith B took out a comparator and slapped it on the table. The comparator looked like this:

	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 + '胜利')
		}
	}

This comparator starts:

	compareSharpnessAndDurability(sword1, sword2)

Obviously the sword sb won. But it was Blacksmith A's turn to be dissatisfied, saying that it was a magic sword that could release great magic - destroying the world. Blacksmith B fired back, don't be complacent, I can also release great magic - Holy Annihilation. So the two jointly bought a new comparator:

	//这个比较器比较先进,记录了一些魔法的评分
	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 + '胜利')
		}
	}

This time, SA won again. Blacksmith B said he was convinced and gave up, but he thought something was wrong. The magic was just a little bit off. Why was the score so different? So he quietly approached the comparator company.
The Comparator company did a little research and said that the scoring proportions of magic and vulnerability should not be calculated this way, and the sword has more data and needs to be changed to more complex logic. So a comparator containing dozens of if-else and thousands of lines was born... Later developers said they had no way to start.
So, what if we use the strategy pattern?
Encapsulate the comparison strategies in an object, and dynamically select the comparison strategy based on the object's attribute name.
The code will become like this:

	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 + '胜利')
		}
	}

In the future, whether you add attributes to be compared or modify the comparison method of attributes, you only need to change them in the strategy. Even different strategies can be separated and written in different files.

Advantages and Disadvantages of Strategy Pattern

Advantages: 1. The algorithm can be switched freely. 2. Avoid using multiple conditional judgments. 3. Good scalability.

Disadvantages: 1. The number of strategy functions will increase.

Usage scenarios of strategy pattern

  • A system needs to dynamically choose one of several algorithms.
  • An object has many behaviors, which are implemented using multiple conditional selection statements.

Form validator based on strategy pattern

Based on my understanding of the strategy pattern, I wrote a form validator based on the strategy pattern.
First define a constructor function Validator.

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

The cache is the verification function used to record all strings that need to be verified now.
errFunc is a function used to handle error information.
Define another policy object.

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

Four form validation strategies commonly used in development are defined in the strategy, including null judgment, maximum and minimum length restrictions, and mobile phone number verification. The last parameter in each strategy is an error message. Next is the addition of verification strategies. A value can have multiple strategies for verification.

/**
 * 
 * @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))
    }
}

The parameter strategies needs to be passed in one or more of the series of strategies just defined. All functions will be pushed into the cache until the check method is triggered.

/**
 * 验证所有的值的合法性
 */
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
}

The check method will execute all functions in the cache and return false if a mismatch is found.

Use of form validators

Just looking at the code may make the use of this form validator a little unclear. Here are some examples of how to use the form validator.

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()

You can also fill in the strategy in the form of an array in the second parameter of the add method.

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

If there is no problem with the verified string, validator.check() will return true. If the verified string does not meet the requirements of the verification strategy, validator.check() will return false and call the error prompt function with the error information as the first parameter. , prompting an error message.

Guess you like

Origin blog.csdn.net/rdxtfcec/article/details/126153806