前言:語言只是工具,思想才是核心
今天要總結的是 策略模式
策略在開發中的應用非常廣泛,所以也是非常常見且使用的設計模式。
在實際開發中,往往在實現一個功能時,有多種解決方案可行。
常見場景:
- 解壓:gzip演算法解壓,gzip演算法解壓
- 表單驗證:手機號驗證,郵箱驗證,密碼格式驗證
- 工資計算:技術猛男工資,鼓勵師妹妹工資,冷血產品經理工資
總結策略模式的使用場景,在想實現某個效果時,有多種不同的達成方式。這個時候就要考慮策略模式(如果你寫了很多ifelse)
使用策略模式的優點:
- 1.使程式碼更加清晰可讀,減少繁多的if-else
- 2.功能更易擴充,可維護,健壯性【單一指責,抽離變與不變。如果你看了上篇單例設計模式你就看到我也強調過這一點】
- 3.策略可重用
策略模式的應用場景:
場景一:
工資計算
財務妹妹想要給技術猛男、鼓勵師妹妹、冷血產品經理來統計他們本月的工資。但他們的工資計算方式不一樣呀,這可咋整?
- 技術猛男:(10000基本+1000專案獎勵)* 1.2績效
- 鼓勵師妹妹:(9000基本)*1績效
- 冷血產品經理:(500基本)*1.5績效
- 【績效每月都不同】
本著抽離變與不變的思路:
- 變:不同角色不同計算方式
- 不變:都需要計算工資
面對物件思路的寫法:
<script>
// 策略類 定義各個角色的工資計算方式
function WageStrategy() {
}
WageStrategy.prototype.ROLE = {
IT: 'itHandsomeBoy',
ENCOURAGE: 'encourager',
MANAGER: 'productManager',
};
WageStrategy.prototype.itHandsomeBoy = function (performance) {
return (10000 + 2000) * performance;
}
WageStrategy.prototype.encourager = function (performance) {
return (9000) * performance;
}
WageStrategy.prototype.productManager = function (performance) {
return (500) * performance;
}
WageStrategy.prototype.getRoleWage = function (role, performance) {
return this[role](performance);
}
// 中間類 (發起計算的財務)【持有策略物件】
function FinancialMan(strategy) {
this.strategy = strategy;
}
FinancialMan.prototype.getWage = function (role, performance) {
return this.strategy.getRoleWage(role, performance);
};
// 執行
let financialMan = new FinancialMan(new WageStrategy());
console.log(financialMan.getWage(financialMan.strategy.ROLE.IT, 1.2));
console.log(financialMan.getWage(financialMan.strategy.ROLE.ENCOURAGE, 1));
console.log(financialMan.getWage(financialMan.strategy.ROLE.MANAGER, 1.5));
</script>
上面是模擬像Java這樣基於類的語言,而js中存在很多不一樣的特性,例如:函式即物件!所以以下是改進的
Js版本的策略模式。
<script>
// 策略物件,封裝工資演算法
let wageStrategy = {
ROLE : {
IT : 'itHansomeBoy',
ENCOURAGE : 'encourager',
MANAGER : 'productManager',
},
itHansomeBoy: function (performance) {
return (10000 + 2000) * performance;
},
encourager: function (performance) {
return (9000) * performance;
},
productManager: function (performance) {
return (500) * performance;
},
};
// 計算工資物件
let getWage= function (role, performance) {
return wageStrategy[role](performance);
}
console.log(getWage(wageStrategy.ROLE.IT, 1.2)); // 14400
console.log(getWage(wageStrategy.ROLE.ENCOURAGE, 1)); // 9000
console.log(getWage(wageStrategy.ROLE.MANAGER, 1.5)); // 750
</script>
場景二:
表單驗證(註冊賬號)
使用者在填完一堆資訊後【如:手機號、郵箱號、姓名、密碼、驗證碼】,點選註冊,此時應該先對每個欄位進行檢查,都通過後才能提交到後臺。
比較猛的寫法:
let register = function () {
let name, phone,
if(!name) {
return;
}else if (!phone || phone.length !== 11) {
return;
}
// do register request
}
隨著需要檢驗的欄位越來越多,那麼else if的邏輯越來越重。並且,如果有個完善資訊頁面,同樣需要用到手機號,郵箱號這些資訊檢測怎麼複用勒?
那麼,策略模式來了:
<script>
// 表單檢驗策略類
let checkInfoStrategy = {
phone: function (phone) {
let pass = true;
let tips = '';
if (!phone) {
pass = false;
tips = '手機號不能為空';
}
return { pass, tips };
},
email: function (email) {
let pass = true;
let tips = '';
if (!email) {
pass = false;
tips = '郵箱不能為空';
} else if (email.indexOf('@') < 0) {
pass = false;
tips = '郵箱格式不正確';
}
return { pass, tips };
}
}
// 中間者 發起表單檢驗
let Checker = function () {
this.cache = []; // 存放需要檢驗項:{策略,待檢驗字元}
// 新增待檢查項
this.add = function (stragetyItem, beCheckInfo) {
this.cache.push({ item: stragetyItem, info: beCheckInfo });
return this;
};
// 開始檢查
this.startCheck = function () {
let result = { pass : true, tips : '' };
for (let i = 0; i < this.cache.length; i++) {
let checkItem = this.cache[i];
let {pass, tips} = checkItem.item(checkItem.info);
result.pass = pass;
result.tips = tips;
if (!pass) {
break;
}
}
return result;
};
}
// 執行
let phone = '18826274139';
let email = '';
let checker = new Checker();
checker.add(checkInfoStrategy.phone, phone).add(checkInfoStrategy.email, email);
let { pass, tips } = checker.startCheck();
console.log(':::' + pass, tips); // :::false 郵箱格式不正確
</script>
其他不多說了,一定要動過。
思路就是抽離變與不變。策略類之放不同的演算法/邏輯,使用中間類來協助發起策略類的使用。