1、策略模式定义:
将一系列算法封装起来,为了以后可以互相替换使用,由策略类和context组成,context接受用户信息,然后将请求委托给策略类
(现实生活中,我们要去一个城市,交通方式就有:飞机、高铁、开车、大巴等,这些方式都能到达目的地,我们可以根据自身需求来选择一个策略)
2、策略模式的优点
1.策略模式利用组合、委托和多态等技术思想,可以有效的避免许多重条件选择语句
2.有弹性,遵守封闭/开发原则,将算法封装在独立的strategy中,使他易于切换、易于理解、易于扩展
3.复用方便
4.利用委托和组合来让context拥有执行算法的能力,这也是继承的一种更轻便的替代方案
3、策略模式的缺点
1.会产生比较多的类和函数
2.要使用策略就要明白所有策略,违反知识最少化原则
4、通过策略模式学习收获到什么
1.封装变化、多态、委托
5、通过例子来加深策略模式的认识
1.奖金的计算
<script>
// ///////////////////////////////////没有使用策略模式/////////////////////////////////////////
function cuculateBounds(leave,salary){
if(leave==="A"){
return 4*salary
}else if(leave ==="B"){
return 3*salary
}else{
return 2*salary
}
}
// console.log(cuculateBounds("A",3000))
// console.log(cuculateBounds("B",3000))
// 缺点:
// 1、所有逻辑都在cuculateBounds函数体里面,要包含所有的if-else,体量庞大
// 2、缺乏弹性,如果要修改奖金等级A的系数为3.5,就要改变函数体,违反封闭开放与原则
// 3、复用性差,如果要复用则要用复制粘贴
// ///////////////////////////////////用策略模式改写/////////////////////////////////////////
// 1. 将使用算法和算法分开,使用算法的方式不会变,算法会变,所以封装算法(封装变化)
// 2.策略类-不同策略返回不同结果(多态)
var strateies={
"A":function(salary){
return 4*salary;
},
"B":function(salary){
return 3*salary;
},
"C":function(salary){
return 2*salary;
}
}
// 3.context 使用算法,接收请求,不执行操作,将请求委托给策略类(委托)
var caculateBounds = function(leave,salary){
return strateies[leave](salary);
}
console.log(caculateBounds("A",2000))
2.动画的实现
<div id="box" style="position:absolute;width: 200px;height: 200px;background-color: cornflowerblue;"></div>
<script>
// /////////////////////1.策略类-封装动画缓动算法/////////////////////
/**params
*
*开始位置
*要移动的距离
*消耗了多少时间
*总耗时
* */
var easing={
"linear":function(starPos,pos,time,duration){
return pos*time/duration + starPos;
},
"easeIn":function( starPos,pos,time,duration){
return pos*(time/=duration)*time +starPos;
}
}
// /////////////////////2.动画类/////////////////////////////////
// 利用定时器,没19毫秒一帧,更新dom节点样式
function Animate(dom){
this.dom = dom ;
}
// 接收4个参数
/**
* 样式
* 移动目标位置
* 执行时间
* 缓动类型
* **/
Animate.prototype.start = function(propety,pos,duration,estype){
this.propety = propety;
// 开始位置
this.startPos = this.dom.getBoundingClientRect()[propety]
this.endPos = pos;
this.startTime = +new Date;
this.endTime = this.startTime+duration;
this.duraPos = pos-this.startPos;
this.easing = easing[estype];
this.duration = duration;
var _that = this;
var timeId = setInterval(()=>{
if(_that.step()===false){
// 清空定时器
clearInterval(timeId)
timeId = null;
}
},19)
}
Animate.prototype.step = function(){
// 当前时间大于结束时间,返回false
var nowTime =+new Date
if(nowTime>=this.endTime){
// 校正位置
this.update(this.endPos)
return false
}else{
let pos = this.easing(this.startPos,this.duraPos,nowTime-this.startTime,this.duration)
this.update(pos)
}
}
Animate.prototype.update =function(val){
this.dom.style[this.propety] = val+'px'
}
// /////////////////////////////3.调用动画////////////////
var dom = document.getElementById("box");
var animate = new Animate(dom);
// animate.start("top",500,3000,"linear")
// animate.start("left",500,2000,"easeIn")
animate.start("left",500,2000,"linear")
</script>
3.验证表单
<form id="formpane">
用户名:<input type="text" value="" id="userName" placeholder="请输入" />
手机号:<input type="tel" value="" id="telphoneNum" />
密码:<input type="password" value="" id="userPassword" />
<button type="submit">提交</button>
</form>
<script>
// 多规则验证,满足条件,表单放行
/**多规则验证,满足条件,表单放行
* 规则1:用户名不能为空
* 规则2:手机格式正确
* 规则3:密码长度小于6
*/
let regisform = document.getElementById("formpane");
/////////////////////////没有策略模式写法(我的常见写法)//////////////////
// regisform.onsubmit = function(){
// // 用户名不能为空
// if(regisform.userName.value.length===0){
// console.log("用户名不能为空")
// return false
// }
// else if(!/(^1[3|5|8][0-9]{9}$)/.test(regisform.telphoneNum.value)){
// console.log("手机格式不正确")
// return false
// }else if(regisform.userPassword.value.length>6){
// // 密码长度小于6
// console.log("密码长度小于6")
// return false
// }
// alert("提交成功")
// }
/**该写法的启发
* 1、表单的值可以通过表单的dom.id(子id)例regisform.userName
* 2、onsubmit函数体比较庞大,包含所有的if-else逻辑
* 3、缺乏弹性,当要修改验证规则时,需要改动内部逻辑,违反封闭开放原则
* 4、不易复用
*/
// ///////////////////////////用策略模式改写////////////////////
// 1、创建一个策略类
var stargeies = {
isNameEmpty: function (value, msg) {
if (value.length === 0)
return msg
},
isNumberTrue: function (value, msg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return msg
}
},
isMinlen: function (value, min, msg) {
if (value.length < min)
return msg
}
}
// 2、创建一个context的类用来接收用户的请求
function Invalidator() {
this.catchs = [];
}
// 添加规则
Invalidator.prototype.add = function (dom, rules, msg) {
var arr = rules.split(":");
this.catchs.push(function () {
let starge = arr.shift();//删除数组第一个
let value = dom.value;
arr.unshift(value);//在数组第一个插入
arr.push(msg)
return stargeies[starge].apply(dom, arr);
})
}
// 执行规则返回结果
Invalidator.prototype.start = function () {
// 这种方式遍历,当满足条件,退出循环
for (let index = 0; index < this.catchs.length; index++) {
console.log(index);
let msg = this.catchs[index]();
if (msg) {
return msg
}
}
}
// 3、用户调用规则,根据结果判断是否提交表单
// var invaliFunc = function(){
// var invalidator = new Invalidator();
// invalidator.add(regisform.userName,"isNameEmpty","用户名不能为空");
// invalidator.add(regisform.telphoneNum,"isNumberTrue","手机格式不正确");
// invalidator.add(regisform.userPassword,"isMinlen:8","密码长度不能小于8");
// return invalidator.start();
// }
// regisform.onsubmit = function(){
// let value = invaliFunc()
// if(value){
// console.log(value);
// return false;
// }
// }
// //////////////////////////////策略模式-表单验证多规则//////////////
// 添加多规则
Invalidator.prototype.adds = function (dom, arrs) {
arrs.forEach(element => {
let { rules, msg } = element;
let arr = rules.split(":");
this.catchs.push(function () {
let starge = arr.shift();//删除数组第一个
let value = dom.value;
arr.unshift(value);//在数组第一个插入
arr.push(msg)
return stargeies[starge].apply(dom, arr);
})
});
}
var invaliFunc = function () {
var invalidator = new Invalidator();
invalidator.adds(regisform.userName, [{ rules: "isNameEmpty", msg: "用户名不能为空" }, { rules: "isMinlen:6", msg: "用户名不能小于6" }]);
invalidator.add(regisform.telphoneNum, "isNumberTrue", "手机格式不正确");
invalidator.add(regisform.userPassword, "isMinlen:8", "密码长度不能小于8");
return invalidator.start();
}
regisform.onsubmit = function () {
let value = invaliFunc()
if (value) {
console.log(value);
return false;
}
}
</script>
6、实际场景中使用的高阶函数实现隐形的策略模式
<!-- 常见的策略模式。不会有策略类来存放策略方法 -->
<script>
function planA(params) {
console.log("A"+params)
}
function planB(params) {
console.log("B"+params)
}
function planC(params) {
console.log("C"+params)
}
// 使用高阶函数的方式,参数传入函数,然后将事件委托到策略类中执行,多态,调用这个方法传入不同状态,返回不同结果
function caculateBounds(func,params){
func(params)
}
caculateBounds(planA,"欢迎使用A策略")
</script>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。