策略模式定義了算法家族,分別封裝起來(lái),讓他們之間可以互相替換,此模式讓算法的變化不會(huì)影響到使用算法的客戶。
在理解策略模式之前,我們先來(lái)一個(gè)例子,一般情況下,如果我們要做數(shù)據(jù)合法性驗(yàn)證,很多時(shí)候都是按照swith語(yǔ)句來(lái)判斷,但是這就帶來(lái)幾個(gè)問(wèn)題,首先如果增加需求的話,我們還要再次修改這段代碼以增加邏輯,而且在進(jìn)行單元測(cè)試的時(shí)候也會(huì)越來(lái)越復(fù)雜,代碼如下:
validator = { validate: function (value, type) { switch (type) { case 'isNonEmpty ': { return true; // NonEmpty 驗(yàn)證結(jié)果 } case 'isNumber ': { return true; // Number 驗(yàn)證結(jié)果 break; } case 'isAlphaNum ': { return true; // AlphaNum 驗(yàn)證結(jié)果 } default: { return true; } } } }; // 測(cè)試 alert(validator.validate("123", "isNonEmpty"));
那如何來(lái)避免上述代碼中的問(wèn)題呢,根據(jù)策略模式,我們可以將相同的工作代碼單獨(dú)封裝成不同的類(lèi),然后通過(guò)統(tǒng)一的策略處理類(lèi)來(lái)處理,OK,我們先來(lái)定義策略處理類(lèi),代碼如下:
var validator = { // 所有可以的驗(yàn)證規(guī)則處理類(lèi)存放的地方,后面會(huì)單獨(dú)定義 types: {}, // 驗(yàn)證類(lèi)型所對(duì)應(yīng)的錯(cuò)誤消息 messages: [], // 當(dāng)然需要使用的驗(yàn)證類(lèi)型 config: {}, // 暴露的公開(kāi)驗(yàn)證方法 // 傳入的參數(shù)是 key => value對(duì) validate: function (data) { var i, msg, type, checker, result_ok; // 清空所有的錯(cuò)誤信息 this.messages = []; for (i in data) { if (data.hasOwnProperty(i)) { type = this.config[i]; // 根據(jù)key查詢是否有存在的驗(yàn)證規(guī)則 checker = this.types[type]; // 獲取驗(yàn)證規(guī)則的驗(yàn)證類(lèi) if (!type) { continue; // 如果驗(yàn)證規(guī)則不存在,則不處理 } if (!checker) { // 如果驗(yàn)證規(guī)則類(lèi)不存在,拋出異常 throw { name: "ValidationError", message: "No handler to validate type " + type }; } result_ok = checker.validate(data[i]); // 使用查到到的單個(gè)驗(yàn)證類(lèi)進(jìn)行驗(yàn)證 if (!result_ok) { msg = "Invalid value for " + i + ", " + checker.instructions; this.messages.push(msg); } } } return this.hasErrors(); }, // helper hasErrors: function () { return this.messages.length !== 0; } };
然后剩下的工作,就是定義types里存放的各種驗(yàn)證類(lèi)了,我們這里只舉幾個(gè)例子:
// 驗(yàn)證給定的值是否不為空 validator.types.isNonEmpty = { validate: function (value) { return value !== ""; }, instructions: "傳入的值不能為空" }; // 驗(yàn)證給定的值是否是數(shù)字 validator.types.isNumber = { validate: function (value) { return !isNaN(value); }, instructions: "傳入的值只能是合法的數(shù)字,例如:1, 3.14 or 2010" }; // 驗(yàn)證給定的值是否只是字母或數(shù)字 validator.types.isAlphaNum = { validate: function (value) { return !/a-z0-9/i.test(value); }, instructions: "傳入的值只能保護(hù)字母和數(shù)字,不能包含特殊字符" };
使用的時(shí)候,我們首先要定義需要驗(yàn)證的數(shù)據(jù)集合,然后還需要定義每種數(shù)據(jù)需要驗(yàn)證的規(guī)則類(lèi)型,代碼如下:
var data = { first_name: "Tom", last_name: "Xu", age: "unknown", username: "TomXu" }; validator.config = { first_name: 'isNonEmpty', age: 'isNumber', username: 'isAlphaNum' };
最后,獲取驗(yàn)證結(jié)果的代碼就簡(jiǎn)單了:
validator.validate(data); if (validator.hasErrors()) { console.log(validator.messages.join("\n")); }
策略模式定義了一系列算法,從概念上來(lái)說(shuō),所有的這些算法都是做相同的事情,只是實(shí)現(xiàn)不同,他可以以相同的方式調(diào)用所有的方法,減少了各種算法類(lèi)與使用算法類(lèi)之間的耦合。
從另外一個(gè)層面上來(lái)說(shuō),單獨(dú)定義算法類(lèi),也方便了單元測(cè)試,因?yàn)榭梢酝ㄟ^(guò)自己的算法進(jìn)行單獨(dú)測(cè)試。
實(shí)踐中,不僅可以封裝算法,也可以用來(lái)封裝幾乎任何類(lèi)型的規(guī)則,是要在分析過(guò)程中需要在不同時(shí)間應(yīng)用不同的業(yè)務(wù)規(guī)則,就可以考慮是要策略模式來(lái)處理各種變化。
更多建議: