歡迎光臨
每天分享高質量文章

理解javascript中的策略樣式

作者:塗根華的部落格

網址:http://www.cnblogs.com/tugenhua0707/p/4722696.html

策略樣式的定義是:定義一系列的演演算法,把它們一個個封裝起來,並且使它們可以相互替換。

使用策略樣式的優點如下:

優點:1. 策略樣式利用組合,委託等技術和思想,有效的避免很多if條件陳述句。

2. 策略樣式提供了開放-封閉原則,使程式碼更容易理解和擴充套件。

3. 策略樣式中的程式碼可以復用。

一:使用策略樣式計算獎金;

下麵的demo是我在書上看到的,但是沒有關係,我們只是來理解下策略樣式的使用而已,我們可以使用策略樣式來計算獎金問題;

比如公司的年終獎是根據員工的工資和績效來考核的,績效為A的人,年終獎為工資的4倍,績效為B的人,年終獎為工資的3倍,績效為C的人,年終獎為工資的2倍;現在我們使用一般的編碼方式會如下這樣編寫程式碼:

var calculateBouns = function(salary,level) {

if(level === ‘A’) {

return salary * 4;

}

if(level === ‘B’) {

return salary * 3;

}

if(level === ‘C’) {

return salary * 2;

}

};

// 呼叫如下:

console.log(calculateBouns(4000,’A’)); // 16000

console.log(calculateBouns(2500,’B’)); // 7500

第一個引數為薪資,第二個引數為等級;

程式碼缺點如下:

  1. calculateBouns 函式包含了很多if-else陳述句。
  2. calculateBouns 函式缺乏彈性,假如還有D等級的話,那麼我們需要在calculateBouns 函式內新增判斷等級D的if陳述句;
  3. 演演算法復用性差,如果在其他的地方也有類似這樣的演演算法的話,但是規則不一樣,我們這些程式碼不能通用。

2. 使用組合函式重構程式碼

組合函式是把各種演演算法封裝到一個個的小函式裡面,比如等級A的話,封裝一個小函式,等級為B的話,也封裝一個小函式,以此類推;如下程式碼:

var performanceA = function(salary) {

return salary * 4;

};

var performanceB = function(salary) {

return salary * 3;

};

var performanceC = function(salary) {

return salary * 2

};

var calculateBouns = function(level,salary) {

if(level === ‘A’) {

return performanceA(salary);

}

if(level === ‘B’) {

return performanceB(salary);

}

if(level === ‘C’) {

return performanceC(salary);

}

};

// 呼叫如下

console.log(calculateBouns(‘A’,4500)); // 18000

程式碼看起來有點改善,但是還是有如下缺點:

calculateBouns 函式有可能會越來越大,比如增加D等級的時候,而且缺乏彈性。

3. 使用策略樣式重構程式碼

策略樣式指的是 定義一系列的演演算法,把它們一個個封裝起來,將不變的部分和變化的部分隔開,實際就是將演演算法的使用和實現分離出來;演演算法的使用方式是不變的,都是根據某個演演算法取得計算後的獎金數,而演演算法的實現是根據績效對應不同的績效規則;

一個基於策略樣式的程式至少由2部分組成,第一個部分是一組策略類,策略類封裝了具體的演演算法,並負責具體的計算過程。第二個部分是環境類Context,該Context接收客戶端的請求,隨後把請求委託給某一個策略類。我們先使用傳統面向物件來實現;

如下程式碼:

var performanceA = function(){};

performanceA.prototype.calculate = function(salary) {

return salary * 4;

};

var performanceB = function(){};

performanceB.prototype.calculate = function(salary) {

return salary * 3;

};

var performanceC = function(){};

performanceC.prototype.calculate = function(salary) {

return salary * 2;

};

// 獎金類

var Bouns = function(){

this.salary = null; // 原始工資

this.levelObj = null; // 績效等級對應的策略物件

};

Bouns.prototype.setSalary = function(salary) {

this.salary = salary; // 儲存員工的原始工資

};

Bouns.prototype.setlevelObj = function(levelObj){

this.levelObj = levelObj; // 設定員工績效等級對應的策略物件

};

// 取得獎金數

Bouns.prototype.getBouns = function(){

// 把計算獎金的操作委託給對應的策略物件

return this.levelObj.calculate(this.salary);

};

var bouns = new Bouns();

bouns.setSalary(10000);

bouns.setlevelObj(new performanceA()); // 設定策略物件

console.log(bouns.getBouns()); // 40000

bouns.setlevelObj(new performanceB()); // 設定策略物件

console.log(bouns.getBouns()); // 30000

如上程式碼使用策略樣式重構程式碼,可以看到程式碼職責更新分明,程式碼變得更加清晰。

4. Javascript版本的策略樣式

程式碼如下:

var obj = {

“A”: function(salary) {

return salary * 4;

},

“B” : function(salary) {

return salary * 3;

},

“C” : function(salary) {

return salary * 2;

}

};

var calculateBouns =function(level,salary) {

return obj[level](salary);

};

console.log(calculateBouns(‘A’,10000)); // 40000

可以看到程式碼更加簡單明瞭;

策略樣式指的是定義一系列的演演算法,並且把它們封裝起來,但是策略樣式不僅僅只封裝演演算法,我們還可以對用來封裝一系列的業務規則,只要這些業務規則標的一致,我們就可以使用策略樣式來封裝它們;

表單效驗

比如我們經常來進行表單驗證,比如註冊登入對話方塊,我們登入之前要進行驗證操作:比如有以下幾條邏輯:

  1. 使用者名稱不能為空
  2. 密碼長度不能小於6位。
  3. 手機號碼必須符合格式。

比如HTML程式碼如下:

我們正常的編寫表單驗證程式碼如下:

var registerForm = document.getElementById(“registerForm”);

registerForm.onsubmit = function(){

if(registerForm.userName.value === ”) {

alert(‘使用者名稱不能為空’);

return;

}

if(registerForm.password.value.length < 6) {

alert(“密碼的長度不能小於6位”);

return;

}

if(!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {

alert(“手機號碼格式不正確”);

return;

}

}

但是這樣編寫程式碼有如下缺點:

  1. registerForm.onsubmit 函式比較大,程式碼中包含了很多if陳述句;
  2. registerForm.onsubmit 函式缺乏彈性,如果增加了一種新的效驗規則,或者想把密碼的長度效驗從6改成8,我們必須改registerForm.onsubmit 函式內部的程式碼。違反了開放-封閉原則。
  3. 演演算法的復用性差,如果在程式中增加了另外一個表單,這個表單也需要進行一些類似的效驗,那麼我們可能又需要複製程式碼了;

下麵我們可以使用策略樣式來重構表單效驗;

第一步我們先來封裝策略物件;如下程式碼:

var strategy = {

isNotEmpty: function(value,errorMsg) {

if(value === ”) {

return errorMsg;

}

},

// 限制最小長度

minLength: function(value,length,errorMsg) {

if(value.length < length) {

return errorMsg;

}

},

// 手機號碼格式

mobileFormat: function(value,errorMsg) {

if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {

return errorMsg;

}

}

};

接下來我們準備實現Validator類,Validator類在這裡作為Context,負責接收使用者的請求並委託給strategy 物件,如下程式碼:

var Validator = function(){

this.cache = []; // 儲存效驗規則

};

Validator.prototype.add = function(dom,rule,errorMsg) {

var str = rule.split(“:”);

this.cache.push(function(){

// str 傳回的是 minLength:6

var strategy = str.shift();

str.unshift(dom.value); // 把input的value新增進引數串列

str.push(errorMsg); // 把errorMsg新增進引數串列

return strategys[strategy].apply(dom,str);

});

};

Validator.prototype.start = function(){

for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {

var msg = validatorFunc(); // 開始效驗 並取得效驗後的傳回信息

if(msg) {

return msg;

}

}

};

Validator類在這裡作為Context,負責接收使用者的請求並委託給strategys物件。上面的程式碼中,我們先建立一個Validator物件,然後透過validator.add方法往validator物件中新增一些效驗規則,validator.add方法接收3個引數,如下程式碼:

validator.add(registerForm.password,’minLength:6′,’密碼長度不能小於6位’);

registerForm.password 為效驗的input輸入框dom節點;

minLength:6: 是以一個冒號隔開的字串,冒號前面的minLength代表客戶挑選的strategys物件,冒號後面的數字6表示在效驗過程中所必須驗證的引數,minLength:6的意思是效驗 registerForm.password 這個文字輸入框的value最小長度為6位;如果字串中不包含冒號,說明效驗過程中不需要額外的效驗資訊;

第三個引數是當效驗未透過時傳回的錯誤資訊;

當我們往validator物件裡新增完一系列的效驗規則之後,會呼叫validator.start()方法來啟動效驗。如果validator.start()傳回了一個errorMsg字串作為傳回值,說明該次效驗沒有透過,此時需要registerForm.onsubmit方法傳回false來阻止表單提交。下麵我們來看看初始化程式碼如下:

var validateFunc = function(){

var validator = new Validator(); // 建立一個Validator物件

/* 新增一些效驗規則 */

validator.add(registerForm.userName,’isNotEmpty’,’使用者名稱不能為空’);

validator.add(registerForm.password,’minLength:6′,’密碼長度不能小於6位’);

validator.add(registerForm.userName,’mobileFormat’,’手機號碼格式不正確’);

var errorMsg = validator.start(); // 獲得效驗結果

return errorMsg; // 傳回效驗結果

};

var registerForm = document.getElementById(“registerForm”);

registerForm.onsubmit = function(){

var errorMsg = validateFunc();

if(errorMsg){

alert(errorMsg);

return false;

}

}

下麵是所有的程式碼如下:

var strategys = {

isNotEmpty: function(value,errorMsg) {

if(value === ”) {

return errorMsg;

}

},

// 限制最小長度

minLength: function(value,length,errorMsg) {

if(value.length < length) {

return errorMsg;

}

},

// 手機號碼格式

mobileFormat: function(value,errorMsg) {

if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {

return errorMsg;

}

}

};

var Validator = function(){

this.cache = []; // 儲存效驗規則

};

Validator.prototype.add = function(dom,rule,errorMsg) {

var str = rule.split(“:”);

this.cache.push(function(){

// str 傳回的是 minLength:6

var strategy = str.shift();

str.unshift(dom.value); // 把input的value新增進引數串列

str.push(errorMsg); // 把errorMsg新增進引數串列

return strategys[strategy].apply(dom,str);

});

};

Validator.prototype.start = function(){

for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {

var msg = validatorFunc(); // 開始效驗 並取得效驗後的傳回信息

if(msg) {

return msg;

}

}

};

var validateFunc = function(){

var validator = new Validator(); // 建立一個Validator物件

/* 新增一些效驗規則 */

validator.add(registerForm.userName,’isNotEmpty’,’使用者名稱不能為空’);

validator.add(registerForm.password,’minLength:6′,’密碼長度不能小於6位’);

validator.add(registerForm.userName,’mobileFormat’,’手機號碼格式不正確’);

var errorMsg = validator.start(); // 獲得效驗結果

return errorMsg; // 傳回效驗結果

};

var registerForm = document.getElementById(“registerForm”);

registerForm.onsubmit = function(){

var errorMsg = validateFunc();

if(errorMsg){

alert(errorMsg);

return false;

}

};

如上使用策略樣式來編寫表單驗證程式碼可以看到好處了,我們透過add配置的方式就完成了一個表單的效驗;這樣的話,那麼程式碼可以當做一個元件來使用,並且可以隨時呼叫,在修改表單驗證規則的時候,也非常方便,透過傳遞引數即可呼叫;

給某個文字輸入框新增多種效驗規則

上面的程式碼我們可以看到,我們只是給輸入框只能對應一種效驗規則,比如上面的我們只能效驗輸入框是否為空,

validator.add(registerForm.userName,’isNotEmpty’,’使用者名稱不能為空’);但是如果我們既要效驗輸入框是否為空,還要效驗輸入框的長度不要小於10位的話,那麼我們期望需要像如下傳遞引數:

validator.add(registerForm.userName,[{strategy:’isNotEmpty’,errorMsg:’使用者名稱不能為空’},{strategy: ‘minLength:6′,errorMsg:’使用者名稱長度不能小於6位’}])

我們可以編寫程式碼如下:

// 策略物件

var strategys = {

isNotEmpty: function(value,errorMsg) {

if(value === ”) {

return errorMsg;

}

},

// 限制最小長度

minLength: function(value,length,errorMsg) {

if(value.length < length) {

return errorMsg;

}

},

// 手機號碼格式

mobileFormat: function(value,errorMsg) {

if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {

return errorMsg;

}

}

};

var Validator = function(){

this.cache = []; // 儲存效驗規則

};

Validator.prototype.add = function(dom,rules) {

var self = this;

for(var i = 0, rule; rule = rules[i++]; ){

(function(rule){

var strategyAry = rule.strategy.split(“:”);

var errorMsg = rule.errorMsg;

self.cache.push(function(){

var strategy = strategyAry.shift();

strategyAry.unshift(dom.value);

strategyAry.push(errorMsg);

return strategys[strategy].apply(dom,strategyAry);

});

})(rule);

}

};

Validator.prototype.start = function(){

for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {

var msg = validatorFunc(); // 開始效驗 並取得效驗後的傳回信息

if(msg) {

return msg;

}

}

};

// 程式碼呼叫

var registerForm = document.getElementById(“registerForm”);

var validateFunc = function(){

var validator = new Validator(); // 建立一個Validator物件

/* 新增一些效驗規則 */

validator.add(registerForm.userName,[

{strategy: ‘isNotEmpty’,errorMsg:’使用者名稱不能為空’},

{strategy: ‘minLength:6′,errorMsg:’使用者名稱長度不能小於6位’}

]);

validator.add(registerForm.password,[

{strategy: ‘minLength:6′,errorMsg:’密碼長度不能小於6位’},

]);

validator.add(registerForm.phoneNumber,[

{strategy: ‘mobileFormat’,errorMsg:’手機號格式不正確’},

]);

var errorMsg = validator.start(); // 獲得效驗結果

return errorMsg; // 傳回效驗結果

};

// 點選確定提交

registerForm.onsubmit = function(){

var errorMsg = validateFunc();

if(errorMsg){

alert(errorMsg);

return false;

}

}

註意:如上程式碼都是按照書上來做的,都是看到書的程式碼,最主要我們理解策略樣式實現,比如上面的表單驗證功能是這樣封裝的程式碼,我們平時使用jquery外掛表單驗證程式碼原來是這樣封裝的,為此我們以後也可以使用這種方式來封裝表單等學習。

贊(0)

分享創造快樂