歡迎訪問主頁,有更多文章內(nèi)容
轉(zhuǎn)載請(qǐng)注明原出處
原文鏈接地址:前端設(shè)計(jì)模式-策略模式
前言
策略模式是定義一系列的算法,把它們一個(gè)個(gè)封裝起來瑞凑,并且使它們可以相互替換。將不變的部分和變化的部分隔開是每個(gè)設(shè)計(jì)模式的主題砂碉。策略模式的目的就是使算法的使用與算法分離開來五辽。封裝的算法具有一定的獨(dú)立性,不會(huì)隨客戶端的變化而變化蹋岩。
一個(gè)策略模式的程序至少有兩部分組成赖草。第一部分是策略類,策略類封裝了具體的算法剪个,負(fù)責(zé)具體的計(jì)算過程秧骑。 第二部分是環(huán)境類Context
,它接受了客戶的要求扣囊,隨后吧請(qǐng)求委托給某一具體的策略類乎折。要做到這點(diǎn),Context
中要維持對(duì)摸個(gè)策略對(duì)象的引用。
例子
這個(gè) demo 是一個(gè)計(jì)算的代碼段
function count(type, number1, number2) {
switch (type) {
case "add":
return number1 + number2;
case "subtract":
return number1 - number2;
case "multiply":
return number1 * number2;
default:
return number1 / number2;
}
}
// if else
function count1(type, number1, number2) {
if (type === "add") {
return number1 + number2;
} else if (type === "subtract") {
return number1 - number2;
} else if (type === "multiply") {
return number1 * number2;
} else {
return number1 / number2;
}
}
或者這中間的switch
用if...else...
來實(shí)現(xiàn)侵歇,但switch
比if...else...
更加一目了然骂澄。這段計(jì)算方法本身沒有問題,但是有更加便于維護(hù)和擴(kuò)展的實(shí)現(xiàn)方式惕虑。下面來進(jìn)行改進(jìn)
const count = {
add(number1, number2) {
return number1 + number2;
},
subtract(number1, number2) {
return number1 - number2;
},
multiply(number1, number2) {
return number1 * number2;
},
divide(number1, number2) {
return number1 / number2;
},
};
count.add(2, 3); // 5
count.subtract(10, 3); // 7
count.multiply(2, 3); // 6
count.divide(6, 2); // 3
經(jīng)過改造后酗洒,可以看成是一個(gè)策略模式,接下來看個(gè)復(fù)雜的例子枷遂。商場的會(huì)員分普通會(huì)員樱衷、銀卡會(huì)員、金卡會(huì)員酒唉、鉆石會(huì)員矩桂、至尊會(huì)員等級(jí)別,在商場消費(fèi)享不同的折扣和積分。會(huì)員在同一商品的折扣分別是 98侄榴、95雹锣、9、8.8癞蚕、8.5 折優(yōu)惠蕊爵。積分比例分別是 1、1.2桦山、1.5攒射、1.6、1.8恒水。購買商品對(duì)應(yīng)的會(huì)員折扣和積分獲取用策略模式來實(shí)現(xiàn):
const priceStrategy = {
level1(price) {
return price * (98 / 100);
},
level2(price) {
return price * (95 / 100);
},
level3(price) {
return price * (90 / 100);
},
level4(price) {
return price * (88 / 100);
},
level5(price) {
return price * (85 / 100);
},
};
const cumulativeScoreStrategy = {
level1(price) {
return Math.floor(price);
},
level2(price) {
return Math.floor(price * 1.1);
},
level3(price) {
return Math.floor(price * 1.2);
},
level4(price) {
return Math.floor(price * 1.3);
},
level5(price) {
return Math.floor(price * 1.4);
},
};
function calculatePrice(level, price) {
return priceStrategy[level] ? priceStrategy[level](price) : 0;
}
function calculateScore(level, price) {
const actually = calculatePrice(level, price);
return (
cumulativeScoreStrategy[level] && cumulativeScoreStrategy[level](actually)
);
}
// 一臺(tái)的彩電的價(jià)格是 8888会放,鉆石會(huì)員的折后價(jià)
calculatePrice("level4", 8888); // 7554.8
// 獲得的積分
calculateScore("level4", 8888); // 10567
表單驗(yàn)證的策略模式
const strategies = {
isCorrectPassword(value, errorMsg) {
if (!/^(?:(?=.*[A-Z])(?=.*[0-9])).\\S{7,19}$/.test(value)) {
return errorMsg;
}
return null;
},
isNotEmpty(value, errorMsg) {
if (value === "" || value === undefined) {
return errorMsg;
}
return null;
},
minLength(value, length, errorMsg) {
if (value.length < length) {
return errorMsg;
}
return null;
},
maxLength(value, length, errorMsg) {
if (value.length > length) {
return errorMsg;
}
return null;
},
isMobile(value, errorMsg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
return null;
},
isNotAllEmpty(value = [], errorMsg) {
if (!value.some((i) => !!i)) {
return errorMsg;
}
return null;
},
};
class Validator {
constructor() {
this.cache = [];
}
add(value, rules) {
for (let i = 0, rule; (rule = rules[i++]); ) {
const strategyArray = rule.strategy.split(":") || [];
const { errorMsg } = rule;
this.cache.push(() => {
const strategy = strategyArray.shift();
strategyArray.unshift(value);
strategyArray.push(errorMsg);
return strategies[strategy].apply(value, strategyArray);
});
}
}
run() {
for (let i = 0, validatorFunc; (validatorFunc = this.cache[i++]); ) {
const msg = validatorFunc();
if (msg) {
return msg;
}
}
return null;
}
}
// 使用
const validator = new Validator();
validator.add(registerForm.userName, [
{
strategy: "isNonEmpty",
errorMsg: "userName not empty!",
},
{
strategy: "minLength:6",
errorMsg: "userName length should more than 6",
},
]);
validator.add(registerForm.password, [
{
strategy: "minLength:6",
errorMsg: "password length should more than 6",
},
]);
const errorMsg = validator.start();
if (errorMsg) {
console.log(errorMsg);
return errorMsg;
}
策略模式屬于對(duì)象行為型模式,主要針對(duì)一組算法钉凌,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類中咧最,從而使得它們可以相互替換。策略模式使得算法可以在不影響 到客戶端的情況下發(fā)生變化御雕。通常矢沿,策略模式適用于當(dāng)一個(gè)應(yīng)用程序需要實(shí)現(xiàn)一種特定的服務(wù)或者功能,而且該程序有多種實(shí)現(xiàn)方式時(shí)使用酸纲。
策略模式的優(yōu)缺點(diǎn)
策略模式是一種常用且有效的得設(shè)計(jì)模式捣鲸,
優(yōu)點(diǎn)
· 減少重復(fù)代碼 策略模式利用組合、委托和多態(tài)等技術(shù)和思想福青,減少很多模板代碼。
· 擴(kuò)展性 策略模式提供了對(duì)開放-封閉原則的完美支持脓诡,將算法封裝在獨(dú)立的策略類里面无午,使它們易于切換,易于理解祝谚,易于擴(kuò)展宪迟。
· 在策略模式中的算法利用組合和委托讓Context
擁有執(zhí)行算法的能力,這也是繼承的一種更輕便的替換方案交惯。
缺點(diǎn)
· 使用策略模式會(huì)的程序增加了許多策略類或者策略對(duì)象次泽,但實(shí)際上這比把他們負(fù)責(zé)的邏輯堆砌在Context
中要更好。
· 要使用策略模式席爽,必須了解所有的策略意荤,必須了解各個(gè)策略之間的不同點(diǎn),因?yàn)楦鞣N算法之間相互獨(dú)立只锻,對(duì)于復(fù)雜的算法處理相同的邏輯無法實(shí)現(xiàn)共享玖像。
參考
本文參考<<javascript 設(shè)計(jì)模式與開發(fā)實(shí)踐>> 推薦閱讀。