最近在看《javascript設計模式與開發(fā)實踐》。
var calc = function(level,salary){
if(level === "A"){
return 3*salary
}
if(level === "B"){
return 2*salary
}
if(level === "C"){
return 1*salary
}
}
這段代碼看起來十分簡單,但是隨著后期的拓展,要添加更多的if語句,calc函數(shù)會變得更加龐大,不易于維護。
如果我要修改原有的計算分之,可能每次都要修改calc這個方法。實際上,這個calc這個方法的設計初衷只是為了返回 績效工資。
在整個代碼的其它部分,可能還需要這個計算方法,難道還要copy一份過去么,那么同樣的修改要修改兩塊代碼。
用策略模式來重構(gòu)代碼
策略模式指的是定義一系列的算法,把他們一個個都封裝起來。將不變的部分和變化的部分分割開始每個設計模式的主題,策略模式的目的就是將算法的使用與算法的實現(xiàn)分離開來。
再說上邊的需求,我們簡單思考下,其實變化的部分就是員工的等級和工資,而我們的目的就是返回績效工資這部分是不變的
// 根據(jù)員工的level和salary 金額的計算
var straigies = {
“A”: function(salary){
return fn*3
},
“B”: function(salary){
return fn*2
},
“C”: function(salary){
return fn*1
}
}
//不變的部分
var calc = function(level,salary){
return straigies[level](salary)
}
// 執(zhí)行
calc("A",5000) // => 15000
比較下兩段代碼,不見了不友好的 if判斷,算法(策略)都放在了straigies 中,后續(xù)需求 只需要維護straigies對象即可。
- 用策略模式來重構(gòu)表單驗證
很多需要提交表單的業(yè)務場景,都需要如下結(jié)構(gòu)的代碼
$('submitBtn').on('click',functioin(){
if($name.value().length < 6){
console.log("用戶名長度不能少于6")
}
if($phone.value() != ''){
console.log("電話號碼不能為空")
}
ajaxFn()
})
隨著 判斷條件的增加,函數(shù)的結(jié)構(gòu)會越來越臃腫龐大,而需要修改判斷的條件,就又要深入指定的if判斷去篩選,單純的新增規(guī)則在這個函數(shù)里,復用性太差。結(jié)合策略模式,可以把if判斷這一部分 函數(shù)里提取出來重新封裝,提高代碼的復用性和可讀性。
對代碼結(jié)構(gòu)的期望
$('submitBtn').on('click',functioin(){
var form = document.querySelector('.form');
var validator = new validator ();
validator.addrules(form.username,"isNonEmpty","不能為空")
validator.addrules(form.phone,"minLength:6","最少為6位")
var erroMsg = validator.start();
if(erroMsg){
alert(validator);
return false
}
ajaxFn()
})
我們先封裝策略類
var stratigies = {
isNonEmpty: function(value,erromsg){
if(value == ""){
return erromsg
}
},
minlength: function(value,length,erromsg){
if(value.length < length){
return erromsg
}
}
}
封裝 驗證(validator類)
var Validator = function(){
this.cache = [] //用來緩存
}
Validator.prototype.add = function(item,rule,erroMsg){
var arg = rule.split(":") // rule= > "isEmpty" => ["isEmpty"] || "minLength:5"=> ["minlength", “5”];
this.cache.push(function(){
var strategy = arg.shift(); // =>"isEmpty" || minLength
arg.unshift(item.value); //[value] || [value,5]
arg.push(errMsg)// [value,errMsg] || [value,5,errMsg]
return stratigies[strategy].apply(item,arg)
})
}
//start方法就是遍歷這個Validator里緩存的方法并執(zhí)行
Validator.prototype.start = function(){
for(var i=0;i<this.cache.length;i++){
var fn = this.cache[i];
var msg = fn() // =>開始校驗,
if(msg){
return msg // 如果msg有值 就說明驗證不通過,跳出循環(huán)
}
}
}
此時,如果需要添加規(guī)則 或是修改驗證規(guī)則,就很簡單了
我們可以在stratigies里添加策略,也可以在 提交表單的函數(shù)里,新增或者修改規(guī)則
validator.addrules(form.phone,"minLength:6") =》 validator.addrules(form.phone,"minLength:10")
很蛋疼的是,對用戶名判斷只能一次輸入一種規(guī)則,如果需要加一種判斷規(guī)則,我就要在add一次strategy,
書中提供了進一步的拓展。
//預期
validator.addrules(form.username,[
{
"erroMsg": "不能為空",
"strategy": 'isNonEmpty'
},
{
"erroMsg": "最小為6位",
"strategy": 'minLength:6'
}
])
將策略用數(shù)組的方式傳入validator對象里,緩存在this.cache里,只需要調(diào)整對應的add方法就可以
//old type
Validator.prototype.add = function(item,rule,erroMsg){
var arg = rule.split(":") // rule= > "isEmpty" => ["isEmpty"] || "minLength:5"=> ["minlength", “5”];
this.cache.push(function(){
var strategy = arg.shift(); // =>"isEmpty" || minLength
arg.unshift(item.value); //[value] || [value,5]
arg.push(errMsg)// [value,errMsg] || [value,5,errMsg]
return stratigies[strategy].apply(item,arg)
})
}
// new Type
Validator.prototype.add = function(item,rules){
var _this = this; //保存this指針 for循環(huán)里的的自執(zhí)行函數(shù)的this指向window,用_this修復指針
//遍歷rules
for(var i = 0;i<rules.length;i++){
var rule = rules[i];
(function(rule ){
var arg = rule.strategy.split(":") // rule= > "isEmpty" => ["isEmpty"] || "minLength:5"=> ["minlength", “5”];
var errMsg = rule.errMsg;
_this.cache.push(function(){
var strategy = arg.shift(); // =>"isEmpty" || minLength
arg.unshift(item.value); //[value] || [value,5]
arg.push(errMsg)// [value,errMsg] || [value,5,errMsg]
return stratigies[strategy].apply(item,arg)
})
})(rule)
}
}