javascript 設計模式之單例模式javascript
javascript 適配、代理、裝飾者模式的比較github
javascript 設計模式之觀察者模式markdown
策略模式:定義一系列的算法(這些算法目標一致),把它們一個個封裝起來,而且使它們能夠相互替換。
每家公司年終獎的發放都會根據該年度員工表現給予必定的獎懲,固然 A 公司也不例外, A 公司的年終績效制度以下:
代碼實現以下:
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel == 'S') {
return salary * 4
}
if (performanceLevel == 'A') {
return salary * 3
}
if (performanceLevel == 'B') {
return salary * 2
}
if (performanceLevel == 'C') {
return salary * 0.3
}
}
console.info(calculateBonus('S', 20000)) // 80000
console.info(calculateBonus('C', 15000)) // 4500
複製代碼
功能上能夠正常使用,但存在以下幾個缺點:
讓咱們先來解決第三點算法複用性差問題。 把各類算法(即年終獎的計算規則)封裝到一個個獨立的小函數中。 代碼改爲以下:
var performanceS = function (salary) {
return salary * 4
}
var performanceA = function (salary) {
return salary * 3
}
var performanceB = function (salary) {
return salary * 2
}
var performanceC = function (salary) {
return salary * 0.3
}
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel == 'S') {
return performanceS(salary)
}
if (performanceLevel == 'A') {
return performanceA(salary)
}
if (performanceLevel == 'B') {
return performanceB(salary)
}
if (performanceLevel == 'C') {
return performanceC(salary)
}
}
console.info(calculateBonus('S', 20000)) // 80000
console.info(calculateBonus('C', 15000)) // 4500
複製代碼
採用組合函數,將每一個績效算法抽離成單獨的函數,是解決了複用問題,若是有別的地方要計算 S 等級的薪資,直接調用 performanceS 函數便可。 但上面一、2兩個問題仍然存在,咱們繼續優化,引出主角策略模式
設計模式中很重要的一點就是將不變的部分和變化的部分分離出來,而策略模式的目的就是將算法的使用和算法的實現分離開來。
在這個例子中,算法的使用方式是不變的,都是根據等級和薪資計算年終獎;算法的實現是變化的,好比 S 有 S 等級的計算方式, A 有 A 等級的計算方式。
策略模式的組成:
- 一組策略類,策略類封裝了具體的算法,並負責具體的計算過程。
- 環境類Context,Context接收客戶的請求,隨後把請求委託給某一個策略類。
定義策略類:
//策略類(S)
class performanceS {
calculate(salary) {
return salary * 4;
}
}
//策略類(A)
class performanceA {
calculate(salary) {
return salary * 3;
}
}
//策略類(B)
class performanceB {
calculate(salary) {
return salary * 2;
}
}
//策略類(C)
class performanceC {
calculate(salary) {
return salary * 0.3;
}
}
複製代碼
定義環境類:
// 環境類
class Bonus {
constructor() {
this.salary = null; //原始工資
this.strategy = null; //績效公司對應的策略對象
}
setSalary(salary) {
this.salary = salary; //設置原始工資
}
setStrategy(strategy) {
this.strategy = strategy; //設置員工績效等級對應的策略對象
}
getBonus() {//取得獎金數額
//維持對策略對象的引用
//委託給對應的策略對象
return this.strategy.calculate( this.salary );
}
}
複製代碼
驗證:
const bonus = new Bonus()
bonus.setSalary( 20000 );
bonus.setStrategy( new performanceS() ); //設置策略對象
console.info(bonus.getBonus()) // 80000
複製代碼
上述展現了策略模式的應用,代碼比較清晰,解決了上述的幾大問題,要再增長個績效 D 的邏輯,不會動到 Bonus 類,只要再定義個 performanceD 策略類便可,在 Bonus 類裏作的事情也很單一,負責接收策略類實例,並調用。
上述展現的策略模式是基於傳統的面嚮對象語言,能夠進一步對這段代碼進行優化,變成JavaScript版本的策略模式。
在 JS 中,函數也是對象,因此能夠將策略類直接定義爲函數,並以對象映射形式展現。
//策略對象
var strategies = {
//一系列算法
"S": function ( salary ) {
return salary * 4;
},
"A": function ( salary ) {
return salary * 3;
},
"B": function ( salary ) {
return salary * 2;
},
"C": function (salary) {
return salary * 0.3;
},
};
複製代碼
同時,也能夠將環境類定義爲函數,改爲以下:
var calculateBonus = function ( level, salary) {
return strategies[ level ]( salary );
};
複製代碼
驗證下:
console.log( calculateBonus('S', 20000)); // 80000
複製代碼
再來看下策略模式的定義
定義一系列的算法(這些算法目標一致),把它們一個個封裝起來,而且使它們能夠相互替換。
策略模式指的是定義一系列的算法,而且把它們封裝起來,可是策略模式不只僅只封裝算法,咱們還能夠用來封裝一系列的業務規則,只要這些業務規則目標一致,咱們就可使用策略模式來封裝它們;好比表單驗證
需求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表單驗證</title>
</head>
<body>
<form action='xxx.com' id='registerForm' method='post'>
請輸入用戶名:<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>
複製代碼
跟上述績效獎金,存在同樣的問題,函數過於龐大、缺少彈性以及複用性差,下面採用策略模式優化
採用策略模式,首先要定義策略類,那策略類要先找到算法具體是指什麼:表單驗證邏輯的業務規則
因此定義策略類以下:
// 定義策略類
const 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;
}
}
}
複製代碼
接着要定義環境類,在定義環境類以前,先看下客戶通常是怎麼使用的?
var validataFunc = function () {
//建立一個validator對象
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; //阻止表單提交
}
}
複製代碼
從上述代碼中,能夠明確在環境類 Validator 中要有 add 方法,經過 add 方法來添加校驗規則。
同時有 start 方法,經過 start 方法開始校驗,若是有錯誤,那麼就返回錯誤信息( errorMsg )
有了策略對象以及策略對象與環境類( Validator )的橋樑,咱們即可以寫出 Validator 類代碼
class Validator {
constructor() {
this.cache = []; //保存校驗規則
}
//添加檢驗規則函數
add(dom,rule,errorMsg){
//把strategy和參數分開'minLength:6' 如'minLength:6' -> ["minLength", "6"]
let ary = rule.split(':');
this.cache.push ( function () {
//用戶挑選的strategy ["minLength", "6"] -> 'minLength'
let strategy = ary.shift();
//把input的value添加進參數列表
ary.unshift( dom.value );
//把errorMsg添加進參數列表
ary.push( errorMsg );
//委託策略對象調用
return strategies[ strategy ].apply( dom, ary );
})
}
start(){
for ( var i = 0,validatorFunc; validatorFunc = this.cache[i++];) {
var msg = validatorFunc(); //開始校驗,並取得校驗後的返回信息
if ( msg ) { //若是msg存在,則說明校驗不經過
return msg;
}
}
}
}
複製代碼
在上述中,經過對業務規則這種算法的抽象,經過策略模式來完成表單檢驗,在修改某個校驗規則的時候,咱們只有修改少許代碼便可。好比想把用戶名的輸入改爲不能少於4個字符,只須要把minLength:6改成minLength:4便可
目前實現的表單校驗有一點小問題:一個文本輸入框只能對應一種校驗規則。 若是想要添加多種檢驗規則,能夠經過如下方式添加:
validator.add( registerForm.userName, [{
strategy: 'isNonEmpty',
errorMsg: '用戶名不能爲空'
},{
strategy: 'minLength:10',
errorMsg: '用戶名長度不能小於10位'
}])
複製代碼
要修改 Validator 中的 add 方法,經過遍歷的方式,把多個檢驗規則添加到cache中。
add (dom, rules) {
let self = this;
for (let i = 0,rule; rule = rules[i++];) {
(function ( rule ) {
let strategyAry = rule.strategy.split( ':' );
let errorMsg = rule.errorMsg;
self.cache.push( function () {
let strategy = strategyAry.shift();
strategyAry.unshift( dom.value );
strategyAry.push( errorMsg );
return strategies[ strategy ].apply( dom, strategyAry )
})
})(rule)
}
}
複製代碼
優勢:
缺點:
使用場景:
你的點贊是對我最大的確定,若是以爲有幫助,請留下你的讚揚,謝謝!!!