設計模式-什麼是快樂星球,什麼是策略模式(二)

當咱們計劃國慶出去遊玩時,在交通方式上,咱們能夠選擇貴而快的飛機/價格中等但稍慢的動車/便宜但超級慢的火車,根據不一樣的人,選擇對應的交通方式,且能夠隨意更換交通方式,這就是策略模式javascript

1、什麼是策略模式

策略模式的定義是,定義一系列算法,把它們一個個封裝起來,而且使它們能夠相互替換。html

2、實際場景

1. 計算年終獎

1.1 傳統作法

有一個計算員工年終獎的需求,假設,績效爲 S 的員工年終獎是 4 倍工資,績效爲 A 的員工年終獎是 3 倍工資,績效爲 B 的員工年終獎是 2 倍工資,下面咱們來計算員工的年終獎。java

var calculateBonus = function(performanceLevel, salary) {
	if (performanceLevel === 'S') {
		return salary * 4;
	}
	if (performanceLevel === 'A') {
		return salary * 3;
	}
	if (performanceLevel === 'B') {
		return salary * 2;

	}
};



calculateBonus('B', 20000); // 輸出:40000
 
calculateBonus( 'S', 6000 ); // 輸出:24000

複製代碼

上述代碼有如下缺點:git

  1. 使用 if-else 語句描述邏輯,代碼龐大;
  2. 缺少彈性,若是須要修改績效 S 的獎金係數,必須修改 calculateBonus 函數,違反了開放-封閉原則;
  3. 沒法再次複用,當其餘地方須要用到這套邏輯,只能再複製一份。

1.2 策略模式作法

使用策略模式改良後github

const strategies = {
	S: salary => {
		return salary * 4
	},
	A: salary => {
		return salary * 3
	},
	B: salary => {
		return salary * 2
	}
}

const calculateBonus = (level, salary) => {
	return strtegies[level](salary)
}

console.log(calculateBonus('s', 20000))
console.log(calculateBonus('a', 10000))
複製代碼

能夠看到上述代碼作了如下改動:web

  1. 策略類 strategies 封裝了具體的算法和計算過程(每種績效的計算規則);
  2. 環境類 calculateBonus 接受請求,把請求委託給策略類 strategies(員工的績效和工資;
  3. 將算法的使用和算法的實現分離,代碼清晰,職責分明;
  4. 消除大量的 if-else 語句。

1.3 小結

策略模式使代碼可讀性更高,易於拓展更多的策略算法。當績效係數改變,或者績效等級增長,咱們只須要爲 strategies 調整或新增算法,符合開放-封閉原則。算法

2. 表單校驗

當網頁上的表單須要校驗輸入框/複選框等等規則時,如何去實現呢?設計模式

如今有一個註冊用戶的表單需求,在提交表單以前,須要驗證如下規則:markdown

  1. 用戶名不能爲空
  2. 密碼長度不能少於6位
  3. 手機號碼必須符合格式

2.1 傳統作法

使用 if-else 語句判斷表單輸入是否符合對應規則,如不符合,提示錯誤緣由。dom

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>
	<form id='registerForm' action="xxx" method="post">
		用戶名:<input type="text" name="userName">
		密碼:<input type="text" name="password">
		手機號:<input type="text" name="phone">
		<button>提交</button>
	</form>
	<script type="text/javascript"> let registerForm = document.getElementById('registerForm') registerForm.onsubmit = () => { if (registerForm.userName.value) { alert('用戶名不能爲空') return false } if (registerForm.password.value.length < 6) { alert('密碼長度不能少於6') return false } if (!/(^1[3|5|8][0-9]$)/.test(registerForm.phone.value)) { alert('手機號碼格式不正確') return false } } </script>
</body>
</html>
複製代碼

image.png

上述代碼有如下缺點:

  • onsubmit 函數龐大,包含大量 if-else 語句;
  • onsubmit 缺少彈性,當有規則須要調整,或者須要新增規則時,須要改動 onsubmit 函數內部,違反開放-封閉原則;
  • 算法複用性差,只能經過複製,複用到其餘表單。

2.2 策略模式作法

使用策略模式重構上述代碼。

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body><form action="http://xxx.com/register" id="registerForm" method="post">
		
 請輸入用戶名:
		<input type="text" name="userName" />
		
 請輸入密碼:
		<input type="text" name="password" />
		
 請輸入手機號碼:
		<input type="text" name="phoneNumber" />
		<button>
			提交
		</button>
	</form>
	<script type="text/javascript" src="index.js">
		
	</script></body></html>
複製代碼
// 表單dom
const registerForm = document.getElementById('registerForm')

// 表單規則
const rules = {
    userName: [
        {
            strategy: 'isNonEmpty',
            errorMsg: '用戶名不能爲空'
        },
        {
            strategy: 'minLength:10',
            errorMsg: '用戶名長度不能小於10位'
        }	
    ],
    password: [
        {
            strategy: 'minLength:6',
            errorMsg: '密碼長度不能小於6位'
        }
    ],
    phoneNumber: [
        {
            strategy: 'isMobile',
            errorMsg: '手機號碼格式不正確'
        }
    ]
}

// 策略類
var strategies = {

    isNonEmpty: function(value, errorMsg) {

        if (value === '') {

            return errorMsg;

        }

    },
     minLength: function(value, errorMsg, length) {

        console.log(length)
        if (value.length < length) {

            return errorMsg;

        }

    },
     isMobile: function(value, errorMsg) {

        if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {

            return errorMsg;

        }

    }

};



// 驗證類
const Validator = function () {
    this.cache = []
}

// 添加驗證方法
Validator.prototype.add = function ({ dom, rules}) {
    rules.forEach(rule => {
        const { strategy, errorMsg } = rule
        console.log(rule)
        const [ strategyName, strategyCondition ] = strategy.split(':')
        console.log(strategyName)
        const { value } = dom
        this.cache.push(strategies[strategyName].bind(dom, value, errorMsg, strategyCondition))
    })
}

// 開始驗證
Validator.prototype.start = function () {
    let errorMsg
    this.cache.some(cacheItem => {
            const _errorMsg = cacheItem()
            if (_errorMsg) {
                    errorMsg = _errorMsg
                    return true
            } else {
                    return false
            }
    })

    return errorMsg
}

// 驗證函數
const validatorFn = () => {
    const validator = new Validator()
    console.log(validator.add)

    Object.keys(rules).forEach(key => {
        console.log(2222222, rules[key])
        validator.add({
            dom: registerForm[key],
            rules: rules[key]
        })
    })

    const errorMsg = validator.start()
    return errorMsg
}


// 表單提交
registerForm.onsubmit = () => {
    const errorMsg = validatorFn()
    if (errorMsg) {
        alert(errorMsg)
        return false
    }
    return false
}
複製代碼

上述代碼經過 strategies 定義規則算法,經過 Validator 定義驗證算法,將規則和算法分離,咱們僅僅經過配置的方式就能夠完成表單的校驗,這些校驗規則也能夠複用在程序的任何地方,還能做爲插件的形式,方便的被移植到其餘項目中。

3、總結

策略模式是一種經常使用且有效的設計模式,經過上述例子,能夠總結出策略模式的一些優勢:

  • 策略模式利用組合/委託和多態等技術和思想,能夠有效的避免多重條件選擇語句;
  • 策略模式提供了對開放-封閉原則的完美支持,將算法封裝中獨立的策略類中,使得它們易於切換/理解/擴展;
  • 在策略模式中利用組合和委託來讓 Context 擁有執行算法的能力,這也是繼承的一種更輕便的代替方案。

以上例子源碼地址爲:源碼地址


· 往期精彩 ·

【設計模式-誰沒碰見過幾個單例模式(一)】

【設計模式-什麼是快樂星球,什麼是策略模式(二)】

【設計模式-原來這就是代理模式(三)】

【設計模式-簡單易懂的觀察者模式(四)】

【設計模式-不會吧,不會還有人不知道裝飾器模式吧(五)】

相關文章
相關標籤/搜索