JavaScript设计模式之策略模式

1,432 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

也不知道是因为坚持了跑步,还是因为和她的相遇,自己的状态🤣也愈发的更好了,就像陶立夏书中所说的--“遇见你,生命转弯,像平缓的河流奔向下未知的悬崖。”

这篇呢,是读书笔记啦,希望通过自己的理解输出,增加记忆和理解,也希望能够帮到在读文章的你。

1.策略模式:

定义:策略模式是指通过一系列算法,把他们一个个封装起来,并且使他们可以相互替换。

下面来看个例子:

很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。绩效为S级的年终奖可能是12倍工资,绩效为A级的人年终奖有3倍工资,而绩效为B的人年终奖是2倍工资,如何通过一段代码来计算员工的年终奖呢?

//设置各个级别的类	以及相应的级别算法
var levelS = function(){};
	performanceS.prototype.calculate = function( salary ){
		return salary * 4;
	};
	var levelA = function(){};
	performanceA.prototype.calculate = function( salary ){
		return salary * 3;
	};
	var levelB = function(){};
	performanceB.prototype.calculate = function( salary ){
		return salary * 2;
	};

  • 设置各个评级级别的类,并将相应级别的算法挂在原型上
	//接下来定义奖金类Bonus:
	var Bonus = function(){
		this.salary = null; // 原始工资
		this.strategy = null; // 绩效等级对应的策略对象
	};

	Bonus.prototype.setSalary = function( salary ){
		this.salary = salary; // 设置员工的原始工资
	};

	Bonus.prototype.setStrategy = function( strategy ){
		this.strategy = strategy; // 设置员工绩效等级对应的策略对象
	};

	Bonus.prototype.getBonus = function(){ // 取得奖金数额
		return this.strategy.calculate( this.salary ); // 把计算奖金的操作委托给对应的策略对象
	};
  • 设置一个Bouns的构造函数,主要还是处理组合业务逻辑;该函数中中初始化了两个属性,salary(基础工资)strategy(用于后续赋值绩效评级)
 // 使用	
var bonus = new Bonus();
	bonus.setSalary( 10000 );

	bonus.setStrategy( new performanceS() ); // 设置策略对象
	console.log( bonus.getBonus() ); // 输出:40000
	bonus.setStrategy( new performanceA() ); // 设置策略对象
	console.log( bonus.getBonus() ); // 输出:30000
  • 通过构造函数实例化一个Bouns的实例
  • 通过setSalavy设置基础薪资
  • 通过setStrategy设置绩效评级
  • 通过getBonus()来获取最后的奖金数额

通过多个评级构造函数,和奖金构造函数实现了简单的策略模式

实际上,在js中,函数也是对象,故而上述的代码也可以简化成下面:

	var strategies = {
		"S": function( salary ){
			return salary * 4;
		},
		"A": function( salary ){
			return salary * 3;
		},
		"B": function( salary ){
			return salary * 2;

		}
	};
	var calculateBonus = function( level, salary ){
		return strategies[ level ]( salary );
	};

	console.log( calculateBonus( 'S', 20000 ) ); // 输出:80000
	console.log( calculateBonus( 'A', 10000 ) ); // 输出:30000
  • 通过为strategies对象的不同属性,设置不同的绩效评级算法
  • 通过函数另外一个函数,根据参数levelsalary从而得出最后的绩效工资

2.使用策略模式实现缓动动画:

JavaScript实现动画原理:通过连续改变元素的某个CSS属性,比如left,top,background-position来实现动画效果。

🚀需求:希望能够编写一个动画类和一些缓动算法,让🏀以各种各样的缓动效果在页面中运动。

思路:

  • 动画开始时,🏀所在的原始位置;
  • 🏀移动的目标位置;
  • 动画开始时间的准确时间点;
  • 🏀运动持续的时间;

首先是缓动算法,这些算法都接收4个参数:

  • t:动画已消耗的时间

  • b·:🏀原始位置

  • c:🏀目标位置

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

html部分:

<body>
	<div style="position:absolute;background:blue" id="div">我是div</div>
</body>

接着来看下Animate

  • Animate初始化:
// Animate构造函数	
var Animate = function( dom ){
		this.dom = dom; // 进行运动的dom 节点
		this.startTime = 0; // 动画开始时间
		this.startPos = 0; // 动画开始时,dom 节点的位置,即dom 的初始位置
		this.endPos = 0; // 动画结束时,dom 节点的位置,即dom 的目标位置
		this.propertyName = null; // dom 节点需要被改变的css 属性名
		this.easing = null; // 缓动算法
		this.duration = null; // 动画持续时间
	};

  • Animate开启动画的方法:
    • propertyName:节点需要被改变的CSS 属性名
    • endPos:节点目标位置
    • duration:动画持续时间
    • easing:缓动算法
	Animate.prototype.start = function( propertyName, endPos, duration, easing ){
		this.startTime = +new Date; // 动画启动时间
		this.startPos = this.dom.getBoundingClientRect()[ propertyName ]; // dom 节点初始位置
		this.propertyName = propertyName; // dom 节点需要被改变的CSS 属性名
		this.endPos = endPos; // dom 节点目标位置
		this.duration = duration; // 动画持续事件
		this.easing = tween[ easing ]; // 缓动算法
		var self = this;
		var timeId = setInterval(function(){ // 启动定时器,开始执行动画
			if ( self.step() === false ){ // 如果动画已结束,则清除定时器
				clearInterval( timeId );
			}
		}, 19 );
	};
  • 这里通过setInterval去持续的执行setp函数,直到动画结束为止,清理定时器。

  • Animate动画每一帧要做的事情:

	Animate.prototype.step = function(){
	var t = +new Date; // 取得当前时间
	if ( t >= this.startTime + this.duration ){ // (1)🌵
		this.update( this.endPos ); // 更新小球的CSS 属性值
		return false;
	}
	var pos = this.easing( t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration );
	// pos 为小球当前位置
		this.update( pos ); // 更新小球的CSS 属性值
	};

🌵处代码的意思是,如果当前时间大于动画开始时间加上动画持续时间之和,说明动画已经结束了要修改🏀的位置。

  • Animate动画负责修改css样式的部分:
    • pos为移动位置
	Animate.prototype.update = function( pos ){
		this.dom.style[ this.propertyName ] = pos + 'px';
	};

让🏀动起来:

	var div = document.getElementById( 'div' );
	var animate = new Animate( div );
	animate.start( 'left', 500, 1000, 'strongEaseOut' );

更多的还是要去思考,充分的思考让看到的成为理解的,才可能在实际项目中有所应用。