設計模式之-策略模式

一、策略模式定義:

將一系列算法封裝起來,爲了之後能夠互相替換使用,由策略類和context組成,context接受用戶信息,而後將請求委託給策略類
(現實生活中,咱們要去一個城市,交通方式就有:飛機、高鐵、開車、大巴等,這些方式都能到達目的地,咱們能夠根據自身需求來選擇一個策略)算法

二、策略模式的優勢

1.策略模式利用組合、委託和多態等技術思想,能夠有效的避免許多重條件選擇語句
2.有彈性,遵照封閉/開發原則,將算法封裝在獨立的strategy中,使他易於切換、易於理解、易於擴展
3.複用方便
4.利用委託和組合來讓context擁有執行算法的能力,這也是繼承的一種更輕便的替代方案

三、策略模式的缺點

1.會產生比較多的類和函數
2.要使用策略就要明白全部策略,違反知識最少化原則

四、經過策略模式學習收穫到什麼

1.封裝變化、多態、委託

五、經過例子來加深策略模式的認識

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))
// 缺點:
// 一、全部邏輯都在cuculateBounds函數體裏面,要包含全部的if-else,體量龐大
// 二、缺少彈性,若是要修改獎金等級A的係數爲3.5,就要改變函數體,違反封閉開放與原則
// 三、複用性差,若是要複用則要用複製粘貼

// ///////////////////////////////////用策略模式改寫/////////////////////////////////////////
// 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("提交成功")


        // }
        /**該寫法的啓發
         * 一、表單的值能夠經過表單的dom.id(子id)例regisform.userName
         * 二、onsubmit函數體比較龐大,包含全部的if-else邏輯
         * 三、缺少彈性,當要修改驗證規則時,須要改動內部邏輯,違反封閉開放原則
         * 四、不易複用
         */

        // ///////////////////////////用策略模式改寫////////////////////
        // 一、建立一個策略類
        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
            }
        }
        // 二、建立一個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
                }

            }

        }

        // 三、用戶調用規則,根據結果判斷是否提交表單
        // 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>

六、實際場景中使用的高階函數實現隱形的策略模式

<!-- 常見的策略模式。不會有策略類來存放策略方法 -->
    <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>
相關文章
相關標籤/搜索