【設計模式系列】之【策略模式】

前言:要提升代碼水平,就繞不開設計模式。之前也有過一些瞭解,但並沒有深入學習。最近準備系統的學習一下設計模式,提高設計,解耦的能力,發現了一本好書《JavaScript設計模式與開發實踐》,所以邊讀邊寫,把常用的設計模式學習並記錄在這裏。

定義與介紹

單例模式的定義:定義一系列算法,把它們一個個封裝起來,並且使它們可以互相替換。

策略模式是最親民的設計模式了,即使之前沒學過,在很多代碼中我們都會不知不覺的使用簡單的策略模式,但本篇簡述的完整策略模式更加的面向對象

JavaScript中的策略模式

JavaScript中,函數也是對象,最直接的做法就是把策略直接定義爲函數:

// 根據績效和工資計算年終獎的例子:
// 根據績效不同,有不同的算法,把這些算法封裝成不同的策略
const strategies = {
	S: function(salary) {
		return salary * 4;
	},
	A: function(salary) {
		return salary * 3;
	},
	B: function(salary) {
		return salary * 2;
	}
}

const calculateBouns = function(level, salary) {
	return strategies[level](salary);
}

calculateBouns(S, 10000);

可以看到,上邊的做法其實不需要學習,我們在日常編碼中就很可能會自發的這樣使用,僅僅針對這個例子而言,甚至可以更加簡潔。當然這樣做有好有壞,可以根據實際情況決定,如下:

const strategies = {
	S: 4,
	A: 3,
	B: 2,
}

const calculateBouns = function(level, salary) {
	return strategies[level] * salary;
}

calculateBouns(A, 20000);

策略模式的例子

我們有個很常見的表單校驗需求,希望這些校驗規則能夠方便的替換和組合使用,來預防後期的需求變更。


// 表單結構
<html>
	<body>
		<form action='xxx' id='registerForm' method="POST">
			請輸入用戶名:<input type='text' name='userName' />
			請輸入密碼:<input type='text' name='password' />
			請輸入手機號:<input type='text' name='mobile' />
		</form>
	</body>
<html>

// js
/******* 策略對象 ********/
const strategies = {
	isNotEmpty: function(val, errMsg) {
		if(val === '') {
			return errMsg
		}
	},
	minLength: function(val, minLen, errMsg) {
		if(val.length < minLen) {
			return errMsg
		}
	},
	isMobile: function(val, errMsg) {
		if(!/(^1[3|5|8][0-9]{9}$)/.test(val)) {
			return errMsg
		}
	}
}
/******* Validator 類 ********/
class Validator {
	constructor() {
		this.cache = [];
	}
	add(dom, rules) {
		rules.forEach(rule => {
			const strategyArr = rule.strategy.aplit(':');
			const errMsg = rule.errMsg;
			// catch中保存的是校驗方法
			this.catch.push(function() {
				// 取出冒號左邊的策略名稱
				// 如 minLen: 6,爲最小長度爲6位
				const strategy = strategyArr.shift();
				// 把實際的輸入值從左側插入參數數組
				strategyArr.unshift(dom.value);
				// 把錯誤提示從右側插入參數數組
				strategyArr.push(errMsg);
				// 取得對應的策略,使用apply在dom下執行,傳入參數數組strategyArr
				return strategies[strategy].apply(dom, strategyArr);
			})
		})
	}
	start() {
		this.cacht.forEach(validatorFunc => {
			const errMsg = validatorFunc();
			if (errMsg) {
				return errMsg;
			}
		})
	}
}
/******* 調用代碼 ********/
const registerForm = document.getElementById('registerForm');

const validataFunc = function() {
	const validator = new Validator();
	// 校驗對象增加校驗規則
	validator.add(registerForm.userName, [{
		strategy: 'isNotEmpty',
		errMsg: '用戶名不能爲空',
	}, {
		strategy: 'minLen: 10',
		errMsg: '用戶名長度不能小於10位',
	}]);
	validator.add(registerForm.password, [{
		strategy: 'minLen: 6',
		errMsg: '用戶名長度不能小於6位',
	}]);
	validator.add(registerForm.mobile, [{
		strategy: 'isMobile',
		errMsg: '手機號碼格式不正確',
	}]);
	// 校驗對象開始校驗
	const errMsg = validator.start();
	return errMsg;
}

registerForm.onsubmit = function() {
	const errMsg = validataFunc();
	if(errMsg) {
		alert(errMsg)return
	}
	// do submit
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章