javaScript设计模式与开发实践(二)策略模式
策略模式定义:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
使用策略模式计算奖金:
实现一:最初代码实现
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
实现一缺点:
函数庞大,if-else语句需要覆盖所有逻辑分支;
函数缺乏弹性,新增绩效等级时,需要深入函数内部实现,违反了开放-封闭原则;
算法复用性差。
实现二:策略模式,目的是将算法的使用与实现分离开
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
通过使用策略模式重构代码,消除了原程序中大片的条件分支语句。
策略对象负责的算法被各自封装在了对象内部。
使用策略模式验证表单:
实现一:普通方法
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; } }
实现一的缺点:
onsubmit函数庞大,包含了很多If-else语句,需要覆盖所有校验规则
onsubmit函数缺乏弹性
算法复用性差,新增一个表单时,需要重新复制代码
实现二:策略模式
/***********************策略对象**************************/ 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, rules) {
var self = this;
for(var i=0,len=rules.length; i<len; i++) {
var rule = rules[i];
(function(rule){
var strategAry = rule.stratege.split(':'); var errorMsg = rule.errorMsg;
self.cache.push(function(){ var stratege = strategyAry.shift(); strategyAry.unshift( dom.value ); strategyAry.push( errorMsg ); return strategies[ strategy ].apply( dom, strategyAry ); }); })(rule); } }; Validator.prototype.start = function(){ for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){ var errorMsg = validatorFunc(); if ( errorMsg ){ return errorMsg; } } }; /***********************客户调用代码**************************/ var registerForm = document.getElementById( 'registerForm' ); var validataFunc = function(){ var validator = new Validator(); validator.add( registerForm.userName, [{ strategy: 'isNonEmpty', errorMsg: '用户名不能为空' }, { strategy: 'minLength:6', errorMsg: '用户名长度不能小于10 位' }]); validator.add( registerForm.password, [{ strategy: 'minLength:6', errorMsg: '密码长度不能小于6 位' }]); validator.add( registerForm.phoneNumber, [{ strategy: 'isMobile', errorMsg: '手机号码格式不正确' }]); var errorMsg = validator.start(); return errorMsg; } registerForm.onsubmit = function(){ var errorMsg = validataFunc(); if ( errorMsg ){ alert ( errorMsg ); return false; } };
策略模式的优缺点:
优点:
策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的strategy 中,使得它们易于切换,易于理解,易于扩展。
策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
在策略模式中利用组合和委托来让Context 拥有执行算法的能力,这也是继承的一种更轻便的替代方案。
缺点:
使用策略模式会在程序中增加许多策略类或者策略对象。
要使用策略模式,必须了解所有的strategy。