策略模式指的是定義一系 列的算法,把它們一個個封裝起來。將不變的部分和變化的部分隔開是每一個設計模式的主題,策 略模式也不例外,策略模式的目的就是將算法的使用與算法的實現分離開來。html
一個基於策略模式的程序至少由兩部分組成。第一個部分是一組策略類,策略類封裝了具體 的算法,並負責具體的計算過程。 第二個部分是環境類 Context,Context接受客戶的請求,隨後 把請求委託給某一個策略類。要作到這點,說明 Context中要維持對某個策略對象的引用算法
<!--algorithm-->
var strategies = {
s: function(salary){
return salary *4;
},
a: function(salary){
return salary * 3;
},
b: function(salary){
return salary * 2
}
}
<!--context-->
var calculateBonus = function(level, salary){
return strategies[level](salary)
}
複製代碼
在實際開發中,咱們一般會把算法的含義擴散開來,使策略模式也能夠用來封裝 一系列的「業務規則」。只要這些業務規則指向的目標一致,而且能夠被替換使用,咱們就能夠 用策略模式來封裝它們。設計模式
在一個 Web項目中,註冊、登陸、修改用戶信息等功能的實現都離不開提交表單。 在將用戶輸入的數據交給後臺以前,經常要作一些客戶端力所能及的校驗工做,好比註冊的 時候須要校驗是否填寫了用戶名,密碼的長度是否符合規定,等等。這樣能夠避免由於提交不合 法數據而帶來的沒必要要網絡開銷。網絡
假設咱們正在編寫一個註冊的頁面,在點擊註冊按鈕以前,有以下幾條校驗邏輯。app
<body>
<form action="http:// xxx.com/register" id="registerForm" method="post">
請輸入用戶名:<input type="text" name="userName"/ >
請輸入密碼:<input type="text" name="password"/ >
</body>
複製代碼
var registerForm = document.getElementById( 'registerForm' );
registerForm.onsubmit = function(){
if ( registerForm.userName.value === '' ){
alert ( '用戶名不能爲空' );
return false;
}
if ( registerForm.password.value.length < 6 ){
alert ( '密碼長度不能少於 6 位' );
return false;
}
if ( !/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value ) ){
alert ( '手機號碼格式不正確' );
return false;
}
}
複製代碼
下面咱們將用策略模式來重構表單校驗的代碼,很顯然第一步咱們要把這些校驗邏輯都封裝 成策略對象:dom
var strategies = {
isNonEmpty: function(value, errorMsg){
if(value === ''){
return errorMsg;
}
},
minLength: function(value, length, errorMsg){
if(value.length < length){
return errorMsg
}
},
isMobile: function(value, errorMsg){
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)){
return errorMsg;
}
}
}
複製代碼
Validator 類的實現:函數
var Validator = function(){
this.cache = [];
}
Validator.prototype.add = function(dom, rule, errorMsg){
var ary = rule.split(':');// 把 strategy 和參數分開
this.cache.push(function(){// 把校驗的步驟用空函數包裝起來,而且放入 cache
var strategy = ary.shifg();// 用戶挑選的 strategy
ary.unshift(dom.value);// 把 input 的 value 添加進參數列表
ary.push(errorMsg);// 把 errorMsg 添加進參數列表
return strategies[strategy].apply(dom, ary);
})
}
Validator.prototype.start = function(){
for(var i=0, validatorFunc; validatorFunc = this.cache[i++]){
var msg = validatorFunc();// 開始校驗,並取得校驗後的返回信息
if(msg){ // 若是有確切的返回值,說明校驗沒有經過
return msg;
}
}
}
複製代碼
使用策略模式重構代碼以後,咱們僅僅經過「配置」的方式就能夠完成一個表單的校驗, 這些校驗規則也能夠複用在程序的任何地方,還能做爲插件的形式,方便地被移植到其餘項 目中。post
在修改某個校驗規則的時候,只須要編寫或者改寫少許的代碼。好比咱們想將用戶名輸入框 的校驗規則改爲用戶名不能少於 4個字符。能夠看到,這時候的修改是絕不費力的。代碼以下:ui
validator.add( registerForm.userName, 'isNonEmpty', '用戶名不能爲空' );
// 改爲:
validator.add( registerForm.userName, 'minLength:10', '用戶名長度不能小於 10 位' );
複製代碼
<!--Validator類-->
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();
})
})(rule)
}
}
Validator.prototype.start = function(){
for(var i=0, validatorFunc;validatorFunc = this.cache[i++];){
var errorMsg = validatorFunc();
if(errorMsg){
return errorMsg;
}
}
}
複製代碼
優勢:this
缺點: