1. 适配器模式
1.1 适配器的意义
比作一个插头,与一个接口不兼容,但是接上了适配器,就可以通过适配器的另一端去与接口适配上了。相同的也有USB转接口、电源适配器、港式插头转换器等。
1.1 适配器的应用
适配器只有在接口无法正常工作的时候能用上。
实例场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var renderMap = function( map ){ if ( map.show instanceof Function ){ map.show(); //渲染定图的接口方法为show } }; var googleMap = { show: function(){ //谷歌地图方法为show console.log( '开始渲染谷歌地图' ); } }; var baiduMap = { display: function(){ //百度地图方法为display console.log( '开始渲染百度地图' ); } };
var baiduMapAdapter = {//通过百度适配器来使百度地图也能调用接口 show: function(){ return baiduMap.display(); } };
renderMap( baiduMapAdapter ); // 百度地图调用接口
|
就是经过适配器后可以转换接口对接方式。
1.3 小结
适配器模式是一对相对简单的模式。装饰者模式、代理模式和外观模式都属于“包装模式”,都是由一个对象包装另一个对象。区别它们的关键仍然是模式的意图。
- 适配器模式主要解决两个已有接口直接不适配的问题,不考虑接口的实现和将来的演化。不需要改变已有接口,只需要使它们协同作用。
- 装饰者模式和代理模式也不会改变接口,但装饰者模式作用是为了给对象增加功能。适配器通常只包装一次。代理模式是为了控制对对象的访问,也只包装一次。
- 外观模式的作用和适配器相似,但外观模式最显著的特点是定义了一个新的接口。
2. 策略模式
2.1 策略模式的意义
从A到B有多种不同的方案可以选择,这些方案可以随意互相替换,就是策略模式。
2.2 策略模式的应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| //使用策略模式前的代码 var calculateBonus = function( performanceLevel, salary ){ if ( performanceLevel === 'S' ){ return salary * 4; } if ( performanceLevel === 'A' ){ return salary * 3; } if ( performanceLevel === 'B' ){ return salary * 2; } }; //策略模式重构后的代码 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
|
2.3 多态在策略模式中的体现
通过策略模式重构后,消除了条件分支语句。跟计算有关的逻辑不放在Context中,分部在各个策略对象中。Context并没有计算奖金的能力,而是把这个职责委托给了某个策略对象。每个策略对象已经被各自封装在对象内部,这是对象多态性的体系,也是相互替换的目的。
2.4 使用策略模式实现缓动动画
通过将对象的动作都保存起来作为属性,然后通过定时器去分别调用不同的动作,形成一个缓动的效果,可以让对象动起来。
2.5 表单校验
使用策略模式进行表单校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| //定制校验策略 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作为Context,负责接收用户请求然后委托给策略对象 var validataFunc = function(){ var validator = new Validator(); // 创建一个 validator 对象 /***************添加一些校验规则****************/ validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空' ); validator.add( registerForm.password, 'minLength:6', '密码长度不能少于 6 位' ); validator.add( registerForm.phoneNumber, 'isMobile', '手机号码格式不正确' ); var errorMsg = validator.start(); // 获得校验结果 return errorMsg; // 返回校验结果 } var registerForm = document.getElementById( 'registerForm' ); registerForm.onsubmit = function(){ var errorMsg = validataFunc(); // 如果 errorMsg 有确切的返回值,说明未通过校验 if ( errorMsg ){ alert ( errorMsg ); return false; // 阻止表单提交 } }; //实现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.shift(); // 用户挑选的 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; } } };
|
2.6 策略模式的优缺点
- 利用组合、委托和多态等技术思想,可以有效地避免多重条件选择语句。
- 提供了对开放-封闭原则的完美支持,将策略封装在独立的对象中,易于切换,理解,扩展
- 可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作
- 利用组合和委托来让context拥有执行算法的能力,也是继承的一种轻便替代方案
2.7 小结
策略类一般被函数所替代,策略模式让我们明白使用函数的好处。
3. 状态模式