# javascript 設計模式-- 策略模式

在《javascript設計模式》中,作者并沒有向我們介紹策略模式,然而它卻是一種在開發中十分常見的設計模式。最常見的就是當我們遇到一個復雜的表單驗證的時候,常常需要編寫一大段的if和else邏輯代碼,這些代碼維護起來非常麻煩,但是麻煩的事情遠遠不止于此。通常一個項目中不止涉及單個的表單或者數據的認證,他們往往成群結隊地出現。所以一開始為了他們而編寫的if和else邏輯代碼不僅會顯得非常臃腫,而且隨著項目的擴展,使你的代碼或變得越來越黏糊,就像。。。。。。。是的,就像意大利面條一樣!為了改動小小的一個邏輯——姓名從以前的三個字的限制變成現在四個字限制——,為了這個,你必須把整個項目的所有關于驗證的界面都查找替換個遍。但是如果你一開始采用合理的設計模式來設計你的代碼的話,或許結果就并不是在漆黑的夜苦苦地加班了。在這里,我們以一個簡簡單單的數據類型為例,來了解一下我們的策略模式是怎么樣幫助我們工作的。
```
var data = {
firstName : '曉薇兒',
lastName : '被瑞爾',
age : 100,
sex : 0,
adress : 'stree 12th',
phone : '15988563324',
boss : true
}
```
這是我們要驗證的數據源,我們限制條件如下
1. fistName和lastName字段不能為空并且長度小于三;
2. age字段不能大于100,
3. 電話號碼必須是正確的
4. 地址長度必須限制在200個字符以內
5. 是不是老板這個嘛,只能告訴我們是true和false,也就是只能是boolean類型
傳統的驗證做法:
```
function g() {
if(data.firstName!="" && data.length < 4) {
return false;
}
if(data.lastName !="" && data.length <4) {
return false;
}
......................//此處省略N多行
}
```
這還只是一個數據源的驗證代碼,隨著數據源的增加,代碼會不會龐大起來?
下面讓我們來看看策略模式是怎么樣實現的
```
var voidValue = {//策略者
C : {},//默認配置規則
M : {//未通過驗證時輸出的信息
isEmpty : 'EMPTY',
isPhone : 'NOTPHONE',
isBoolean : 'NOTBOOLEAN',
isLength : 'BIGGER THAN MAX',
isUndefined : 'UNDEFINED',
isNumber : 'NOTNUMBER'
},
R : {//自定義規則,所有的規則在里面逐步添加
isEmpty : function(v) {
return v != '';
},
isUndefined : function(v) {
return typeof v === 'undefined';
},
isPhone : function(v) {
return /^1[3|4|5|8]\d{9}$/.test(v);
},
isBoolean : function(v) {
return Object.prototype.toString.call(v) === '[object Boolean]';
},
isNumber : function(v) {
return Object.prototype.toString.call(v) === '[object Number]';
},
isName : function(v) {
return /^[\u4E00-\u9FFF]{1,6}$/.test(v);
},<br> isAdress : function(v) {<br> return this.isEmpty(v) && v.length<200;<br> }<br>
},
vaild : function(d) {//入口函數,傳入數據源
for(var i in d) {//循環傳入的對象
if(!this.C[i]) continue;
if(this.C[i].fn) {//判斷是否有用戶自定義輸出的字符串,這里其實是經常用到的,比如某個字段沒有通過驗證需要怎么樣提示,以及提示的文字,在驗證表單是尤其重要!
var fn = this.R[this.C[i]['fn']] || this.C[i]['fn'], message = this.C[i]['tip'];
}else {
var fn = this.R[this.C[i]] || this.C[i], message = this.M[this.C[i]];
}
var t = Object.prototype.toString.call(fn);//這里我們判斷是需要執行驗證函數還是比對數值大小
if(t === '[object Function]') {
if(!fn(d[i])) {
console.warn(message);
return false;
}
}else if(t === '[object Number]') {
if(!/\d+/.test(d[i]) || parseInt(d[i]) >= fn) {
console.warn(message);
return false;
}
}
}
return d;//如果都匹配到了,可以輸出完整的數據源對象。
}
}
```
我們來看下他如何調用的:
```
voidValue.C = {//C是公開的對象,外面可以任意改變其值,是為了針對多種多樣的驗證邏輯。
firstName : {fn : 'isName', tip : '請填寫正確的姓名'},
//最外層的字段必須是和數據源字段一一對應的,里面的對象第一個參數fn是需要驗證的方法,第二個是未通過該方法時提示的文字。
lastName : {fn : 'isName', tip : '請填寫正確的姓名'},
age : {fn : 122, tip : '您是彭祖嗎?這么高齡!'},
adress : {fn : 'isAdress', tip : '請輸入正確的地址'},
phone : {fn : 'isPhone', tip : '請填寫正確的手機號碼'},
boss : {fn : 'isBoolean', tip : '你確定你是老板媽媽嗎?'},
}
```
首先你要做的是改變這個策略者的驗證邏輯,如果之后的某個字段驗證邏輯一樣的話,就不必再寫一次了。下面就是簡簡單單有一句調用入口函數
```
var m = voidValue.vaild(data);
```
如果一切都順利,就會放回data給m了。如果不順利,也就是說某個字段未通過驗證,那么m === false;