#策略模式基础
策略模式是把特定的操作封装在独立的策略函数中,不使用 if 条件语句就能切换不同的操作。
策略模式的简单应用
如果不使用策略模式,需要在 foo 函数中判断本次应该执行的操作
compute 策略函数,也可以在别的地方复用
jsconst compute = { add: (a, b) => a + b, multiply: (a, b) => a * b, } function foo(compute, num1, num2) { return compute(num1, num2) } console.log(foo(compute.add, 1, 2)) // 3 console.log(foo(compute.multiply, 1, 2)) // 2
#策略模式-动画类
动画类将计算 dom 元素当前的位置的函数,封装在外部,调用时传入不同的函数名,即可简单的切换需要应用的动画。
js/** * @description: 计算最终位置函数 * @param {*} t 动画已消耗的时间 * @param {*} b dom原始位置 * @param {*} c dom目标位置和起始位置的差值 * @param {*} d 总持续时间 */ var tween = { linear: function (t, b, c, d) { return (c * t) / d + b }, easeIn: function (t, b, c, d) { return c * (t /= d) * t + b }, strongEaseIn: function (t, b, c, d) { return c * (t /= d) * t * t * t * t + b }, strongEaseOut: function (t, b, c, d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b }, sineaseIn: function (t, b, c, d) { return c * (t /= d) * t * t + b }, sineaseOut: function (t, b, c, d) { return c * ((t = t / d - 1) * t * t + 1) + b }, } class Animation { constructor(dom) { this.dom = dom this.startTime = 0 this.startPos = 0 this.endPos = 0 this.easing = null this.duration = 0 this.propertyName = '' } start(propertyName, endPos, duration, easing) { this.startTime = Date.now() // 计算dom元素起始位置 this.startPos = parseInt(window.getComputedStyle(this.dom)[propertyName], 10) this.propertyName = propertyName this.endPos = endPos this.duration = duration this.easing = tween[easing] let timer = null timer = setInterval(() => { // 动画结束 if (this.step() === false) { clearInterval(timer) } }, 19) } step() { const time = Date.now() if (time - this.startTime > this.duration) { this.update(this.endPos) return false } // 使用动画函数计算dom当前的位置 var pos = this.easing(time - this.startTime, this.startPos, this.endPos - this.startPos, this.duration) this.update(pos) } update(pos) { this.dom.style[this.propertyName] = `${pos}px` } }
使用
html<body> <div class="box"> <div class="ball"></div> </div> <script src="./Animation.js"></script> <script> const ball = document.querySelector('.ball') const animation = new Animation(ball) // animation.start('right', 0, 5000, 'sineaseIn') animation.start('bottom', 0, 5000, 'sineaseIn') </script> </body>
#策略模式-表单验证类
简单的表单验证类
jsconst strategies = { required(value, errMsg) { if (value === '') { return errMsg } }, minLength(value, length, errMsg) { if (value.length < length) { return errMsg } }, } class Validate { constructor() { this.cache = [] } add(key, rules) { rules.forEach(rule => { const [strategy, ...arr] = rule.strategy.split(':') this.cache.push(form => { arr.unshift(form[key]) arr.push(rule.errMsg) return strategies[strategy](...arr) }) }) } async check(form) { for (let i = 0, len = this.cache.length; i < len; i++) { const msg = this.cache[i](form) if (msg) { throw new Error(msg) } } } } const form = { username: 'zwh', password: '12345678', } const validator = new Validate() validator.add('username', [{ strategy: 'required', errMsg: '用户名不为空' }]) validator.add('password', [ { strategy: 'required', errMsg: '密码不为空' }, { strategy: 'minLength:8', errMsg: '密码最短8位' }, ]) validator .check(form) .then(() => { console.log('验证通过') }) .catch(({ message }) => { console.log(message) })
#策略模式的优缺点
优点
- 策略模式避免了多重if语句
- 策略模式是把特定的操作封装在独立的策略函数中,方便切换
- 封装的策略函数,也能方便的在别的地方复用
缺点
- 选择策略时,必须知道每个策略函数的运行效果