欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

學(xué)習(xí)JavaScript設(shè)計(jì)模式(策略模式)

 更新時(shí)間:2015年11月26日 15:31:47   作者:小平果118  
這篇文章主要帶領(lǐng)大家學(xué)習(xí)JavaScript設(shè)計(jì)模式,其中重點(diǎn)介紹策略模式,以年終獎(jiǎng)為實(shí)例對(duì)策略模式進(jìn)行分析,對(duì)策略模式進(jìn)行詳細(xì)剖析,感興趣的小伙伴們可以參考一下

何為策略?比如我們要去某個(gè)地方旅游,可以根據(jù)具體的實(shí)際情況來(lái)選擇出行的線路。
1、策略模式的定義

如果沒(méi)有時(shí)間但是不在乎錢,可以選擇坐飛機(jī)。
如果沒(méi)有錢,可以選擇坐大巴或者火車。
如果再窮一點(diǎn),可以選擇騎自行車。
在程序設(shè)計(jì)中,我們也常常遇到類似的情況,要實(shí)現(xiàn)某一個(gè)功能有多種方案可以選擇。比如一個(gè)壓縮文件的程序,既可以選擇zip算法,也可以選擇gzip算法。

定義:策略模式定義一系列的算法,分別封裝起來(lái),讓他們之間可以互相替換,此模式讓算法的變化獨(dú)立于使用算飯的客戶.

策略模式有著廣泛的應(yīng)用。本節(jié)我們就以年終獎(jiǎng)的計(jì)算為例進(jìn)行介紹。

2、年終獎(jiǎng)實(shí)例

很多公司的年終獎(jiǎng)是根據(jù)員工的工資基數(shù)和年底績(jī)效情況來(lái)發(fā)放的。例如,績(jī)效為S的人年終獎(jiǎng)有4倍工資,績(jī)效為A的人年終獎(jiǎng)有3倍工資,而績(jī)效為B的人年終獎(jiǎng)是2倍工資。假設(shè)財(cái)務(wù)部要求我們提供一段代碼,來(lái)方便他們計(jì)算員工的年終獎(jiǎng)。

1). 最初的代碼實(shí)現(xiàn)

我們可以編寫(xiě)一個(gè)名為calculateBonus的函數(shù)來(lái)計(jì)算每個(gè)人的獎(jiǎng)金數(shù)額。很顯然,calculateBonus函數(shù)要正確工作,就需要接收兩個(gè)參數(shù):?jiǎn)T工的工資數(shù)額和他的績(jī)效考核等級(jí)。代碼如下:

var calculateBonus = function( performanceLevel, salary ){
 if ( performanceLevel === 'S' ){
 return salary * 4;
 }

 if ( performanceLevel === 'A' ){
 return salary * 3;
 }

 if ( performanceLevel === 'B' ){
 return salary * 2;
 }
};
calculateBonus( 'B', 20000 ); // 輸出:40000
calculateBonus( 'S', 6000 ); // 輸出:24000

可以發(fā)現(xiàn),這段代碼十分簡(jiǎn)單,但是存在著顯而易見(jiàn)的缺點(diǎn)。

calculateBonus函數(shù)比較龐大,包含了很多if-else語(yǔ)句,這些語(yǔ)句需要覆蓋所有的邏輯分支。

calculateBonus函數(shù)缺乏彈性,如果增加了一種新的績(jī)效等級(jí)C,或者想把績(jī)效S的獎(jiǎng)金系數(shù)改為5,那我們必須深入calculateBonus函數(shù)的內(nèi)部實(shí)現(xiàn),這是違反開(kāi)放-封閉原則的。

算法的復(fù)用性差,如果在程序的其他地方需要重用這些計(jì)算獎(jiǎng)金的算法呢?我們的選擇只有復(fù)制和粘貼。因此,我們需要重構(gòu)這段代碼。

2). 使用組合函數(shù)重構(gòu)代碼

一般最容易想到的辦法就是使用組合函數(shù)來(lái)重構(gòu)它,我們把各種算法封裝到一個(gè)個(gè)的小函數(shù)里面,這些小函數(shù)有著良好的命名,可以一目了然地知道它對(duì)應(yīng)著哪種算法,它們也可以被復(fù)用在程序的其他地方。代碼如下:

var performanceS = function( salary ){
 return salary * 4;
};

var performanceA = function( salary ){
 return salary * 3;
};

var performanceB = function( salary ){
 return salary * 2;
};

var calculateBonus = function( performanceLevel, salary ){

 if ( performanceLevel === 'S' ){
 return performanceS( salary );
 }

 if ( performanceLevel === 'A' ){
 return performanceA( salary );
 }

 if ( performanceLevel === 'B' ){
 return performanceB( salary );
 }

};
calculateBonus( 'A' , 10000 ); // 輸出:30000

目前,我們的程序得到了一定的改善,但這種改善非常有限,我們依然沒(méi)有解決最重要的問(wèn)題:calculateBonus函數(shù)有可能越來(lái)越龐大,而且在系統(tǒng)變化的時(shí)候缺乏彈性。

3). 使用策略模式重構(gòu)代碼

經(jīng)過(guò)思考,我們想到了更好的辦法——使用策略模式來(lái)重構(gòu)代碼。策略模式指的是定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái)。將不變的部分和變化的部分隔開(kāi)是每個(gè)設(shè)計(jì)模式的主題,策略模式也不例外,策略模式的目的就是將算法的使用與算法的實(shí)現(xiàn)分離開(kāi)來(lái)。

在這個(gè)例子里,算法的使用方式是不變的,都是根據(jù)某個(gè)算法取得計(jì)算后的獎(jiǎng)金數(shù)額。而算法的實(shí)現(xiàn)是各異和變化的,每種績(jī)效對(duì)應(yīng)著不同的計(jì)算規(guī)則。

一個(gè)基于策略模式的程序至少由兩部分組成。第一個(gè)部分是一組策略類,策略類封裝了具體的算法,并負(fù)責(zé)具體的計(jì)算過(guò)程。 第二個(gè)部分是環(huán)境類Context,Context接受客戶的請(qǐng)求,隨后把請(qǐng)求委托給某一個(gè)策略類。要做到這點(diǎn),說(shuō)明Context中要維持對(duì)某個(gè)策略對(duì)象的引用。

現(xiàn)在用策略模式來(lái)重構(gòu)上面的代碼。第一個(gè)版本是模仿傳統(tǒng)面向?qū)ο笳Z(yǔ)言中的實(shí)現(xiàn)。我們先把每種績(jī)效的計(jì)算規(guī)則都封裝在對(duì)應(yīng)的策略類里面:

var performanceS = function(){};

performanceS.prototype.calculate = function( salary ){
 return salary * 4;
};

var performanceA = function(){};

performanceA.prototype.calculate = function( salary ){
 return salary * 3;
};

var performanceB = function(){};

performanceB.prototype.calculate = function( salary ){
 return salary * 2;
};

接下來(lái)定義獎(jiǎng)金類Bonus:

var Bonus = function(){
 this.salary = null; //原始工資
 this.strategy = null; //績(jī)效等級(jí)對(duì)應(yīng)的策略對(duì)象
};

Bonus.prototype.setSalary = function( salary ){
 this.salary = salary; //設(shè)置員工的原始工資
};

Bonus.prototype.setStrategy = function( strategy ){
 this.strategy = strategy; //設(shè)置員工績(jī)效等級(jí)對(duì)應(yīng)的策略對(duì)象
};

Bonus.prototype.getBonus = function(){ //取得獎(jiǎng)金數(shù)額
 return this.strategy.calculate( this.salary ); //把計(jì)算獎(jiǎng)金的操作委托給對(duì)應(yīng)的策略對(duì)象
};

在完成最終的代碼之前,我們?cè)賮?lái)回顧一下策略模式的思想:

定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái),并且使它們可以相互替換。

這句話如果說(shuō)得更詳細(xì)一點(diǎn),就是:定義一系列的算法,把它們各自封裝成策略類,算法被封裝在策略類內(nèi)部的方法里。在客戶對(duì)Context發(fā)起請(qǐng)求的時(shí)候,Context總是把請(qǐng)求委托給這些策略對(duì)象中間的某一個(gè)進(jìn)行計(jì)算。

“并且使它們可以相互替換”,這句話在很大程度上是相對(duì)于靜態(tài)類型語(yǔ)言而言的。因?yàn)殪o態(tài)類型語(yǔ)言中有類型檢查機(jī)制,所以各個(gè)策略類需要實(shí)現(xiàn)同樣的接口。當(dāng)它們的真正類型被隱藏在接口后面時(shí),它們才能被相互替換。而在JavaScript這種“類型模糊”的語(yǔ)言中沒(méi)有這種困擾,任何對(duì)象都可以被替換使用。因此,JavaScript中的“可以相互替換使用”表現(xiàn)為它們具有相同的目標(biāo)和意圖。

現(xiàn)在我們來(lái)完成這個(gè)例子中剩下的代碼。先創(chuàng)建一個(gè)bonus對(duì)象,并且給bonus對(duì)象設(shè)置一些原始的數(shù)據(jù),比如員工的原始工資數(shù)額。接下來(lái)把某個(gè)計(jì)算獎(jiǎng)金的策略對(duì)象也傳入bonus對(duì)象內(nèi)部保存起來(lái)。當(dāng)調(diào)用bonus.getBonus()來(lái)計(jì)算獎(jiǎng)金的時(shí)候,bonus對(duì)象本身并沒(méi)有能力進(jìn)行計(jì)算,而是把請(qǐng)求委托給了之前保存好的策略對(duì)象:

var bonus = new Bonus();

bonus.setSalary( 10000 );
bonus.setStrategy( new performanceS() ); //設(shè)置策略對(duì)象

console.log( bonus.getBonus() ); // 輸出:40000 

bonus.setStrategy( new performanceA() ); //設(shè)置策略對(duì)象
console.log( bonus.getBonus() ); // 輸出:30000 

剛剛我們用策略模式重構(gòu)了這段計(jì)算年終獎(jiǎng)的代碼,可以看到通過(guò)策略模式重構(gòu)之后,代碼變得更加清晰,各個(gè)類的職責(zé)更加鮮明。但這段代碼是基于傳統(tǒng)面向?qū)ο笳Z(yǔ)言的模仿,下一節(jié)我們將了解用JavaScript實(shí)現(xiàn)的策略模式。

在5.1節(jié)中,我們讓strategy對(duì)象從各個(gè)策略類中創(chuàng)建而來(lái),這是模擬一些傳統(tǒng)面向?qū)ο笳Z(yǔ)言的實(shí)現(xiàn)。實(shí)際上在JavaScript語(yǔ)言中,函數(shù)也是對(duì)象,所以更簡(jiǎn)單和直接的做法是把strategy直接定義為函數(shù):

var strategies = {
 "S": function( salary ){
 return salary * 4;
 },
 "A": function( salary ){
 return salary * 3;
 },
 "B": function( salary ){
 return salary * 2;
 }
}; 

同樣,Context也沒(méi)有必要必須用Bonus類來(lái)表示,我們依然用calculateBonus 函數(shù)充當(dāng)Context來(lái)接受用戶的請(qǐng)求。經(jīng)過(guò)改造,代碼的結(jié)構(gòu)變得更加簡(jiǎn)潔:

var strategies = {
 "S": function( salary ){
 return salary * 4;
 },
 "A": function( salary ){
 return salary * 3;
 },
 "B": function( salary ){
 return salary * 2;
 }
};

var calculateBonus = function( level, salary ){
 return strategies[ level ]( salary );
};

console.log( calculateBonus( 'S', 20000 ) ); // 輸出: 80000
console.log( calculateBonus( 'A', 10000 ) ); // 輸出: 30000

3、實(shí)例再講解

一個(gè)小例子就能讓我們一目了然。
回憶下jquery里的animate方法.

$( div ).animate( {"left: 200px"}, 1000, 'linear' ); 
//勻速運(yùn)動(dòng)
$( div ).animate( {"left: 200px"}, 1000, 'cubic' ); 
//三次方的緩動(dòng)

這2句代碼都是讓div在1000ms內(nèi)往右移動(dòng)200個(gè)像素. linear(勻速)和cubic(三次方緩動(dòng))就是一種策略模式的封裝.

再來(lái)一個(gè)例子. 很多頁(yè)面都會(huì)有個(gè)即時(shí)驗(yàn)證的表單. 表單的每個(gè)成員都會(huì)有一些不同的驗(yàn)證規(guī)則.

比如姓名框里面, 需要驗(yàn)證非空,敏感詞,字符過(guò)長(zhǎng)這幾種情況。 當(dāng)然是可以寫(xiě)3個(gè)if else來(lái)解決,不過(guò)這樣寫(xiě)代碼的擴(kuò)展性和維護(hù)性可想而知。如果表單里面的元素多一點(diǎn),需要校驗(yàn)的情況多一點(diǎn),加起來(lái)寫(xiě)上百個(gè)if else也不是沒(méi)有可能。

所以更好的做法是把每種驗(yàn)證規(guī)則都用策略模式單獨(dú)的封裝起來(lái)。需要哪種驗(yàn)證的時(shí)候只需要提供這個(gè)策略的名字。就像這樣:

nameInput.addValidata({
 notNull: true,
 dirtyWords: true,
 maxLength: 30
})
而notNull,maxLength等方法只需要統(tǒng)一的返回true或者false,來(lái)表示是否通過(guò)了驗(yàn)證。

validataList = {
 notNull: function( value ){
 return value !== '';
 },
 maxLength: function( value, maxLen ){
 return value.length() > maxLen;
 }
}

可以看到,各種驗(yàn)證規(guī)則很容易被修改和相互替換。如果某天產(chǎn)品經(jīng)理建議字符過(guò)長(zhǎng)的限制改成60個(gè)字符。那只需要0.5秒完成這次工作。

大概內(nèi)容就為大家介紹到這。

聊一聊題外話,馬上2015年要過(guò)去了,大家的年終獎(jiǎng)是不是很豐厚呀?。?!

希望大家可以在這一年里有所收獲,通過(guò)這篇文章也能有所收獲,知道什么是策略模式,理解小編精心為大家準(zhǔn)備的兩個(gè)實(shí)例。

相關(guān)文章

最新評(píng)論