策略模式在Angular中用到了,這里給大家分享一篇文章,看過(guò)之后便于理解angular
1、策略模式的定義:定義一系列算法,把他們一個(gè)個(gè)封裝起來(lái),并且使他們可以相互替換
優(yōu)點(diǎn):(1)策略模式利用組合,委托等技術(shù)和思想,有效避免多if條件語(yǔ)句
(2)策略模式提供了,開放-封閉原則,使代碼更容易理解和擴(kuò)展。
(3)策略模式的代碼可以復(fù)用
2、一:使用策略模式計(jì)算獎(jiǎng)金;
下面的demo是我在書上看到的,但是沒(méi)有關(guān)系,我們只是來(lái)理解下策略模式的使用而已,我們可以使用策略模式來(lái)計(jì)算獎(jiǎng)金問(wèn)題;比如公司的年終獎(jiǎng)是根據(jù)員工的工資和績(jī)效來(lái)考核的,績(jī)效為A的人,年終獎(jiǎng)為工資的4倍,績(jī)效為B的人,年終獎(jiǎng)為工資的3倍,績(jī)效為C的人,年終獎(jiǎng)為工資的2倍;現(xiàn)在我們使用一般的編碼方式會(huì)如下這樣編寫代碼:var
calculateBous=function(salary,level){
if(level==="A"){
return salary*4;
}
if(level==="B"){
return salary*3;
}
if(level==="C"){
return salary*2;
}
};
console.log(calculateBounds(4000,'A'));
console.log(calculateBounds(2500,'B'));
缺點(diǎn):(1)calculateBouns函數(shù)包含了很多if-else語(yǔ)句
(2)calculateBouns函數(shù)缺乏彈性,不能擴(kuò)展
(3)算法復(fù)用性差,算法不能通用
- 使用組合函數(shù)重構(gòu)代碼組合函數(shù)是把各種算法封裝到一個(gè)個(gè)的小函數(shù)里面,比如等級(jí)A的話,封裝一個(gè)小函數(shù),等級(jí)為B的話,也封裝一個(gè)小函數(shù),以此類推;如下代碼:
var performanceA=function(salary){
return salary*4;
};
var performanceB=function(salary){
return salary*3;
};
var performanceC=function(salary){
return salary*2;
};
var calculateBouns=function(salary,level){
if(levle==='A'){
return performanc(salary);
}
if(levle==='B'){
return performanc(salary);
}
if(levle==='C'){
return performanc(salary);
}
};
console.log(calculateBouns('A',4500));
代碼看起來(lái)有點(diǎn)改善,但是還是有如下缺點(diǎn):calculateBouns 函數(shù)有可能會(huì)越來(lái)越大,比如增加D等級(jí)的時(shí)候,而且缺乏彈性。
3、使用策略模式重構(gòu)代碼
策略模式指的是定義一系列算法,把它們一個(gè)個(gè)封裝起來(lái),將不變的部分和變化的部分隔開,實(shí)際就是將算法的使用和實(shí)現(xiàn)分離出來(lái);算法的使用方式不變的,都是根據(jù)根據(jù)某個(gè)算法計(jì)算后的獎(jiǎng)金數(shù),而算法的實(shí)現(xiàn)時(shí)根據(jù)績(jī)效對(duì)應(yīng)不同的績(jī)效規(guī)則;
一個(gè)基于策略模式的程序至少由兩部分組成,第一部分是策略類,策略類封裝了具體的算法,并負(fù)責(zé)具體的計(jì)算過(guò)程。第二部分是環(huán)境類context,該context接受客戶端的請(qǐng)求,隨后把請(qǐng)求委托給某個(gè)策略類。
使用傳統(tǒng)面向?qū)ο髞?lái)實(shí)現(xiàn):
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*3;
};
//獎(jiǎng)金類
var Bouns=funtion(){
this.salary=null; //原始工資
this.levelObj=null; //績(jī)效等級(jí)對(duì)應(yīng)的策略
};
Bounds.prototype.setSalry=function(salary){
this.salry=salary; //保存原始工資
};
Bounds.prototype.setLvelObj=function(salary){
this.setLevleObj=levelObj; //設(shè)置員工的績(jī)效等級(jí)
};
//取得獎(jiǎng)金
Bouns.prototype.getBouns=function(){
return this.levelObj.calculate(this.salary);
};
var bouns= new Bouns();
bouns.setSalary(10000);
bouns.setLevelObj(new performanceA); //設(shè)置策略對(duì)象
console.log(bouns.getBouns()); //40000
//對(duì)于prototype的方法或者屬性,我們可以動(dòng)態(tài)的增加,而由其創(chuàng)建的對(duì)象自動(dòng)會(huì)繼承相關(guān)的方法和屬性
bouns.setLevelObj(new performanceB);
console.log(bouns.getBouns());
- Javascript版本的策略模式代碼如下:
var obj={
"A":function(salary){
return salary*4;
},
"B":function(salary){
return salary*3;
},
"C":funtion (salary){
return salary*2;
}
};
var calculateBouns=function(level,salary){
return obj[level][salary];
};
console.log(calculateBouns('A',1000));
可以看到代碼更加簡(jiǎn)單明了;策略模式指的是定義一系列的算法,并且把它們封裝起來(lái),但是策略模式不僅僅只封裝算法,我們還可以對(duì)用來(lái)封裝一系列的業(yè)務(wù)規(guī)則,只要這些業(yè)務(wù)規(guī)則目標(biāo)一致,我們就可以使用策略模式來(lái)封裝它們;
5、
比如我們經(jīng)常來(lái)進(jìn)行表單驗(yàn)證,比如注冊(cè)登錄對(duì)話框,我們登錄之前要進(jìn)行驗(yàn)證操作:比如有以下幾條邏輯:
1.用戶名不能為空2.密碼長(zhǎng)度不能小于6位。
3.手機(jī)號(hào)碼必須符合格式。比如HTML代碼如下:
<form action = "http://www.baidu.com" id="registerForm" method = "post">
<p>
<label>請(qǐng)輸入用戶名:</label>
<input type="text" name="userName"/>
</p>
<p>
<label>請(qǐng)輸入密碼:</label>
<input type="text" name="password"/>
</p>
<p>
<label>請(qǐng)輸入手機(jī)號(hào)碼:</label>
<input type="text" name="phoneNumber"/>
</p>
</form>
我們正常的表單編寫如下:
var registerForm=document.getElementById("registerForm");
registerForm.onsubmit=function(){
if(registerForm.usename.value=""){
alert('用戶名不能為空');
return;
}
if(registerForm.password.value.length<6){
alert("密碼長(zhǎng)度不能小于6位");
return;
}
if(!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
alert("手機(jī)號(hào)碼格式不正確");
return;
};
但是這樣編寫代碼有如下缺點(diǎn):1.registerForm.onsubmit 函數(shù)比較大,代碼中包含了很多if語(yǔ)句;2.registerForm.onsubmit 函數(shù)缺乏彈性,如果增加了一種新的效驗(yàn)規(guī)則,或者想把密碼的長(zhǎng)度效驗(yàn)從6改成8,我們必須改registerForm.onsubmit 函數(shù)內(nèi)部的代碼。違反了開放-封閉原則。3.算法的復(fù)用性差,如果在程序中增加了另外一個(gè)表單,這個(gè)表單也需要進(jìn)行一些類似的效驗(yàn),那么我們可能又需要復(fù)制代碼了;下面我們可以使用策略模式來(lái)重構(gòu)表單效驗(yàn);第一步我們先來(lái)封裝策略對(duì)象;如下代碼:
var strategy = {
isNotEmpty: function(value,errorMsg) {
if(value === '') {
return errorMsg;
}
}, // 限制最小長(zhǎng)度
minLength: function(value,length,errorMsg) {
if(value.length < length) {
return errorMsg;
}
}, // 手機(jī)號(hào)碼格式
mobileFormat: function(value,errorMsg) {
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}};
接下來(lái)我們準(zhǔn)備實(shí)現(xiàn)Validator類,Validator類在這里作為Context,負(fù)責(zé)接收用戶的請(qǐng)求并委托給strategy 對(duì)象,如下代碼:
var Validator = function(){
this.cache = []; // 保存效驗(yàn)規(guī)則
};
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添加進(jìn)參數(shù)列表
str.push(errorMsg); // 把errorMsg添加進(jìn)參數(shù)列表
return strategys[strategy].apply(dom,str);
});
};
Validator.prototype.start = function(){
for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
var msg = validatorFunc(); // 開始效驗(yàn) 并取得效驗(yàn)后的返回信息
if(msg) {
return msg;
}
}
};
Validator類在這里作為Context,負(fù)責(zé)接收用戶的請(qǐng)求并委托給strategys對(duì)象。上面的代碼中,我們先創(chuàng)建一個(gè)Validator對(duì)象,然后通過(guò)validator.add方法往validator對(duì)象中添加一些效驗(yàn)規(guī)則,validator.add方法接收3個(gè)參數(shù),如下代碼:
validator.add(registerForm.password,’minLength:6′,’密碼長(zhǎng)度不能小于6位’);
registerForm.password 為效驗(yàn)的input輸入框dom節(jié)點(diǎn);
minLength:6: 是以一個(gè)冒號(hào)隔開的字符串,冒號(hào)前面的minLength代表客戶挑選的strategys對(duì)象,冒號(hào)后面的數(shù)字6表示在效驗(yàn)過(guò)程中所必須驗(yàn)證的參數(shù),minLength:6的意思是效驗(yàn) registerForm.password 這個(gè)文本輸入框的value最小長(zhǎng)度為6位;如果字符串中不包含冒號(hào),說(shuō)明效驗(yàn)過(guò)程中不需要額外的效驗(yàn)信息;
第三個(gè)參數(shù)是當(dāng)效驗(yàn)未通過(guò)時(shí)返回的錯(cuò)誤信息;
當(dāng)我們往validator對(duì)象里添加完一系列的效驗(yàn)規(guī)則之后,會(huì)調(diào)用validator.start()方法來(lái)啟動(dòng)效驗(yàn)。如果validator.start()返回了一個(gè)errorMsg字符串作為返回值,說(shuō)明該次效驗(yàn)沒(méi)有通過(guò),此時(shí)需要registerForm.onsubmit方法返回false來(lái)阻止表單提交。下面我們來(lái)看看初始化代碼如下:
var validateFunc = function(){
var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
/* 添加一些效驗(yàn)規(guī)則 */
validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');
validator.add(registerForm.password,'minLength:6','密碼長(zhǎng)度不能小于6位');
validator.add(registerForm.userName,'mobileFormat','手機(jī)號(hào)碼格式不正確');
var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
return errorMsg; // 返回效驗(yàn)結(jié)果
};
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;
}
},
// 限制最小長(zhǎng)度
minLength: function(value,length,errorMsg) {
if(value.length < length) {
return errorMsg;
}
},
// 手機(jī)號(hào)碼格式
mobileFormat: function(value,errorMsg) {
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
};
var Validator = function(){
this.cache = []; // 保存效驗(yàn)規(guī)則
};
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添加進(jìn)參數(shù)列表
str.push(errorMsg); // 把errorMsg添加進(jìn)參數(shù)列表
return strategys[strategy].apply(dom,str);
});
};
Validator.prototype.start = function(){
for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
var msg = validatorFunc(); // 開始效驗(yàn) 并取得效驗(yàn)后的返回信息
if(msg) {
return msg;
}
}
};
var validateFunc = function(){
var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
/* 添加一些效驗(yàn)規(guī)則 */
validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');
validator.add(registerForm.password,'minLength:6','密碼長(zhǎng)度不能小于6位');
validator.add(registerForm.userName,'mobileFormat','手機(jī)號(hào)碼格式不正確');
var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
return errorMsg; // 返回效驗(yàn)結(jié)果
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
var errorMsg = validateFunc();
if(errorMsg){
alert(errorMsg);
return false;
}
};
如上使用策略模式來(lái)編寫表單驗(yàn)證代碼可以看到好處了,我們通過(guò)add配置的方式就完成了一個(gè)表單的效驗(yàn);這樣的話,那么代碼可以當(dāng)做一個(gè)組件來(lái)使用,并且可以隨時(shí)調(diào)用,在修改表單驗(yàn)證規(guī)則的時(shí)候,也非常方便,通過(guò)傳遞參數(shù)即可調(diào)用;
給某個(gè)文本輸入框添加多種效驗(yàn)規(guī)則
上面的代碼我們可以看到,我們只是給輸入框只能對(duì)應(yīng)一種效驗(yàn)規(guī)則,比如上面的我們只能效驗(yàn)輸入框是否為空,
validator.add(registerForm.userName,’isNotEmpty’,'用戶名不能為空’);但是如果我們既要效驗(yàn)輸入框是否為空,還要效驗(yàn)輸入框的長(zhǎng)度不要小于10位的話,那么我們期望需要像如下傳遞參數(shù):
validator.add(registerForm.userName,[{strategy:’isNotEmpty’,errorMsg:’用戶名不能為空’},{strategy: 'minLength:6',errorMsg:'用戶名長(zhǎng)度不能小于6位'}])
我們可以編寫代碼如下:
// 策略對(duì)象
var strategys = {
isNotEmpty: function(value,errorMsg) {
if(value === '') {
return errorMsg;
}
},
// 限制最小長(zhǎng)度
minLength: function(value,length,errorMsg) {
if(value.length < length) {
return errorMsg;
}
},
// 手機(jī)號(hào)碼格式
mobileFormat: function(value,errorMsg) {
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
};
var Validator = function(){
this.cache = []; // 保存效驗(yàn)規(guī)則
};
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(); // 開始效驗(yàn) 并取得效驗(yàn)后的返回信息
if(msg) {
return msg;
}
}
};
// 代碼調(diào)用
var registerForm = document.getElementById("registerForm");
var validateFunc = function(){
var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
/* 添加一些效驗(yàn)規(guī)則 */
validator.add(registerForm.userName,[
{strategy: 'isNotEmpty',errorMsg:'用戶名不能為空'},
{strategy: 'minLength:6',errorMsg:'用戶名長(zhǎng)度不能小于6位'}
]);
validator.add(registerForm.password,[
{strategy: 'minLength:6',errorMsg:'密碼長(zhǎng)度不能小于6位'},
]);
validator.add(registerForm.phoneNumber,[
{strategy: 'mobileFormat',errorMsg:'手機(jī)號(hào)格式不正確'},
]);
var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
return errorMsg; // 返回效驗(yàn)結(jié)果
};
// 點(diǎn)擊確定提交
registerForm.onsubmit = function(){
var errorMsg = validateFunc();
if(errorMsg){
alert(errorMsg);
return false;
}
}