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>

一声蔷薇udVkP
25 声望3 粉丝

未来可期