js常用設計模式2-策略模式 1,計算獎金 2,表單校驗 3,策略模式的優缺點

在生活中,我們去某個地方,可以走路,騎自行車,坐地鐵,坐火箭等等,方案很多。
策略模式的定義:定義一系列算法,把他們一個個封裝起來,並且使他們可以相互替換。

1,計算獎金

1.1,根據員工績效和工資來發獎金。

//策略模式:最初代碼實現
var calculateBonus = function (level, salary) {
    if (level === 'S') {
        return salary * 5
    }
    if (level === 'A') {
        return salary * 3
    }
    if (level === 'B') {
        return salary * 2
    }
}
console.log(calculateBonus('S', 4000))
console.log(calculateBonus('B', 2000))

缺點:
calculateBonus 裏全是if-else,所有的邏輯都在這裏面實現;
calculateBonus缺乏彈性,如果新增一個等級C,那麼就不得不修改calculateBonus的代碼
複用性差

1.2,javaScript版策略模式

// js版本的策略模式->1.html
var strategies = {
    'S': function (salary) {
        return salary * 5
    },
    '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', 4000))
console.log(calculateBonus('B', 2000))

2,表單校驗

2.1,實現一個普通的表單校驗功能

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <form action="http://test.com/register" method='post' id="registerForm">
        請輸入用戶名:<input type="text" name="userName" />
        請輸入密碼:<input type="text" name="passWord" />
        請輸入手機號:<input type="text" name="phoneNumber" />
        <button>提交</button>
    </form>

    <script>
        var registerForm = document.getElementById('registerForm')

        registerForm.onsubmit = function () {
            if (registerForm.userName.value === '') {
                alert('用戶名不能爲空')
                return false
            }
            if (registerForm.passWord.value.length < 6) {
                alert('密碼長度不能小於6位')
                return false
            }
            if (!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
                alert('手機號碼格式不正確')
                return false
            }
        }
    </script>
</body>

</html> 

缺點很明顯:
所有的邏輯都在registerForm.onsubmit中,包含很多if-else語句
registerForm.onsubmit缺乏彈性
算法複用性差

2.2,策略模式重構表單校驗

(1)現在,我們要用策略模式來重構表單校驗。首先,我們要把校驗邏輯都封裝成策略對象:

        // 策略基本完成
        var strategies = {
            isNonEmpty: function (value, errorMsg) {
                if (value === '') {
                    return errorMsg
                }
            },
            minLength: function (value, length, errorMsg) {
                if (value.length < length) {
                    return errorMsg
                }
            },
            isMobile: function (value, errorMsg) {
                if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
                    return errorMsg
                }
            },
        }

(2)接下來我們來實現Validator類。Validator類在這裏作爲Context,負責接收用戶的請求並委託給strategy對象。先看一下Validator類對象是如何工作的,這樣便於我們編寫Validator類的代碼:

     //新增方法,統一管理form
        var validataFunc = function () {
            var validator = new Validator()
            validator.add(registerForm.userName, 'isNonEmpty', '用戶名不能爲空')
            validator.add(registerForm.passWord, 'minLength:6', '密碼長度不能小於6位')
            validator.add(registerForm.phoneNumber, 'isMobile', '手機號碼格式不正確')

            var errorMsg = validator.start()
            return errorMsg
        }
        
        var registerForm = document.getElementById('registerForm')
        registerForm.onsubmit = function () {
            var errorMsg = validataFunc()
            if (errorMsg) {
                alert(errorMsg)
                return false
            }
        }

(3)可以看到,validator對象時通過add方法添加校驗規則,add方法有三個參數:

  • registerForm.passWord:指參與校驗的input框
  • 'minLength:6':以冒號隔開的字符串。冒號前面的minLength是校驗的策略名,6是需要的參數
  • '密碼長度不能小於6位':校驗未通過時的錯誤信息
    最後還需要一個start方法,用來對add進來的內容進行校驗,並返回錯誤信息。如果返回了錯誤信息,registerForm.onsubmit()方法返回false來阻止表單提交。
        // Validator類在這裏作爲Context,負責接收用戶的請求併發送給strategies
        var Validator = function () {
            this.cache = []
        }
        // add中保存需要處理的函數
        Validator.prototype.add = function (dom, rule, errorMsg) {
            this.cache.push(function () {
                var array = rule.split(':')
                var strategy = array.shift()
                array.unshift(dom.value)
                array.push(errorMsg)
                return strategies[strategy].apply(dom, array)
            })
        }
        Validator.prototype.start = function () {
            for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
                var errorMsg = validatorFunc()
                if (errorMsg) {
                    return errorMsg
                }
            }
        }

2.3,一個文本框可對應多個校驗規則

實際上2.2可以實現這個功能,只是不太好看

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <form action="http://test.com/register" method='post' id="registerForm">
        請輸入用戶名:<input type="text" name="userName" />
        請輸入密碼:<input type="text" name="passWord" />
        請輸入手機號:<input type="text" name="phoneNumber" />
        <button>提交</button>
    </form>

    <script>
        // 這裏主要是爲了在一個輸入框中匹配多個驗證規則;實際上之前的策略可以實現,多寫幾次就好了
        /*  策略對象  */
        var strategies = {
            isNonEmpty: function (value, errorMsg) {
                if (value === '') {
                    console.log(errorMsg)

                    return errorMsg
                }
            },
            minLength: function (value, length, errorMsg) {
                if (value.length < length) {
                    return errorMsg
                }
            },
            isMobile: function (value, errorMsg) {
                if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
                    return errorMsg
                }
            },
        }


        /* Validator類 */
        var Validator = function () {
            this.cache = []
        }
        // add中保存需要處理的函數
        Validator.prototype.add = function (dom, rules) {
            var self = this
            for (let i = 0, rule; rule = rules[i]; i++) {
                (function (rule) {
                    self.cache.push(function () {
                        var startegyArr = rule.startegy.split(':')
                        var startegy = startegyArr.shift()
                        startegyArr.unshift(dom.value)
                        startegyArr.push(rule.errorMsg)

                        return strategies[startegy].apply(dom, startegyArr)
                    })
                })(rule)

            }
        }
        // 上面寫法可以改寫成這個
        // Validator.prototype.add = function (dom, rules) {
        //     var self = this
        //     for (let i = 0; i < rules.length; i++) {
        //         self.cache.push(function () {
        //             var rule = rules[i]
        //             var startegyArr = rule.startegy.split(':')
        //             var startegy = startegyArr.shift()
        //             startegyArr.unshift(dom.value)
        //             startegyArr.push(rule.errorMsg)

        //             return strategies[startegy].apply(dom, startegyArr)
        //         })
        //     }
        // }
        Validator.prototype.start = function () {
            for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
                var errorMsg = validatorFunc()
                if (errorMsg) {
                    return errorMsg
                }
            }
        }

        /* 用戶代碼 */
        var registerForm = document.getElementById('registerForm')

        var validataFunc = function () {
            var validator = new Validator()
            validator.add(registerForm.userName, [{
                startegy: 'isNonEmpty',
                errorMsg: '用戶名不能爲空',
            }, {
                startegy: 'minLength:3',
                errorMsg: '密碼長度不能小於3位',
            }])
            validator.add(registerForm.passWord, [{
                startegy: 'minLength:6',
                errorMsg: '密碼長度不能小於6位',
            }])
            validator.add(registerForm.phoneNumber, [{
                startegy: 'isMobile',
                errorMsg: '手機號碼格式不正確',
            }])

            var errorMsg = validator.start()
            return errorMsg
        }


        registerForm.onsubmit = function () {
            var errorMsg = validataFunc()
            if (errorMsg) {
                alert(errorMsg)
                return false
            }
        }

    </script>
</body>

</html>

3,策略模式的優缺點

優點:

  • 策略模式利用組合、委託、多態等技術和思想,可以有效地避免多重條件選擇語句
  • 策略模式提供了對開放-封閉原則的完美支持,將算法封裝在獨立的strategy中,使得他們易於切換,易於理解,易於擴展
  • 複用性強
    缺點:
  • 用戶在使用策略模式時,需要了解其中所有的細節,strategy要暴露所有實現,違反最少知識原則
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章