策略模式的定義是:定義一系列的算法,把它們一個個封裝起來,而且使它們能夠相互替換。俗話說,條條大路通羅馬。在程序設計中,咱們也經常遇到相似的狀況,要實現某一個功能有多種方案能夠選擇。好比一個壓縮文件的程序,既能夠選擇zip算法,也能夠選擇 gzip 算法。算法
以公司發年終獎爲例:不少公司的年終獎是根據員工的工資基數和年末績效狀況來發放的。例如,績效爲 S 的人年 終獎有 4 倍工資,績效爲 A的人年終獎有3倍工資,而績效爲 B 的人年終獎是 2 倍工資。假設財務部要求咱們提供一段代碼,來方便他們計算員工的年終獎。設計模式
// 普通實現
function calcBonus({level, salary}) {
if (level === 'S') {
return salary * level * 4;
}
if (level === 'A') {
return salary * level * 3;
}
if (level === 'B') {
return salary * level * 2;
}
}
複製代碼
這個函數的缺點, 1. 若是狀況複雜的話可能會有多個if else; 2. 缺少彈性, 若是須要 增長一個新的level C 而且把S的level調成5 須要深刻代碼邏輯, 才能修改, 3. 代碼沒法複用bash
const strategies = {
'S': (salary) => salary * 4,
'A': (salary) => salary * 3,
'B': (salary) => salay * 2,
}
const calculateBonus = ({level, salary}) => strategies[level](salary);
const t1 = calculateBonus({ level: 'S', salary: 20000 });
console.log('t1 => ',t1);
複製代碼
經過使用策略模式重構代碼,咱們消除了原程序中大片的條件分支語句。全部跟計算獎金有 關的邏輯再也不放在 Context 中,而是分佈在各個策略對象中。Context 並無計算獎金的能力,而是把這個職責委託給了某個策略對象。每一個策略對象負責的算法已被各自封裝在對象內部。當咱們對這些策略對象發出「計算獎金」的請求時,它們會返回各自不一樣的計算結果,這正是對象多態性的體現,也是「它們能夠相互替換」的目的。替換 Context 中當前保存的策略對象,便能執行不一樣的算法來獲得咱們想要的結果。數據結構
// 數據結構
[{
strategy: 'minLength:6',
errorMsg: '用戶名長度不能小於 10 位',
}]
複製代碼
基本代碼實現dom
import isString from 'lodash/isString';
// 策略對象
const strategies = {
isNonEmpty: (value, errMsg) => {
if (value === '') {
return errMsg;
}
},
minLength: (value, length, errMsg) => {
if (val.length < length) {
return errMsg;
}
},
isMobil: (value, errMsg) => {
var pattern = /^1[3|5|8][0-9]{9}$/ig;
if (!pattern.text(value)) {
return errMsg;
}
},
};
// 校驗器對象
function Validator() {
this.cache = []; // 保存校驗的函數
}
Validator.prototype.add = function(dom, rules) {
const self = this;
for(let i=0; i<rules.length; i++) {
const rule = rules[i];
const { strategy, errorMsg } = rule;
const strategyArr = isString(strategy) && strategy.spilt(':');
self.cache.push((() => {
const strategy = strategyArr.shift();
strategyArr.unshift(dom.value);
strategyArr.push(errorMsg);
return strategies[strategy].call(dom, ...strategyArr);
}))
}
}
Validator.prototype.valid = function() {
let errorMsg;
this.cache.every((item, index) => {
const result = item();
if (result) {
errorMsg = result;
return false;
}
});
return errorMsg;
}
複製代碼
客戶端調用方式函數
// 客戶端調用方式
const userInput = document.getElementById('userInput');
const form = document.getElementById('form');
function validForm() {
const v1 = new Validator();
v1.add( userInput, [
{
strategy: 'isNonEmpty',
errorMsg: '用戶名不能爲空'
}, {
strategy: 'minLength:6',
errorMsg: '用戶名長度不能小於 10 位'
}]
);
const errorMsg = v1.valid();
return errorMsg;
}
form.onsubmit = function() {
const errorMsg = validForm();
if (errorMsg) {
alert(errorMsg);
return false;
}
}
複製代碼
// 校驗器對象代碼改進
class Validator {
constructor() {
this.cache = []; // 保存校驗的函數
}
add = (dom, rules) => {
for(let i=0; i<rules.length; i++) {
const rule = rules[i];
const { strategy, errorMsg } = rule;
const strategyArr = isString(strategy) && strategy.spilt(':');
this.cache.push((() => {
const strategy = strategyArr.shift();
strategyArr.unshift(dom.value);
strategyArr.push(errorMsg);
return strategies[strategy].call(dom, ...strategyArr);
}))
}
}
valid = () => {
let errorMsg;
this.cache.every((item, index) => {
const result = item();
if (result) {
errorMsg = result;
return false;
}
});
return errorMsg;
}
};
複製代碼
策略模式是一種經常使用且有效的設計模式,也具備優勢和缺點ui