码迷,mamicode.com
首页 > 编程语言 > 详细

《JavaScript设计模式与开发实践》—— 策略模式

时间:2016-10-24 13:34:10      阅读:195      评论:0      收藏:0      [点我收藏+]

标签:改变   事件   请求   get   rtti   erro   sub   interval   制作   

策略模式的定义是: 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

 

(1) 使用策略模式计算奖金
经过思考,我们想到了更好的办法——使用策略模式来重构代码。策略模式指的是定义一系列的算法,把它们一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题,策略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来。
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。 第二个部分是环境类 ContextContext 接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明 Context 中要维持对某个策略对象的引用。
JavaScript 语言中,函数也是对象,所以更简单和直接的做法是把 strategy直接定义为函数:

// 定义策略模式算法
        var performance = {

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

        };
    // 计算奖金方法
        var calculateBonus = function ( level , salary ) {
            return performance[level](salary);
        };
    // 测试
        console.log(calculateBonus("B",4200));  // 8400
        console.log(calculateBonus("S",6500));  // 26000

通过使用策略模式重构代码,我们消除了原程序中大片的条件分支语句。所有跟计算奖金有关的逻辑不再放在 Context 中,而是分布在各个策略对象中。 Context 并没有计算奖金的能力,而是把这个职责委托给了某个策略对象。每个策略对象负责的算法已被各自封装在对象内部。当我们对这些策略对象发出“计算奖金”的请求时,它们会返回各自不同的计算结果,这正是对象多态性的体现,也是“它们可以相互替换”的目的。替换 Context 中当前保存的策略对象,便能执行不同的算法来得到我们想要的结果。


(2) 使用策略模式实现缓动动画


1. 实现动画效果的原理
JavaScript 实现动画效果的原理跟动画片的制作一样,动画片是把一些差距不大的原画以较快的帧数播放,来达到视觉上的动画效果。在 JavaScript 中,可以通过连续改变元素的某个 CSS属性,比如 lefttopbackground-position 来实现动画效果。
下图就是通过改变节点的background-position,让人物动起来的。 技术分享

2. 思路和一些准备工作
我们目标是编写一个动画类和一些缓动算法,让小球以各种各样的缓动效果在页面中运动。
现在来分析实现这个程序的思路。在运动开始之前,需要提前记录一些有用的信息,至少包括以下信息:
动画开始时,小球所在的原始位置
小球移动的目标位置
动画开始时的准确时间点
小球运动持续的时间
随后,我们会用 setInterval 创建一个定时器,定时器每隔 19ms 循环一次。在定时器的每一帧里,我们会把动画已消耗的时间、小球原始位置、小球目标位置和动画持续的总时间等信息传入缓动算法。该算法会通过这几个参数,计算出小球当前应该所在的位置。最后再更新该 div 对应的 CSS 属性,小球就能够顺利地运动起来了。
3. 让小球动起来

 <div style="position:absolute; background: pink; border-radius: 50%; width: 60px; height: 60px;" id="div"></div>

    <script>
        /*========================================
        * t : Time —— 运动已消耗的时间
        * b : Begin value —— 开始值
        * c : Change In Value —— 结束值-开始值
        * d : Duration —— 总运动时间
        * 返回值是动画元素应该处在的当前位置
        ======================================== */
    // 运动算法
        var tween = {
            liner : function ( t , b , c , d ) {
                return c * t / d + b;
            },
            easeIn : function ( t , b , c , d ) {
                return c * ( t /= d ) * t + b;
            },
            strongEaseIn: function(t, b, c, d){
                return c * ( t /= d ) * t * t * t * t + b;
            },
            strongEaseOut: function(t, b, c, d){
                return c * ( ( t = t / d - 1 ) * t * t * t * t + 1 ) + b;
            },
            sineaseIn: function( t, b, c, d ){
                return c * ( t /= d ) * t * t + b;
            },
            sineaseOut: function(t,b,c,d){
                return c * ( ( t = t / d - 1 ) * t * t + 1 ) + b;
            }
        };
    // 构造函数
        var Animate = function ( dom ) {
            this.dom = dom;  // 进行运动的dom节点
            this.startTime = 0;  // 动画开始的时间
            this.startPos = 0;  // 动画开始的dom位置
            this.endPos = 0;  // 动画结束的dom位置
            this.propertyName = null;  // dom节点需要被改变的css属性名
            this.easing = null;  // 缓动算法
            this.duration = null;  // 动画持续时间
        };
    // 开始运动方法
        Animate.prototype.start = function ( propertyName , endPos , duration , easing ) {
            this.startTime = +new Date;  // 动画启动时间
            this.startPos = this.dom.getBoundingClientRect()[ propertyName ];  // dom节点初始位置
            this.propertyName = propertyName;  // dom 节点需要被改变的 CSS 属性名
            this.endPos = endPos;  // dom 节点目标位置
            this.duration = duration;  // 动画持续事件
            this.easing = tween[ easing ];  // 缓动算法

            var self = this;
            var timer = setInterval( function () {  // 启动定时器,开始执行动画
                if ( self.step() === false ) {  // 如果动画已结束,则清除定时器
                    clearInterval( timer );
                }
            } , 19 );
        };
    //小球每一帧要做的事情
        Animate.prototype.step = function () {
            var t = +new Date;
            if ( t >= this.startTime + this.duration ) {
                this.update( this.endPos );  // 更新小球的 CSS 属性值
                return false;
            }
            var pos = this.easing( t - this.startTime , this.startPos , this.endPos - this.startPos , this.duration );  // pos 为小球当前位置
            this.update( pos );  // 更新小球的 CSS 属性值
        };
    // 更新小球 CSS 属性值方法
        Animate.prototype.update = function ( pos ) {
            this.dom.style[ this.propertyName ] = pos + ‘px‘;
        };
    // 获取元素
        var div = document.getElementById( ‘div‘ );
        var animate = new Animate( div );
    // 测试
        animate.start( ‘left‘ , 500 , 6000 , ‘sineaseIn‘ );

    </script>

 

(3) 表单校验

<form action="" id="registerForm">
        请输入用户名:<input type="text" name="username">
        请输入密码:<input type="text" name="password">
        请输入手机号码:<input type="text" name="phoneNumber">
        <button>提交</button>
    </form>

    <script>
        var registerForm = document.getElementById( ‘registerForm‘ );

        registerForm.onsubmit = function () {
            if ( registerForm.username.value === ‘‘ ) {
                alert(‘用户名不能为空!‘);
                return false;
            }
            if ( registerForm.password.length < 6 ) {
                alert(‘密码长度不能少于6位!‘);
                return false;
            }
            if ( !/^1[3|5|8][0-9]{9}$/.test( registerForm.phoneNumber.value ) ) {
                alert(‘手机号码格式不正确‘);
                return false;
            }
        }
    </script>

这是一种常见的编码方式,缺点也是显而易见的。

我们现在采用策略模式来实现它:

<form action="" id="registerForm">
        请输入用户名:<input type="text" name="username">
        请输入密码:<input type="text" name="password">
        请输入手机号码:<input type="text" name="phoneNumber">
        <button>提交</button>
    </form>

    <script>

        var strategies = {
            // 用户名不能为空
            isNonEmpty : function ( value , errMsg ) {
                if ( value === ‘‘ ) {
                    return errMsg;
                }
            },
            // 密码长度不能少于6位
            isMinLen : function ( value , length ,errMsg ) {
                if ( value.length < length ) {
                    return errMsg;
                }
            },
            // 手机格式不正确
            isMoblie : function ( value , errMsg ) {
                if ( !/^1[3|5|8][0-9]{9}$/.test( value ) ) {
                    return errMsg;
                }
            }

        };

        var validateFunc = function () {
            var validator = new Validator();
            // 添加校验规则
            validator.add( registerForm.userName , ‘isNonEmpty‘ , ‘用户名不能为空‘ );
            validator.add( registerForm.userName , ‘isMinLen‘ , ‘密码长度不能少于6位‘ );
            validator.add( registerForm.userName , ‘isMoblie‘ , ‘手机格式不正确‘ );
            var errMsg = validator.start();
            return errMsg;
        };

        var registerForm = document.getElementById( ‘registerForm‘ );
        registerForm.onsubmit = function () {
            var errMsg = validateFunc();  // 如果 errorMsg 有确切的返回值,说明未通过校验
            if ( errMsg ) {
                alert( errMsg );
                return false;  // 阻止表单提交
            }
        };

        var Validator = function () {
            this.cache = [];
        };
        Validator.prototype.add = function ( dom , rule , errMsg ) {
            var arr = rule.split( ‘:‘ );
            this.cache.push(function () {
                var strategy = arr.shift();
                arr.unshift( dom.value );
                arr.push( errMsg );
                return strategies[ strategy ].apply( dom , arr );
            });
        };
        
        Validator.prototype.start = function () {
            for ( var i = 0,validatorFunc; validatorFunc = this.cache[ i++ ]; ) {
                var msg = validatorFunc();
                if ( msg ) {
                    return msg;
                }
            }
        }

    </script>

个人表示这个代码写的有点晦涩了,有很多地方看不太懂,理解不了。

《JavaScript设计模式与开发实践》—— 策略模式

标签:改变   事件   请求   get   rtti   erro   sub   interval   制作   

原文地址:http://www.cnblogs.com/luohaoran/p/5992381.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!